| // 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 'dart:convert'; | 
 |  | 
 | /// 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 { | 
 |   final List<Failure> _failureDetails; | 
 |  | 
 |   final bool _allTestsPassed; | 
 |  | 
 |   /// The extra information to be added along side the test result. | 
 |   Map<String, dynamic> data; | 
 |  | 
 |   /// Constructor to use for positive response. | 
 |   Response.allTestsPassed({this.data}) | 
 |       : this._allTestsPassed = true, | 
 |         this._failureDetails = null; | 
 |  | 
 |   /// Constructor for failure response. | 
 |   Response.someTestsFailed(this._failureDetails, {this.data}) | 
 |       : this._allTestsPassed = false; | 
 |  | 
 |   /// Constructor for failure response. | 
 |   Response.toolException({String ex}) | 
 |       : this._allTestsPassed = false, | 
 |         this._failureDetails = [Failure('ToolException', ex)]; | 
 |  | 
 |   /// Constructor for web driver commands response. | 
 |   Response.webDriverCommand({this.data}) | 
 |       : this._allTestsPassed = false, | 
 |         this._failureDetails = null; | 
 |  | 
 |   /// 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); | 
 |     if (responseJson['result'] as String == 'true') { | 
 |       return Response.allTestsPassed(data: responseJson['data']); | 
 |     } else { | 
 |       return Response.someTestsFailed( | 
 |         _failureDetailsFromJson(responseJson['failureDetails']), | 
 |         data: responseJson['data'], | 
 |       ); | 
 |     } | 
 |   } | 
 |  | 
 |   /// Method for formatting the test failures' details. | 
 |   String formatFailures(List<Failure> failureDetails) { | 
 |     if (failureDetails.isEmpty) { | 
 |       return ''; | 
 |     } | 
 |  | 
 |     StringBuffer sb = StringBuffer(); | 
 |     int failureCount = 1; | 
 |     failureDetails.forEach((Failure f) { | 
 |       sb.writeln('Failure in method: ${f.methodName}'); | 
 |       sb.writeln('${f.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; | 
 |     } | 
 |  | 
 |     _failureDetails.forEach((Failure f) { | 
 |       list.add(f.toJson()); | 
 |     }); | 
 |  | 
 |     return list; | 
 |   } | 
 |  | 
 |   /// Creates a [Failure] list using a json response. | 
 |   static List<Failure> _failureDetailsFromJson(List<dynamic> list) { | 
 |     final List<Failure> failureList = <Failure>[]; | 
 |     list.forEach((s) { | 
 |       final String failure = s as String; | 
 |       failureList.add(Failure.fromJsonString(failure)); | 
 |     }); | 
 |     return failureList; | 
 |   } | 
 | } | 
 |  | 
 | /// Representing a failure includes the method name and the failure details. | 
 | class Failure { | 
 |   /// The name of the test method which failed. | 
 |   final String methodName; | 
 |  | 
 |   /// The details of the failure such as stack trace. | 
 |   final String details; | 
 |  | 
 |   /// Constructor requiring all fields during initialization. | 
 |   Failure(this.methodName, this.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) { | 
 |     Map<String, dynamic> failure = json.decode(jsonString); | 
 |     return Failure(failure['methodName'], failure['details']); | 
 |   } | 
 | } | 
 |  | 
 | /// 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 { | 
 |   final bool _isSuccess; | 
 |   final bool _isPending; | 
 |  | 
 |   /// 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; | 
 |  | 
 |   // /// 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 { | 
 |   /// Acknowlegement 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 { | 
 |   /// 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; | 
 |  | 
 |   /// Constructor for [WebDriverCommandType.noop] command. | 
 |   WebDriverCommand.noop() | 
 |       : this.type = WebDriverCommandType.noop, | 
 |         this.values = Map(); | 
 |  | 
 |   /// Constructor for [WebDriverCommandType.noop] screenshot. | 
 |   WebDriverCommand.screenshot(String screenshot_name) | 
 |       : this.type = WebDriverCommandType.screenshot, | 
 |         this.values = {'screenshot_name': screenshot_name}; | 
 |  | 
 |   /// Util method for converting [WebDriverCommandType] to a map entry. | 
 |   /// | 
 |   /// Used for converting messages to json format. | 
 |   static Map<String, dynamic> typeToMap(WebDriverCommandType type) => { | 
 |         '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); | 
 |  | 
 |   /// Request to take a screenshot of the application. | 
 |   Future<void> takeScreenshot(String screenshot); | 
 |  | 
 |   /// 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 succesfully. | 
 |   Completer<bool> get allTestsPassed; | 
 | } |