Unverified Commit 11549e45 authored by Angjie Li's avatar Angjie Li Committed by GitHub

Use Async WebDriver for WebFlutterDriver. (#50835)

parent e7c90057
......@@ -9,7 +9,7 @@ import 'dart:io';
import 'package:matcher/matcher.dart';
import 'package:meta/meta.dart';
import 'package:vm_service_client/vm_service_client.dart';
import 'package:webdriver/sync_io.dart' as sync_io;
import 'package:webdriver/async_io.dart' as async_io;
import 'package:webdriver/support/async.dart';
import '../common/error.dart';
......@@ -109,7 +109,7 @@ class WebFlutterDriver extends FlutterDriver {
_checkBrowserSupportsTimeline();
final List<Map<String, dynamic>> events = <Map<String, dynamic>>[];
for (final sync_io.LogEntry entry in _connection.logs) {
for (final async_io.LogEntry entry in await _connection.logs.toList()) {
if (_startTime.isBefore(entry.timestamp)) {
final Map<String, dynamic> data = jsonDecode(entry.message)['message'] as Map<String, dynamic>;
if (data['method'] == 'Tracing.dataCollected') {
......@@ -168,7 +168,7 @@ class FlutterWebConnection {
/// and whether the WebDriver supports timeline action
FlutterWebConnection(this._driver, this._supportsTimelineAction);
final sync_io.WebDriver _driver;
final async_io.WebDriver _driver;
bool _supportsTimelineAction;
......@@ -191,12 +191,11 @@ class FlutterWebConnection {
{Duration timeout}) async {
// Use sync WebDriver because async version will create a 15 seconds
// overhead when quitting.
final sync_io.WebDriver driver = sync_io.fromExistingSession(
final async_io.WebDriver driver = await async_io.fromExistingSession(
settings['session-id'].toString(),
uri: Uri.parse(settings['session-uri'].toString()),
spec: _convertToSpec(settings['session-spec'].toString().toLowerCase()),
capabilities: jsonDecode(settings['session-capabilities'].toString()) as Map<String, dynamic>);
driver.get(url);
spec: _convertToSpec(settings['session-spec'].toString().toLowerCase()));
await driver.get(url);
await waitUntilExtensionInstalled(driver, timeout);
return FlutterWebConnection(driver, settings['support-timeline-action'] as bool);
......@@ -206,7 +205,7 @@ class FlutterWebConnection {
Future<dynamic> sendCommand(String script, Duration duration) async {
dynamic result;
try {
_driver.execute(script, <void>[]);
await _driver.execute(script, <void>[]);
} catch (_) {
// In case there is an exception, do nothing
}
......@@ -222,7 +221,7 @@ class FlutterWebConnection {
return null;
} finally {
// Resets the result.
_driver.execute(r'''
await _driver.execute(r'''
$flutterDriverResult = null
''', <void>[]);
}
......@@ -230,32 +229,32 @@ class FlutterWebConnection {
}
/// Gets performance log from WebDriver.
List<sync_io.LogEntry> get logs => _driver.logs.get(sync_io.LogType.performance);
Stream<async_io.LogEntry> get logs => _driver.logs.get(async_io.LogType.performance);
/// Takes screenshot via WebDriver.
List<int> screenshot() => _driver.captureScreenshotAsList();
Future<List<int>> screenshot() => _driver.captureScreenshotAsList();
/// Closes the WebDriver.
Future<void> close() async {
_driver.quit(closeSession: false);
await _driver.quit(closeSession: false);
}
}
/// Waits until extension is installed.
Future<void> waitUntilExtensionInstalled(sync_io.WebDriver driver, Duration timeout) async {
Future<void> waitUntilExtensionInstalled(async_io.WebDriver driver, Duration timeout) async {
await waitFor<void>(() =>
driver.execute(r'return typeof(window.$flutterDriver)', <String>[]),
matcher: 'function',
timeout: timeout ?? const Duration(days: 365));
}
sync_io.WebDriverSpec _convertToSpec(String specString) {
async_io.WebDriverSpec _convertToSpec(String specString) {
switch (specString.toLowerCase()) {
case 'webdriverspec.w3c':
return sync_io.WebDriverSpec.W3c;
return async_io.WebDriverSpec.W3c;
case 'webdriverspec.jsonwire':
return sync_io.WebDriverSpec.JsonWire;
return async_io.WebDriverSpec.JsonWire;
default:
return sync_io.WebDriverSpec.Auto;
return async_io.WebDriverSpec.Auto;
}
}
......@@ -5,8 +5,8 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:webdriver/sync_io.dart' as sync_io;
import 'package:meta/meta.dart';
import 'package:webdriver/async_io.dart' as async_io;
import '../application_package.dart';
import '../base/common.dart';
......@@ -146,6 +146,13 @@ class DriveCommand extends RunCommandBase {
if (argResults['use-existing-app'] == null) {
globals.printStatus('Starting application: $targetFile');
if (isWebPlatform) {
throwToolExit(
'Flutter Driver (web) does not support running without use-existing-app.\n'
'Please launch your application beforehand and connects via use-existing-app.'
);
}
if (getBuildInfo().isRelease && !isWebPlatform) {
// This is because we need VM service to be able to drive the app.
// For Flutter Web, testing in release mode is allowed.
......@@ -173,27 +180,46 @@ class DriveCommand extends RunCommandBase {
'VM_SERVICE_URL': observatoryUri,
};
sync_io.WebDriver driver;
async_io.WebDriver driver;
// For web device, WebDriver session will be launched beforehand
// so that FlutterDriver can reuse it.
if (isWebPlatform) {
final Browser browser = _browserNameToEnum(
argResults['browser-name'].toString());
final String driverPort = argResults['driver-port'].toString();
// start WebDriver
final Browser browser = _browserNameToEnum(argResults['browser-name'].toString());
driver = _createDriver(
argResults['driver-port'].toString(),
browser,
argResults['headless'].toString() == 'true',
);
try {
driver = await _createDriver(
driverPort,
browser,
argResults['headless'].toString() == 'true',
);
} on Exception catch (ex) {
throwToolExit(
'Unable to start WebDriver Session for Flutter for Web testing. \n'
'Make sure you have the correct WebDriver Server running at $driverPort. \n'
'Make sure the WebDriver Server matches option --browser-name. \n'
'$ex'
);
}
// set window size
final List<String> dimensions = argResults['browser-dimension'].split(',') as List<String>;
assert(dimensions.length == 2);
final int x = int.parse(dimensions[0]);
final int y = int.parse(dimensions[1]);
final sync_io.Window window = driver.window;
int x, y;
try {
window.setLocation(const math.Point<int>(0, 0));
window.setSize(math.Rectangle<int>(0, 0, x, y));
x = int.parse(dimensions[0]);
y = int.parse(dimensions[1]);
} on FormatException catch (ex) {
throwToolExit('''
Dimension provided to --browser-dimension is invalid:
$ex
''');
}
final async_io.Window window = await driver.window;
try {
await window.setLocation(const math.Point<int>(0, 0));
await window.setSize(math.Rectangle<int>(0, 0, x, y));
} catch (_) {
// Error might be thrown in some browsers.
}
......@@ -215,9 +241,9 @@ class DriveCommand extends RunCommandBase {
if (error is ToolExit) {
rethrow;
}
throwToolExit('CAUGHT EXCEPTION: $error\n$stackTrace');
throw Exception('Unable to run test: $error\n$stackTrace');
} finally {
driver?.quit();
await driver?.quit();
if (boolArg('keep-app-running') ?? (argResults['use-existing-app'] != null)) {
globals.printStatus('Leaving the application running.');
} else {
......@@ -437,11 +463,11 @@ Browser _browserNameToEnum(String browserName){
throw UnsupportedError('Browser $browserName not supported');
}
sync_io.WebDriver _createDriver(String driverPort, Browser browser, bool headless) {
return sync_io.createDriver(
uri: Uri.parse('http://localhost:$driverPort/wd/hub/'),
Future<async_io.WebDriver> _createDriver(String driverPort, Browser browser, bool headless) async {
return async_io.createDriver(
uri: Uri.parse('http://localhost:$driverPort/'),
desired: getDesiredCapabilities(browser, headless),
spec: browser != Browser.iosSafari ? sync_io.WebDriverSpec.JsonWire : sync_io.WebDriverSpec.W3c
spec: async_io.WebDriverSpec.Auto
);
}
......@@ -453,7 +479,7 @@ Map<String, dynamic> getDesiredCapabilities(Browser browser, bool headless) {
return <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'chrome',
'goog:loggingPrefs': <String, String>{ sync_io.LogType.performance: 'ALL'},
'goog:loggingPrefs': <String, String>{ async_io.LogType.performance: 'ALL'},
'chromeOptions': <String, dynamic>{
'w3c': false,
'args': <String>[
......@@ -508,10 +534,6 @@ Map<String, dynamic> getDesiredCapabilities(Browser browser, bool headless) {
case Browser.safari:
return <String, dynamic>{
'browserName': 'safari',
'safari.options': <String, dynamic>{
'skipExtensionInstallation': true,
'cleanSession': true
}
};
break;
case Browser.iosSafari:
......
......@@ -699,10 +699,6 @@ void main() {
test('macOS Safari', () {
final Map<String, dynamic> expected = <String, dynamic>{
'browserName': 'safari',
'safari.options': <String, dynamic>{
'skipExtensionInstallation': true,
'cleanSession': true
}
};
expect(getDesiredCapabilities(Browser.safari, false), expected);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment