blob: b37f56a7c676c7205b1180c35992fe44c78958b9 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@OnPlatform({'windows': Skip('appveyor is not setup to install Chrome')})
import 'dart:async';
import 'dart:io';
import 'package:browser_launcher/src/chrome.dart';
import 'package:logging/logging.dart';
import 'package:test/test.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
const _headlessOnlyEnvironment = 'HEADLESS_ONLY';
bool get headlessOnlyEnvironment =>
Platform.environment[_headlessOnlyEnvironment] == 'true';
void _configureLogging(bool verbose) {
Logger.root.level = verbose ? Level.ALL : Level.INFO;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
}
void main() {
Chrome? chrome;
// Pass 'true' for debugging.
_configureLogging(false);
Future<ChromeTab?> getTab(String url) => chrome!.chromeConnection.getTab(
(t) => t.url.contains(url),
retryFor: const Duration(seconds: 5),
);
Future<List<ChromeTab>?> getTabs() => chrome!.chromeConnection.getTabs(
retryFor: const Duration(seconds: 5),
);
Future<WipConnection> connectToTab(String url) async {
final tab = await getTab(url);
expect(tab, isNotNull);
return tab!.connect();
}
Future<HttpClientResponse> openTab(String url) =>
chrome!.chromeConnection.getUrl(_openTabUrl(url));
Future<void> launchChromeWithDebugPort({
int port = 0,
String? userDataDir,
bool signIn = false,
bool headless = false,
}) async {
chrome = await Chrome.startWithDebugPort(
[_googleUrl],
debugPort: port,
userDataDir: userDataDir,
signIn: signIn,
headless: headless,
);
}
Future<void> launchChrome({bool headless = false}) async {
await Chrome.start([_googleUrl], args: [if (headless) '--headless']);
}
final headlessModes = [
true,
if (!headlessOnlyEnvironment) false,
];
for (var headless in headlessModes) {
group('(headless: $headless)', () {
group('chrome with temp data dir', () {
tearDown(() async {
await chrome?.close();
chrome = null;
});
test('can launch chrome', () async {
await launchChrome(headless: headless);
expect(chrome, isNull);
});
test('can launch chrome with debug port', () async {
await launchChromeWithDebugPort(headless: headless);
expect(chrome, isNotNull);
});
test('has a working debugger', () async {
await launchChromeWithDebugPort(headless: headless);
final tabs = await getTabs();
expect(
tabs,
contains(
const TypeMatcher<ChromeTab>()
.having((t) => t.url, 'url', _googleUrl),
),
);
});
test('uses open debug port if provided port is 0', () async {
await launchChromeWithDebugPort(headless: headless);
expect(chrome!.debugPort, isNot(equals(0)));
});
test('can provide a specific debug port', () async {
final port = await findUnusedPort();
await launchChromeWithDebugPort(port: port, headless: headless);
expect(chrome!.debugPort, port);
});
});
group('chrome with user data dir', () {
late Directory dataDir;
const waitMilliseconds = Duration(milliseconds: 100);
for (var signIn in [false, true]) {
group('and signIn = $signIn', () {
setUp(() {
dataDir = Directory.systemTemp.createTempSync(_userDataDirName);
});
tearDown(() async {
await chrome?.close();
chrome = null;
var attempts = 0;
while (true) {
try {
attempts++;
await Future<dynamic>.delayed(waitMilliseconds);
dataDir.deleteSync(recursive: true);
break;
} catch (_) {
if (attempts > 3) rethrow;
}
}
});
test('can launch with debug port', () async {
await launchChromeWithDebugPort(
userDataDir: dataDir.path,
signIn: signIn,
headless: headless,
);
expect(chrome, isNotNull);
});
test('has a working debugger', () async {
await launchChromeWithDebugPort(
userDataDir: dataDir.path,
signIn: signIn,
headless: headless,
);
final tabs = await getTabs();
expect(
tabs,
contains(
const TypeMatcher<ChromeTab>()
.having((t) => t.url, 'url', _googleUrl),
),
);
});
test(
'has correct profile path',
() async {
await launchChromeWithDebugPort(
userDataDir: dataDir.path,
signIn: signIn,
headless: headless,
);
await openTab(_chromeVersionUrl);
final wipConnection = await connectToTab(_chromeVersionUrl);
await wipConnection.debugger.enable();
await wipConnection.runtime.enable();
final result = await _evaluate(
wipConnection.page,
"document.getElementById('profile_path').textContent",
);
expect(result, contains(_userDataDirName));
},
// Note: When re-enabling, skip for headless mode because headless
// mode does not allow chrome: urls.
skip: 'https://github.com/dart-lang/sdk/issues/52357',
);
});
}
});
});
}
}
String _openTabUrl(String url) => '/json/new?$url';
Future<String?> _evaluate(WipPage page, String expression) async {
String? result;
const stopInSeconds = Duration(seconds: 5);
const waitMilliseconds = Duration(milliseconds: 100);
final stopTime = DateTime.now().add(stopInSeconds);
while (result == null && DateTime.now().isBefore(stopTime)) {
await Future<dynamic>.delayed(waitMilliseconds);
try {
final wipResponse = await page.sendCommand(
'Runtime.evaluate',
params: {'expression': expression},
);
final response = wipResponse.json['result'] as Map<String, dynamic>;
final value = (response['result'] as Map<String, dynamic>)['value'];
result = value?.toString();
} catch (_) {
return null;
}
}
return result;
}
const _googleUrl = 'https://www.google.com/';
const _chromeVersionUrl = 'chrome://version/';
const _userDataDirName = 'data dir';