1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// 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, [Map<String, Object?>? args]) async {
await _sendWebDriverCommand(WebDriverCommand.screenshot(screenshotName, args));
return <String, dynamic>{
'screenshotName': screenshotName,
// Flutter Web doesn't provide the bytes.
'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'};
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(),
};
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(),
};
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));
}
}
}