blob: b3f3ad46563bea6320723e37f8a698d1c6d0833b [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.
import 'dart:async';
import 'package:file/src/interface/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/drive/web_driver_service.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/web/web_runner.dart';
import 'package:test/fake.dart';
import 'package:webdriver/sync_io.dart' as sync_io;
import '../../src/common.dart';
import '../../src/context.dart';
const List<String> kChromeArgs = <String>[
'--bwsi',
'--disable-background-timer-throttling',
'--disable-default-apps',
'--disable-extensions',
'--disable-popup-blocking',
'--disable-translate',
'--no-default-browser-check',
'--no-sandbox',
'--no-first-run',
];
void main() {
testWithoutContext('getDesiredCapabilities Chrome with headless on', () {
final Map<String, dynamic> expected = <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'chrome',
'goog:loggingPrefs': <String, String>{
sync_io.LogType.browser: 'INFO',
sync_io.LogType.performance: 'ALL',
},
'chromeOptions': <String, dynamic>{
'w3c': false,
'args': <String>[
...kChromeArgs,
'--headless',
],
'perfLoggingPrefs': <String, String>{
'traceCategories':
'devtools.timeline,'
'v8,blink.console,benchmark,blink,'
'blink.user_timing',
},
},
};
expect(getDesiredCapabilities(Browser.chrome, true), expected);
});
testWithoutContext('getDesiredCapabilities Chrome with headless off', () {
const String chromeBinary = 'random-binary';
final Map<String, dynamic> expected = <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'chrome',
'goog:loggingPrefs': <String, String>{
sync_io.LogType.browser: 'INFO',
sync_io.LogType.performance: 'ALL',
},
'chromeOptions': <String, dynamic>{
'binary': chromeBinary,
'w3c': false,
'args': kChromeArgs,
'perfLoggingPrefs': <String, String>{
'traceCategories':
'devtools.timeline,'
'v8,blink.console,benchmark,blink,'
'blink.user_timing',
},
},
};
expect(getDesiredCapabilities(Browser.chrome, false, chromeBinary: chromeBinary), expected);
});
testWithoutContext('getDesiredCapabilities Chrome with browser flags', () {
const List<String> webBrowserFlags = <String>[
'--autoplay-policy=no-user-gesture-required',
'--incognito',
'--auto-select-desktop-capture-source="Entire screen"',
];
final Map<String, dynamic> expected = <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'chrome',
'goog:loggingPrefs': <String, String>{
sync_io.LogType.browser: 'INFO',
sync_io.LogType.performance: 'ALL',
},
'chromeOptions': <String, dynamic>{
'w3c': false,
'args': <String>[
...kChromeArgs,
'--autoplay-policy=no-user-gesture-required',
'--incognito',
'--auto-select-desktop-capture-source="Entire screen"',
],
'perfLoggingPrefs': <String, String>{
'traceCategories':
'devtools.timeline,'
'v8,blink.console,benchmark,blink,'
'blink.user_timing',
},
},
};
expect(getDesiredCapabilities(Browser.chrome, false, webBrowserFlags: webBrowserFlags), expected);
});
testWithoutContext('getDesiredCapabilities Firefox with headless on', () {
final Map<String, dynamic> expected = <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'firefox',
'moz:firefoxOptions' : <String, dynamic>{
'args': <String>['-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'},
},
};
expect(getDesiredCapabilities(Browser.firefox, true), expected);
});
testWithoutContext('getDesiredCapabilities Firefox with headless off', () {
final Map<String, dynamic> expected = <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'firefox',
'moz:firefoxOptions' : <String, dynamic>{
'args': <String>[],
'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'},
},
};
expect(getDesiredCapabilities(Browser.firefox, false), expected);
});
testWithoutContext('getDesiredCapabilities Firefox with browser flags', () {
const List<String> webBrowserFlags = <String>[
'-url=https://example.com',
'-private',
];
final Map<String, dynamic> expected = <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'firefox',
'moz:firefoxOptions' : <String, dynamic>{
'args': <String>[
'-url=https://example.com',
'-private',
],
'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'},
},
};
expect(getDesiredCapabilities(Browser.firefox, false, webBrowserFlags: webBrowserFlags), expected);
});
testWithoutContext('getDesiredCapabilities Edge', () {
final Map<String, dynamic> expected = <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'edge',
};
expect(getDesiredCapabilities(Browser.edge, false), expected);
});
testWithoutContext('getDesiredCapabilities macOS Safari', () {
final Map<String, dynamic> expected = <String, dynamic>{
'browserName': 'safari',
};
expect(getDesiredCapabilities(Browser.safari, false), expected);
});
testWithoutContext('getDesiredCapabilities iOS Safari', () {
final Map<String, dynamic> expected = <String, dynamic>{
'platformName': 'ios',
'browserName': 'safari',
'safari:useSimulator': true,
};
expect(getDesiredCapabilities(Browser.iosSafari, false), expected);
});
testWithoutContext('getDesiredCapabilities android chrome', () {
const List<String> webBrowserFlags = <String>[
'--autoplay-policy=no-user-gesture-required',
'--incognito',
];
final Map<String, dynamic> expected = <String, dynamic>{
'browserName': 'chrome',
'platformName': 'android',
'goog:chromeOptions': <String, dynamic>{
'androidPackage': 'com.android.chrome',
'args': <String>[
'--disable-fullscreen',
'--autoplay-policy=no-user-gesture-required',
'--incognito',
],
},
};
expect(getDesiredCapabilities(Browser.androidChrome, false, webBrowserFlags: webBrowserFlags), expected);
});
testUsingContext('WebDriverService starts and stops an app', () async {
final WebDriverService service = setUpDriverService();
final FakeDevice device = FakeDevice();
await service.start(BuildInfo.profile, device, DebuggingOptions.enabled(BuildInfo.profile), true);
await service.stop();
expect(FakeResidentRunner.instance.callLog, <String>[
'run',
'exitApp',
'cleanupAtFinish',
]);
}, overrides: <Type, Generator>{
WebRunnerFactory: () => FakeWebRunnerFactory(),
});
testUsingContext('WebDriverService forwards exception when run future fails before app starts', () async {
final WebDriverService service = setUpDriverService();
final Device device = FakeDevice();
await expectLater(
service.start(BuildInfo.profile, device, DebuggingOptions.enabled(BuildInfo.profile), true),
throwsA('This is a test error'),
);
}, overrides: <Type, Generator>{
WebRunnerFactory: () => FakeWebRunnerFactory(
doResolveToError: true,
),
});
}
class FakeWebRunnerFactory implements WebRunnerFactory {
FakeWebRunnerFactory({
this.doResolveToError = false,
});
final bool doResolveToError;
@override
ResidentRunner createWebRunner(FlutterDevice device, {String? target, bool? stayResident, FlutterProject? flutterProject, bool? ipv6, DebuggingOptions? debuggingOptions, UrlTunneller? urlTunneller, Logger? logger, FileSystem? fileSystem, SystemClock? systemClock, Usage? usage, bool machine = false}) {
expect(stayResident, isTrue);
return FakeResidentRunner(
doResolveToError: doResolveToError,
);
}
}
class FakeResidentRunner extends Fake implements ResidentRunner {
FakeResidentRunner({
required this.doResolveToError,
}) {
instance = this;
}
static late FakeResidentRunner instance;
final bool doResolveToError;
final Completer<int> _exitCompleter = Completer<int>();
final List<String> callLog = <String>[];
@override
Uri get uri => Uri();
@override
Future<int> run({
Completer<DebugConnectionInfo>? connectionInfoCompleter,
Completer<void>? appStartedCompleter,
bool enableDevTools = false,
String? route,
}) async {
callLog.add('run');
if (doResolveToError) {
return Future<int>.error('This is a test error');
}
appStartedCompleter?.complete();
// Emulate stayResident by completing after exitApp is called.
return _exitCompleter.future;
}
@override
Future<void> exitApp() async {
callLog.add('exitApp');
_exitCompleter.complete(0);
}
@override
Future<void> cleanupAtFinish() async {
callLog.add('cleanupAtFinish');
}
}
WebDriverService setUpDriverService() {
final BufferLogger logger = BufferLogger.test();
return WebDriverService(
logger: logger,
processUtils: ProcessUtils(
logger: logger,
processManager: FakeProcessManager.any(),
),
dartSdkPath: 'dart',
);
}
// Unfortunately Device, despite not being immutable, has an `operator ==`.
// Until we fix that, we have to also ignore related lints here.
// ignore: avoid_implementing_value_types
class FakeDevice extends Fake implements Device {
@override
final PlatformType platformType = PlatformType.web;
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm;
}