|  | // Copyright 2014 The Flutter 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 'dart:convert'; | 
|  |  | 
|  | /// A callback to use with [integrationDriver]. | 
|  | /// | 
|  | /// The callback receives the name of screenshot passed to `binding.takeScreenshot(<name>)` and | 
|  | /// a PNG byte buffer. | 
|  | /// | 
|  | /// The callback returns `true` if the test passes or `false` otherwise. | 
|  | /// | 
|  | /// You can use this callback to store the bytes locally in a file or upload them to a service | 
|  | /// that compares the image against a gold or baseline version. | 
|  | /// | 
|  | /// Since the function is executed on the host driving the test, you can access any environment | 
|  | /// variable from it. | 
|  | typedef ScreenshotCallback = Future<bool> Function(String name, List<int> image); | 
|  |  | 
|  | /// Classes shared between `integration_test.dart` and `flutter drive` based | 
|  | /// adoptor (ex: `integration_test_driver.dart`). | 
|  |  | 
|  | /// An object sent from integration_test back to the Flutter Driver in response to | 
|  | /// `request_data` command. | 
|  | class Response { | 
|  | /// Constructor to use for positive response. | 
|  | Response.allTestsPassed({this.data}) | 
|  | : _allTestsPassed = true, | 
|  | _failureDetails = null; | 
|  |  | 
|  | /// Constructor for failure response. | 
|  | Response.someTestsFailed(this._failureDetails, {this.data}) | 
|  | : _allTestsPassed = false; | 
|  |  | 
|  | /// Constructor for failure response. | 
|  | Response.toolException({String? ex}) | 
|  | : _allTestsPassed = false, | 
|  | _failureDetails = <Failure>[Failure('ToolException', ex)]; | 
|  |  | 
|  | /// Constructor for web driver commands response. | 
|  | Response.webDriverCommand({this.data}) | 
|  | : _allTestsPassed = false, | 
|  | _failureDetails = null; | 
|  |  | 
|  | final List<Failure>? _failureDetails; | 
|  |  | 
|  | final bool _allTestsPassed; | 
|  |  | 
|  | /// The extra information to be added along side the test result. | 
|  | Map<String, dynamic>? data; | 
|  |  | 
|  | /// Whether the test ran successfully or not. | 
|  | bool get allTestsPassed => _allTestsPassed; | 
|  |  | 
|  | /// If the result are failures get the formatted details. | 
|  | String get formattedFailureDetails => | 
|  | _allTestsPassed ? '' : formatFailures(_failureDetails!); | 
|  |  | 
|  | /// Failure details as a list. | 
|  | List<Failure>? get failureDetails => _failureDetails; | 
|  |  | 
|  | /// Serializes this message to a JSON map. | 
|  | String toJson() => json.encode(<String, dynamic>{ | 
|  | 'result': allTestsPassed.toString(), | 
|  | 'failureDetails': _failureDetailsAsString(), | 
|  | if (data != null) 'data': data | 
|  | }); | 
|  |  | 
|  | /// Deserializes the result from JSON. | 
|  | static Response fromJson(String source) { | 
|  | final Map<String, dynamic> responseJson = json.decode(source) as Map<String, dynamic>; | 
|  | if ((responseJson['result'] as String?) == 'true') { | 
|  | return Response.allTestsPassed(data: responseJson['data'] as Map<String, dynamic>?); | 
|  | } else { | 
|  | return Response.someTestsFailed( | 
|  | _failureDetailsFromJson(responseJson['failureDetails'] as List<dynamic>), | 
|  | data: responseJson['data'] as Map<String, dynamic>?, | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Method for formatting the test failures' details. | 
|  | String formatFailures(List<Failure> failureDetails) { | 
|  | if (failureDetails.isEmpty) { | 
|  | return ''; | 
|  | } | 
|  |  | 
|  | final StringBuffer sb = StringBuffer(); | 
|  | int failureCount = 1; | 
|  | for (final Failure failure in failureDetails) { | 
|  | sb.writeln('Failure in method: ${failure.methodName}'); | 
|  | sb.writeln(failure.details); | 
|  | sb.writeln('end of failure ${failureCount.toString()}\n\n'); | 
|  | failureCount++; | 
|  | } | 
|  | return sb.toString(); | 
|  | } | 
|  |  | 
|  | /// Create a list of Strings from [_failureDetails]. | 
|  | List<String> _failureDetailsAsString() { | 
|  | final List<String> list = <String>[]; | 
|  | if (_failureDetails == null || _failureDetails!.isEmpty) { | 
|  | return list; | 
|  | } | 
|  |  | 
|  | for (final Failure failure in _failureDetails!) { | 
|  | list.add(failure.toJson()); | 
|  | } | 
|  |  | 
|  | return list; | 
|  | } | 
|  |  | 
|  | /// Creates a [Failure] list using a json response. | 
|  | static List<Failure> _failureDetailsFromJson(List<dynamic> list) { | 
|  | return list.map((dynamic s) { | 
|  | return Failure.fromJsonString(s as String); | 
|  | }).toList(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Representing a failure includes the method name and the failure details. | 
|  | class Failure { | 
|  | /// Constructor requiring all fields during initialization. | 
|  | Failure(this.methodName, this.details); | 
|  |  | 
|  | /// The name of the test method which failed. | 
|  | final String methodName; | 
|  |  | 
|  | /// The details of the failure such as stack trace. | 
|  | final String? details; | 
|  |  | 
|  | /// Serializes the object to JSON. | 
|  | String toJson() { | 
|  | return json.encode(<String, String?>{ | 
|  | 'methodName': methodName, | 
|  | 'details': details, | 
|  | }); | 
|  | } | 
|  |  | 
|  | @override | 
|  | String toString() => toJson(); | 
|  |  | 
|  | /// Decode a JSON string to create a Failure object. | 
|  | static Failure fromJsonString(String jsonString) { | 
|  | final Map<String, dynamic> failure = json.decode(jsonString) as Map<String, dynamic>; | 
|  | return Failure(failure['methodName'] as String, failure['details'] as String?); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Message used to communicate between app side tests and driver tests. | 
|  | /// | 
|  | /// Not all `integration_tests` use this message. They are only used when app | 
|  | /// side tests are sending [WebDriverCommand]s to the driver side. | 
|  | /// | 
|  | /// These messages are used for the handshake since they carry information on | 
|  | /// the driver side test such as: status pending or tests failed. | 
|  | class DriverTestMessage { | 
|  | /// When tests are failed on the driver side. | 
|  | DriverTestMessage.error() | 
|  | : _isSuccess = false, | 
|  | _isPending = false; | 
|  |  | 
|  | /// When driver side is waiting on [WebDriverCommand]s to be sent from the | 
|  | /// app side. | 
|  | DriverTestMessage.pending() | 
|  | : _isSuccess = false, | 
|  | _isPending = true; | 
|  |  | 
|  | /// When driver side successfully completed executing the [WebDriverCommand]. | 
|  | DriverTestMessage.complete() | 
|  | : _isSuccess = true, | 
|  | _isPending = false; | 
|  |  | 
|  | final bool _isSuccess; | 
|  | final bool _isPending; | 
|  |  | 
|  | // /// Status of this message. | 
|  | // /// | 
|  | // /// The status will be use to notify `integration_test` of driver side's | 
|  | // /// state. | 
|  | // String get status => _status; | 
|  |  | 
|  | /// Has the command completed successfully by the driver. | 
|  | bool get isSuccess => _isSuccess; | 
|  |  | 
|  | /// Is the driver waiting for a command. | 
|  | bool get isPending => _isPending; | 
|  |  | 
|  | /// Depending on the values of [isPending] and [isSuccess], returns a string | 
|  | /// to represent the [DriverTestMessage]. | 
|  | /// | 
|  | /// Used as an alternative method to converting the object to json since | 
|  | /// [RequestData] is only accepting string as `message`. | 
|  | @override | 
|  | String toString() { | 
|  | if (isPending) { | 
|  | return 'pending'; | 
|  | } else if (isSuccess) { | 
|  | return 'complete'; | 
|  | } else { | 
|  | return 'error'; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Return a DriverTestMessage depending on `status`. | 
|  | static DriverTestMessage fromString(String status) { | 
|  | switch (status) { | 
|  | case 'error': | 
|  | return DriverTestMessage.error(); | 
|  | case 'pending': | 
|  | return DriverTestMessage.pending(); | 
|  | case 'complete': | 
|  | return DriverTestMessage.complete(); | 
|  | default: | 
|  | throw StateError('This type of status does not exist: $status'); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Types of different WebDriver commands that can be used in web integration | 
|  | /// tests. | 
|  | /// | 
|  | /// These commands are either commands that WebDriver can execute or used | 
|  | /// for the communication between `integration_test` and the driver test. | 
|  | enum WebDriverCommandType { | 
|  | /// Acknowledgement for the previously sent message. | 
|  | ack, | 
|  |  | 
|  | /// No further WebDriver commands is requested by the app-side tests. | 
|  | noop, | 
|  |  | 
|  | /// Asking WebDriver to take a screenshot of the Web page. | 
|  | screenshot, | 
|  | } | 
|  |  | 
|  | /// Command for WebDriver to execute. | 
|  | /// | 
|  | /// Only works on Web when tests are run via `flutter driver` command. | 
|  | /// | 
|  | /// See: https://www.w3.org/TR/webdriver/ | 
|  | class WebDriverCommand { | 
|  | /// Constructor for [WebDriverCommandType.noop] command. | 
|  | WebDriverCommand.noop() | 
|  | : type = WebDriverCommandType.noop, | 
|  | values = <String, dynamic>{}; | 
|  |  | 
|  | /// Constructor for [WebDriverCommandType.noop] screenshot. | 
|  | WebDriverCommand.screenshot(String screenshotName) | 
|  | : type = WebDriverCommandType.screenshot, | 
|  | values = <String, dynamic>{'screenshot_name': screenshotName}; | 
|  |  | 
|  | /// Type of the [WebDriverCommand]. | 
|  | /// | 
|  | /// Currently the only command that triggers a WebDriver API is `screenshot`. | 
|  | /// | 
|  | /// There are also `ack` and `noop` commands defined to manage the handshake | 
|  | /// during the communication. | 
|  | final WebDriverCommandType type; | 
|  |  | 
|  | /// Used for adding extra values to the commands such as file name for | 
|  | /// `screenshot`. | 
|  | final Map<String, dynamic> values; | 
|  |  | 
|  | /// Util method for converting [WebDriverCommandType] to a map entry. | 
|  | /// | 
|  | /// Used for converting messages to json format. | 
|  | static Map<String, dynamic> typeToMap(WebDriverCommandType type) => <String, dynamic>{ | 
|  | 'web_driver_command': '$type', | 
|  | }; | 
|  | } | 
|  |  | 
|  | /// Template methods each class that responses the driver side inputs must | 
|  | /// implement. | 
|  | /// | 
|  | /// Depending on the platform the communication between `integration_tests` and | 
|  | /// the `driver_tests` can be different. | 
|  | /// | 
|  | /// For the web implementation [WebCallbackManager]. | 
|  | /// For the io implementation [IOCallbackManager]. | 
|  | abstract class CallbackManager { | 
|  | /// The callback function to response the driver side input. | 
|  | Future<Map<String, dynamic>> callback( | 
|  | Map<String, String> params, IntegrationTestResults testRunner); | 
|  |  | 
|  | /// Takes a screenshot of the application. | 
|  | /// Returns the data that is sent back to the host. | 
|  | Future<Map<String, dynamic>> takeScreenshot(String screenshot); | 
|  |  | 
|  | /// Android only. Converts the Flutter surface to an image view. | 
|  | Future<void> convertFlutterSurfaceToImage(); | 
|  |  | 
|  | /// Cleanup and completers or locks used during the communication. | 
|  | void cleanup(); | 
|  | } | 
|  |  | 
|  | /// Interface that surfaces test results of integration tests. | 
|  | /// | 
|  | /// Implemented by [IntegrationTestWidgetsFlutterBinding]s. | 
|  | /// | 
|  | /// Any class which needs to access the test results but do not want to create | 
|  | /// a cyclic dependency [IntegrationTestWidgetsFlutterBinding]s can use this | 
|  | /// interface. Example [CallbackManager]. | 
|  | abstract class IntegrationTestResults { | 
|  | /// Stores failure details. | 
|  | /// | 
|  | /// Failed test method's names used as key. | 
|  | List<Failure> get failureMethodsDetails; | 
|  |  | 
|  | /// The extra data for the reported result. | 
|  | Map<String, dynamic>? get reportData; | 
|  |  | 
|  | /// Whether all the test methods completed successfully. | 
|  | /// | 
|  | /// Completes when the tests have finished. The boolean value will be true if | 
|  | /// all tests have passed, and false otherwise. | 
|  | Completer<bool> get allTestsPassed; | 
|  | } |