blob: fa9a0bd8c98d4bd2991bc180ed0006e460413e8f [file] [log] [blame] [edit]
// 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 '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<Map<String, dynamic>> takeScreenshot(String screenshotName) async {
await _sendWebDriverCommand(WebDriverCommand.screenshot(screenshotName));
// Flutter Web doesn't provide the bytes.
return const <String, dynamic>{'bytes': <int>[]};
}
@override
Future<void> convertFlutterSurfaceToImage() async {
// Noop on Web.
}
Future<void> _sendWebDriverCommand(WebDriverCommand command) async {
try {
_webDriverCommandPipe.complete(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);
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<String, dynamic>.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 = <String, dynamic>{};
data.addAll(WebDriverCommand.typeToMap(WebDriverCommandType.noop));
response = <String, String>{
'message': Response.webDriverCommand(data: data).toJson(),
};
break;
case WebDriverCommandType.ack:
throw UnimplementedError('${command.type} is not implemented');
}
} else {
final Map<String, dynamic> data = <String, dynamic>{};
data.addAll(WebDriverCommand.typeToMap(WebDriverCommandType.ack));
response = <String, String>{
'message': Response.webDriverCommand(data: data).toJson(),
};
_driverCommandComplete.complete(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));
}
}
}