blob: 8ef198420c4163400b370154249cb80804bed470 [file] [log] [blame]
// 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.
// @dart = 2.8
import 'dart:async';
import 'dart:math' as math;
import 'package:file/file.dart';
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import 'package:webdriver/async_io.dart' as async_io;
import '../base/common.dart';
import '../base/process.dart';
import '../build_info.dart';
import '../convert.dart';
import '../device.dart';
import '../globals_null_migrated.dart' as globals;
import '../project.dart';
import '../resident_runner.dart';
import '../web/web_runner.dart';
import 'drive_service.dart';
/// An implementation of the driver service for web debug and release applications.
class WebDriverService extends DriverService {
@required ProcessUtils processUtils,
@required String dartSdkPath,
}) : _processUtils = processUtils,
_dartSdkPath = dartSdkPath;
final ProcessUtils _processUtils;
final String _dartSdkPath;
ResidentRunner _residentRunner;
Uri _webUri;
Future<void> start(
BuildInfo buildInfo,
Device device,
DebuggingOptions debuggingOptions,
bool ipv6, {
File applicationBinary,
String route,
String userIdentifier,
String mainPath,
Map<String, Object> platformArgs = const <String, Object>{},
}) async {
final FlutterDevice flutterDevice = await FlutterDevice.create(
target: mainPath,
buildInfo: buildInfo,
platform: globals.platform,
_residentRunner = webRunnerFactory.createWebRunner(
target: mainPath,
ipv6: ipv6,
debuggingOptions: buildInfo.isRelease ?
port: debuggingOptions.port,
: DebuggingOptions.enabled(
port: debuggingOptions.port,
disablePortPublication: debuggingOptions.disablePortPublication,
stayResident: false,
urlTunneller: null,
flutterProject: FlutterProject.current(),
fileSystem: globals.fs,
usage: globals.flutterUsage,
logger: globals.logger,
systemClock: globals.systemClock,
final Completer<void> appStartedCompleter = Completer<void>.sync();
final int result = await
appStartedCompleter: appStartedCompleter,
enableDevTools: false,
route: route,
_webUri = _residentRunner.uri;
if (result != 0) {
Future<int> startTest(String testFile, List<String> arguments, Map<String, String> environment, PackageConfig packageConfig, {
bool headless,
String chromeBinary,
String browserName,
bool androidEmulator,
int driverPort,
List<String> browserDimension,
}) async {
async_io.WebDriver webDriver;
final Browser browser = _browserNameToEnum(browserName);
try {
webDriver = await async_io.createDriver(
uri: Uri.parse('http://localhost:$driverPort/'),
desired: getDesiredCapabilities(browser, headless, chromeBinary),
spec: async_io.WebDriverSpec.Auto
} on Exception catch (ex) {
'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'
final bool isAndroidChrome = browser == Browser.androidChrome;
// Do not set the window size for android chrome browser.
if (!isAndroidChrome) {
assert(browserDimension.length == 2);
int x;
int y;
try {
x = int.parse(browserDimension[0]);
y = int.parse(browserDimension[1]);
} on FormatException catch (ex) {
throwToolExit('Dimension provided to --browser-dimension is invalid: $ex');
final async_io.Window window = await webDriver.window;
await window.setLocation(const math.Point<int>(0, 0));
await window.setSize(math.Rectangle<int>(0, 0, x, y));
final int result = await<String>[
], environment: <String, String>{
'VM_SERVICE_URL': _webUri.toString(),
..._additionalDriverEnvironment(webDriver, browserName, androidEmulator),
await webDriver.quit();
return result;
Future<void> stop({File writeSkslOnExit, String userIdentifier}) async {
await _residentRunner.cleanupAtFinish();
Map<String, String> _additionalDriverEnvironment(async_io.WebDriver webDriver, String browserName, bool androidEmulator) {
return <String, String>{
'DRIVER_SESSION_URI': webDriver.uri.toString(),
'DRIVER_SESSION_SPEC': webDriver.spec.toString(),
'DRIVER_SESSION_CAPABILITIES': json.encode(webDriver.capabilities),
'SUPPORT_TIMELINE_ACTION': (_browserNameToEnum(browserName) ==,
'ANDROID_CHROME_ON_EMULATOR': (_browserNameToEnum(browserName) == Browser.androidChrome && androidEmulator).toString(),
Future<void> reuseApplication(Uri vmServiceUri, Device device, DebuggingOptions debuggingOptions, bool ipv6) async {
throwToolExit('--use-existing-app is not supported with flutter web driver');
/// A list of supported browsers.
enum Browser {
/// Chrome on Android:
/// Chrome:
/// Edge:
/// Firefox:
/// Safari in iOS:
/// Safari in macOS:
/// Returns desired capabilities for given [browser], [headless] and
/// [chromeBinary].
Map<String, dynamic> getDesiredCapabilities(Browser browser, bool headless, [String chromeBinary]) {
switch (browser) {
return <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'chrome',
'goog:loggingPrefs': <String, String>{
async_io.LogType.browser: 'INFO',
async_io.LogType.performance: 'ALL',
'chromeOptions': <String, dynamic>{
if (chromeBinary != null)
'binary': chromeBinary,
'w3c': false,
'args': <String>[
if (headless) '--headless'
'perfLoggingPrefs': <String, String>{
case Browser.firefox:
return <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'firefox',
'moz:firefoxOptions' : <String, dynamic>{
'args': <String>[
if (headless) '-headless'
'prefs': <String, dynamic>{
'dom.file.createInChild': true,
'dom.timeout.background_throttling_max_budget': -1,
'media.autoplay.default': 0,
'media.gmp-manager.url': '',
'media.gmp-provider.enabled': false,
'network.captive-portal-service.enabled': false,
'security.insecure_field_warning.contextual.enabled': false,
'test.currentTimeOffsetSeconds': 11491200
'log': <String, String>{'level': 'trace'}
case Browser.edge:
return <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'edge',
case Browser.safari:
return <String, dynamic>{
'browserName': 'safari',
case Browser.iosSafari:
return <String, dynamic>{
'platformName': 'ios',
'browserName': 'safari',
'safari:useSimulator': true
case Browser.androidChrome:
return <String, dynamic>{
'browserName': 'chrome',
'platformName': 'android',
'goog:chromeOptions': <String, dynamic>{
'androidPackage': '',
'args': <String>['--disable-fullscreen']
throw UnsupportedError('Browser $browser not supported.');
/// Converts [browserName] string to [Browser]
Browser _browserNameToEnum(String browserName){
switch (browserName) {
case 'android-chrome': return Browser.androidChrome;
case 'chrome': return;
case 'edge': return Browser.edge;
case 'firefox': return Browser.firefox;
case 'ios-safari': return Browser.iosSafari;
case 'safari': return Browser.safari;
throw UnsupportedError('Browser $browserName not supported');