Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/integration_test/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.9.0

* Add screenshot capability to web tests.

## 0.8.2

* Add support to get timeline.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This is a Flutter widget test can take a screenshot.
//
// NOTE: Screenshots are only supported on Web for now.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.

import 'package:integration_test/integration_test.dart';

import 'example_integration_io_extended.dart'
if (dart.library.html) 'example_integration_web_extended.dart' as tests;

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
tests.main();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'dart:async';

import 'package:flutter_driver/flutter_driver.dart';
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;
},
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.

import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import 'package:integration_test_example/main.dart' as app;

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

testWidgets('verify text', (WidgetTester tester) async {
// Build our app and trigger a frame.
app.main();

// Trigger a frame.
await tester.pumpAndSettle();

// TODO: https://github.com/flutter/flutter/issues/51890
// Add screenshot capability for mobile platforms.

// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) =>
widget is Text &&
widget.data.startsWith('Platform: ${Platform.operatingSystem}'),
),
findsOneWidget,
);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.

import 'dart:html' as html;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import 'package:integration_test_example/main.dart' as app;

void main() {
final IntegrationTestWidgetsFlutterBinding binding =
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

testWidgets('verify text', (WidgetTester tester) async {
// Build our app and trigger a frame.
app.main();

// Trigger a frame.
await tester.pumpAndSettle();

// Take a screenshot.
await binding.takeScreenshot('platform_name');

// Verify that platform is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) =>
widget is Text &&
widget.data
.startsWith('Platform: ${html.window.navigator.platform}\n'),
),
findsOneWidget,
);
});

testWidgets('verify screenshot', (WidgetTester tester) async {
// Build our app and trigger a frame.
app.main();

// Trigger a frame.
await tester.pumpAndSettle();

// Multiple methods can take screenshots. Screenshots are taken with the
// same order the methods run.
await binding.takeScreenshot('platform_name_2');
});
}
62 changes: 62 additions & 0 deletions packages/integration_test/lib/_callback_io.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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 'common.dart';

/// The dart:io implementation of [CallbackManager].
///
/// See also:
///
/// * [_callback_web.dart], which has the dart:html implementation
CallbackManager get callbackManager => _singletonCallbackManager;

/// IOCallbackManager singleton.
final IOCallbackManager _singletonCallbackManager = IOCallbackManager();

/// Manages communication between `integration_tests` and the `driver_tests`.
///
/// This is the dart:io implementation.
class IOCallbackManager implements CallbackManager {
@override
Future<Map<String, dynamic>> callback(
Map<String, String> params, IntegrationTestResults testRunner) async {
final String command = params['command'];
Map<String, String> response;
switch (command) {
case 'request_data':
final bool allTestsPassed = await testRunner.allTestsPassed.future;
response = <String, String>{
'message': allTestsPassed
? Response.allTestsPassed(data: testRunner.reportData).toJson()
: Response.someTestsFailed(
testRunner.failureMethodsDetails,
data: testRunner.reportData,
).toJson(),
};
break;
case 'get_health':
response = <String, String>{'status': 'ok'};
break;
default:
throw UnimplementedError('$command is not implemented');
}
return <String, dynamic>{
'isError': false,
'response': response,
};
}

@override
void cleanup() {
// no-op.
// Add any IO platform specific Completer/Future cleanups to here if any
// comes up in the future. For example: `WebCallbackManager.cleanup`.
}

@override
Future<void> takeScreenshot(String screenshot) {
throw UnimplementedError(
'Screenshots are not implemented on this platform');
}
}
170 changes: 170 additions & 0 deletions packages/integration_test/lib/_callback_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// 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 'common.dart';

/// The dart:html implementation of [CallbackManager].
///
/// See also:
///
/// * [_callback_io.dart], which has the dart:io implementation
CallbackManager get callbackManager => _singletonWebDriverCommandManager;

/// WebDriverCommandManager singleton.
final WebCallbackManager _singletonWebDriverCommandManager =
WebCallbackManager();

/// Manages communication between `integration_tests` and the `driver_tests`.
///
/// Along with responding to callbacks from the driver side this calls enables
/// usage of Web Driver commands by sending [WebDriverCommand]s to driver side.
///
/// Tests can execute an Web Driver commands such as `screenshot` using browsers'
/// WebDriver APIs.
///
/// See: https://www.w3.org/TR/webdriver/
class WebCallbackManager implements CallbackManager {
/// App side tests will put the command requests from WebDriver to this pipe.
Completer<WebDriverCommand> _webDriverCommandPipe =
Completer<WebDriverCommand>();

/// Updated when WebDriver completes the request by the test method.
///
/// For example, a test method will ask for a screenshot by calling
/// `takeScreenshot`. When this screenshot is taken [_driverCommandComplete]
/// will complete.
Completer<bool> _driverCommandComplete = Completer<bool>();

/// Takes screenshot using WebDriver screenshot command.
///
/// Only works on Web when tests are run via `flutter driver` command.
///
/// See: https://www.w3.org/TR/webdriver/#screen-capture.
@override
Future<void> takeScreenshot(String screenshotName) async {
await _sendWebDriverCommand(WebDriverCommand.screenshot(screenshotName));
}

Future<void> _sendWebDriverCommand(WebDriverCommand command) async {
try {
_webDriverCommandPipe.complete(Future.value(command));
final bool awaitCommand = await _driverCommandComplete.future;
if (!awaitCommand) {
throw Exception(
'Web Driver Command ${command.type} failed while waiting for '
'driver side');
}
} catch (exception) {
throw Exception('Web Driver Command failed: ${command.type} with '
'exception $exception');
} finally {
// Reset the completer.
_driverCommandComplete = Completer<bool>();
}
}

/// The callback function to response the driver side input.
///
/// Provides a handshake mechanism for executing [WebDriverCommand]s on the
/// driver side.
@override
Future<Map<String, dynamic>> callback(
Map<String, String> params, IntegrationTestResults testRunner) async {
final String command = params['command'];
Map<String, String> response;
switch (command) {
case 'request_data':
return params['message'] == null
? _requestData(testRunner)
: _requestDataWithMessage(params['message'], testRunner);
break;
case 'get_health':
response = <String, String>{'status': 'ok'};
break;
default:
throw UnimplementedError('$command is not implemented');
}
return <String, dynamic>{
'isError': false,
'response': response,
};
}

Future<Map<String, dynamic>> _requestDataWithMessage(
String extraMessage, IntegrationTestResults testRunner) async {
Map<String, String> response;
// Driver side tests' status is added as an extra message.
final DriverTestMessage message =
DriverTestMessage.fromString(extraMessage);
// If driver side tests are pending send the first command in the
// `commandPipe` to the tests.
if (message.isPending) {
final WebDriverCommand command = await _webDriverCommandPipe.future;
switch (command.type) {
case WebDriverCommandType.screenshot:
final Map<String, dynamic> data = Map.from(command.values);
data.addAll(
WebDriverCommand.typeToMap(WebDriverCommandType.screenshot));
response = <String, String>{
'message': Response.webDriverCommand(data: data).toJson(),
};
break;
case WebDriverCommandType.noop:
final Map<String, dynamic> data = Map();
data.addAll(WebDriverCommand.typeToMap(WebDriverCommandType.noop));
response = <String, String>{
'message': Response.webDriverCommand(data: data).toJson(),
};
break;
default:
throw UnimplementedError('${command.type} is not implemented');
}
} else {
final Map<String, dynamic> data = Map();
data.addAll(WebDriverCommand.typeToMap(WebDriverCommandType.ack));
response = <String, String>{
'message': Response.webDriverCommand(data: data).toJson(),
};
_driverCommandComplete.complete(Future.value(message.isSuccess));
_webDriverCommandPipe = Completer<WebDriverCommand>();
}
return <String, dynamic>{
'isError': false,
'response': response,
};
}

Future<Map<String, dynamic>> _requestData(
IntegrationTestResults testRunner) async {
final bool allTestsPassed = await testRunner.allTestsPassed.future;
final Map<String, String> response = <String, String>{
'message': allTestsPassed
? Response.allTestsPassed(data: testRunner.reportData).toJson()
: Response.someTestsFailed(
testRunner.failureMethodsDetails,
data: testRunner.reportData,
).toJson(),
};
return <String, dynamic>{
'isError': false,
'response': response,
};
}

@override
void cleanup() {
if (!_webDriverCommandPipe.isCompleted) {
_webDriverCommandPipe
.complete(Future<WebDriverCommand>.value(WebDriverCommand.noop()));
}

if (!_driverCommandComplete.isCompleted) {
_driverCommandComplete.complete(Future<bool>.value(false));
}
}
}
Loading