| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter/widgets.dart'; |
| |
| /// A subclass of [LiveTestWidgetsFlutterBinding] that reports tests results |
| /// on a channel to adapt them to native instrumentation test format. |
| class E2EWidgetsFlutterBinding extends LiveTestWidgetsFlutterBinding { |
| /// Sets up a listener to report that the tests are finished when everything is |
| /// torn down. |
| E2EWidgetsFlutterBinding() { |
| // TODO(jackson): Report test results as they arrive |
| tearDownAll(() async { |
| try { |
| await _channel.invokeMethod<void>( |
| 'allTestsFinished', <String, dynamic>{'results': _results}); |
| } on MissingPluginException { |
| print('Warning: E2E test plugin was not detected.'); |
| } |
| if (!_allTestsPassed.isCompleted) _allTestsPassed.complete(true); |
| }); |
| } |
| |
| final Completer<bool> _allTestsPassed = Completer<bool>(); |
| |
| /// Similar to [WidgetsFlutterBinding.ensureInitialized]. |
| /// |
| /// Returns an instance of the [E2EWidgetsFlutterBinding], creating and |
| /// initializing it if necessary. |
| static WidgetsBinding ensureInitialized() { |
| if (WidgetsBinding.instance == null) { |
| E2EWidgetsFlutterBinding(); |
| } |
| assert(WidgetsBinding.instance is E2EWidgetsFlutterBinding); |
| return WidgetsBinding.instance; |
| } |
| |
| static const MethodChannel _channel = MethodChannel('plugins.flutter.io/e2e'); |
| |
| static Map<String, String> _results = <String, String>{}; |
| |
| // Emulates the Flutter driver extension, returning 'pass' or 'fail'. |
| @override |
| void initServiceExtensions() { |
| super.initServiceExtensions(); |
| Future<Map<String, dynamic>> callback(Map<String, String> params) async { |
| final String command = params['command']; |
| Map<String, String> response; |
| switch (command) { |
| case 'request_data': |
| final bool allTestsPassed = await _allTestsPassed.future; |
| response = <String, String>{ |
| 'message': allTestsPassed ? 'pass' : 'fail', |
| }; |
| break; |
| case 'get_health': |
| response = <String, String>{'status': 'ok'}; |
| break; |
| default: |
| throw UnimplementedError('$command is not implemented'); |
| } |
| return <String, dynamic>{ |
| 'isError': false, |
| 'response': response, |
| }; |
| } |
| |
| registerServiceExtension(name: 'driver', callback: callback); |
| } |
| |
| @override |
| Future<void> runTest(Future<void> testBody(), VoidCallback invariantTester, |
| {String description = '', Duration timeout}) async { |
| // TODO(jackson): Report the results individually instead of all at once |
| // See https://github.com/flutter/flutter/issues/38985 |
| final TestExceptionReporter valueBeforeTest = reportTestException; |
| reportTestException = |
| (FlutterErrorDetails details, String testDescription) { |
| _results[description] = 'failed'; |
| _allTestsPassed.complete(false); |
| valueBeforeTest(details, testDescription); |
| }; |
| await super.runTest(testBody, invariantTester, |
| description: description, timeout: timeout); |
| _results[description] ??= 'success'; |
| } |
| } |