// 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. // This is a CLI library; we use prints as part of the interface. // ignore_for_file: avoid_print import 'dart:async'; import 'dart:io'; import 'package:flutter_driver/flutter_driver.dart'; import 'common.dart'; /// Adaptor to run an integration test using `flutter drive`. /// /// To an integration test `<test_name>.dart` using `flutter drive`, put a file named /// `<test_name>_test.dart` in the app's `test_driver` directory: /// /// ```dart /// import 'dart:async'; /// /// import 'package:integration_test/integration_test_driver_extended.dart'; /// /// Future<void> main() async { /// final FlutterDriver driver = await FlutterDriver.connect(); /// await integrationDriver( /// driver: driver, /// onScreenshot: (String screenshotName, List<int> screenshotBytes) async { /// return true; /// }, /// ); /// } /// ``` /// /// ## Parameters: /// /// `driver` A custom driver. Defaults to `FlutterDriver.connect()`. /// /// `onScreenshot` can be used to process the screenshots taken during the test. /// An example could be that this callback compares the byte array against a baseline image, /// and it returns `true` if both images are equal. /// /// As a result, returning `false` from `onScreenshot` will make the test fail. Future<void> integrationDriver( {FlutterDriver? driver, ScreenshotCallback? onScreenshot}) async { driver ??= await FlutterDriver.connect(); // Test states that it's waiting on web driver commands. // [DriverTestMessage] is converted to string since json format causes an // error if it's used as a message for requestData. String jsonResponse = await driver.requestData(DriverTestMessage.pending().toString()); final Map<String, bool> onScreenshotResults = <String, bool>{}; Response response = Response.fromJson(jsonResponse); // Until `integration_test` returns a [WebDriverCommandType.noop], keep // executing WebDriver commands. while (response.data != null && response.data!['web_driver_command'] != null && response.data!['web_driver_command'] != '${WebDriverCommandType.noop}') { final String? webDriverCommand = response.data!['web_driver_command'] as String?; if (webDriverCommand == '${WebDriverCommandType.screenshot}') { assert(onScreenshot != null, 'screenshot command requires an onScreenshot callback'); // Use `driver.screenshot()` method to get a screenshot of the web page. final List<int> screenshotImage = await driver.screenshot(); final String screenshotName = response.data!['screenshot_name']! as String; final Map<String, Object?>? args = (response.data!['args'] as Map<String, Object?>?)?.cast<String, Object?>(); final bool screenshotSuccess = await onScreenshot!(screenshotName, screenshotImage, args); onScreenshotResults[screenshotName] = screenshotSuccess; if (screenshotSuccess) { jsonResponse = await driver.requestData(DriverTestMessage.complete().toString()); } else { jsonResponse = await driver.requestData(DriverTestMessage.error().toString()); } response = Response.fromJson(jsonResponse); } else if (webDriverCommand == '${WebDriverCommandType.ack}') { // Previous command completed ask for a new one. jsonResponse = await driver.requestData(DriverTestMessage.pending().toString()); response = Response.fromJson(jsonResponse); } else { break; } } // If No-op command is sent, ask for the result of all tests. if (response.data != null && response.data!['web_driver_command'] != null && response.data!['web_driver_command'] == '${WebDriverCommandType.noop}') { jsonResponse = await driver.requestData(null); response = Response.fromJson(jsonResponse); print('result $jsonResponse'); } if (response.data != null && response.data!['screenshots'] != null && onScreenshot != null) { final List<dynamic> screenshots = response.data!['screenshots'] as List<dynamic>; final List<String> failures = <String>[]; for (final dynamic screenshot in screenshots) { final Map<String, dynamic> data = screenshot as Map<String, dynamic>; final List<dynamic> screenshotBytes = data['bytes'] as List<dynamic>; final String screenshotName = data['screenshotName'] as String; bool ok = false; try { ok = onScreenshotResults[screenshotName] ?? await onScreenshot(screenshotName, screenshotBytes.cast<int>()); } catch (exception) { throw StateError( 'Screenshot failure:\n' 'onScreenshot("$screenshotName", <bytes>) threw an exception: $exception', ); } if (!ok) { failures.add(screenshotName); } } if (failures.isNotEmpty) { throw StateError('The following screenshot tests failed: ${failures.join(', ')}'); } } await driver.close(); if (response.allTestsPassed) { print('All tests passed.'); exit(0); } else { print('Failure Details:\n${response.formattedFailureDetails}'); exit(1); } }