Allow signing into default profile and enabling extensions (#29)
* Allow signing into default profile and enabling extensions
- Add `signIn` argument to `startWithDebugPort` API.
- Add tests.
Related: https://github.com/dart-lang/webdev/issues/1490
* Addressed CR comments, fixed test failure
* Addressed more CR comments
* Addressed CR comments, fixed test flake
* Create tem dir for eacch test, better flake fix
* Make closing chrome on each test explicit
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66736db..8a339c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
-## 1.0.1-dev
+## 1.1.0-dev
+
+- Add optional `signIn` argument to `startWithDebugPort`.
+ To be used together with `user-data-dir` to start a chrome
+ window signed into the default profile with extensions enabled.
## 1.0.0
diff --git a/lib/src/chrome.dart b/lib/src/chrome.dart
index 1391884..ed6673e 100644
--- a/lib/src/chrome.dart
+++ b/lib/src/chrome.dart
@@ -65,14 +65,25 @@
/// Starts Chrome with the given arguments and a specific port.
///
/// Each url in [urls] will be loaded in a separate tab.
+ ///
+ /// If [userDataDir] is `null`, a new temp directory will be
+ /// passed to chrome as a user data directory. Chrome will
+ /// start without sign in and with extensions disabled.
+ ///
+ /// If [userDataDir] is not `null`, it will be passed to chrome
+ /// as a user data directory. Chrome will start signed into
+ /// the default profile with extensions enabled if [signIn]
+ /// is also true.
static Future<Chrome> startWithDebugPort(
List<String> urls, {
int debugPort = 0,
bool headless = false,
String? userDataDir,
+ bool signIn = false,
}) async {
Directory dataDir;
if (userDataDir == null) {
+ signIn = false;
dataDir = Directory.systemTemp.createTempSync();
} else {
dataDir = Directory(userDataDir);
@@ -87,9 +98,9 @@
'--disable-background-timer-throttling',
// Since we are using a temp profile, disable features that slow the
// Chrome launch.
- '--disable-extensions',
+ if (!signIn) '--disable-extensions',
'--disable-popup-blocking',
- '--bwsi',
+ if (!signIn) '--bwsi',
'--no-first-run',
'--no-default-browser-check',
'--disable-default-apps',
diff --git a/pubspec.yaml b/pubspec.yaml
index 082db41..d2eb157 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
name: browser_launcher
description: Provides a standardized way to launch web browsers for testing and tools.
-version: 1.0.1-dev
+version: 1.1.0-dev
homepage: https://github.com/dart-lang/browser_launcher
diff --git a/test/chrome_test.dart b/test/chrome_test.dart
index 0263225..0c098a2 100644
--- a/test/chrome_test.dart
+++ b/test/chrome_test.dart
@@ -4,6 +4,7 @@
@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:test/test.dart';
@@ -12,48 +13,137 @@
void main() {
Chrome? chrome;
- Future<void> launchChromeWithDebugPort({int port = 0}) async {
- chrome = await Chrome.startWithDebugPort([_googleUrl], debugPort: port);
+ Future<WipConnection> connectToTab(String url) async {
+ var tab = await chrome!.chromeConnection.getTab((t) => t.url.contains(url));
+ expect(tab, isNotNull);
+ return tab!.connect();
+ }
+
+ Future<void> openTab(String url) async {
+ await chrome!.chromeConnection.getUrl(_openTabUrl(url));
+ }
+
+ Future<void> launchChromeWithDebugPort(
+ {int port = 0, String? userDataDir, bool signIn = false}) async {
+ chrome = await Chrome.startWithDebugPort([_googleUrl],
+ debugPort: port, userDataDir: userDataDir, signIn: signIn);
}
Future<void> launchChrome() async {
await Chrome.start([_googleUrl]);
}
- tearDown(() async {
- await chrome?.close();
- chrome = null;
+ group('chrome with temp data dir', () {
+ tearDown(() async {
+ await chrome?.close();
+ chrome = null;
+ });
+
+ test('can launch chrome', () async {
+ await launchChrome();
+ expect(chrome, isNull);
+ });
+
+ test('can launch chrome with debug port', () async {
+ await launchChromeWithDebugPort();
+ expect(chrome, isNotNull);
+ });
+
+ test('has a working debugger', () async {
+ await launchChromeWithDebugPort();
+ var tabs = await chrome!.chromeConnection.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(port: 0);
+ expect(chrome!.debugPort, isNot(equals(0)));
+ });
+
+ test('can provide a specific debug port', () async {
+ var port = await findUnusedPort();
+ await launchChromeWithDebugPort(port: port);
+ expect(chrome!.debugPort, port);
+ });
});
- test('can launch chrome', () async {
- await launchChrome();
- expect(chrome, isNull);
- });
+ group('chrome with user data dir', () {
+ late Directory dataDir;
- test('can launch chrome with debug port', () async {
- await launchChromeWithDebugPort();
- expect(chrome, isNotNull);
- });
+ for (var signIn in [false, true]) {
+ group('and signIn = $signIn', () {
+ setUp(() {
+ dataDir = Directory.systemTemp.createTempSync(_userDataDirName);
+ });
- test('debugger is working', () async {
- await launchChromeWithDebugPort();
- var tabs = await chrome!.chromeConnection.getTabs();
- expect(
- tabs,
- contains(const TypeMatcher<ChromeTab>()
- .having((t) => t.url, 'url', _googleUrl)));
- });
+ tearDown(() async {
+ await chrome?.close();
+ chrome = null;
- test('uses open debug port if provided port is 0', () async {
- await launchChromeWithDebugPort(port: 0);
- expect(chrome!.debugPort, isNot(equals(0)));
- });
+ var attempts = 0;
+ while (true) {
+ try {
+ attempts++;
+ await Future.delayed(const Duration(milliseconds: 100));
+ dataDir.deleteSync(recursive: true);
+ break;
+ } catch (_) {
+ if (attempts > 3) rethrow;
+ }
+ }
+ });
- test('can provide a specific debug port', () async {
- var port = await findUnusedPort();
- await launchChromeWithDebugPort(port: port);
- expect(chrome!.debugPort, port);
+ test('can launch with debug port', () async {
+ await launchChromeWithDebugPort(
+ userDataDir: dataDir.path, signIn: signIn);
+ expect(chrome, isNotNull);
+ });
+
+ test('has a working debugger', () async {
+ await launchChromeWithDebugPort(
+ userDataDir: dataDir.path, signIn: signIn);
+ var tabs = await chrome!.chromeConnection.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);
+ await openTab(_chromeVersionUrl);
+
+ var wipConnection = await connectToTab(_chromeVersionUrl);
+ var result = await _evaluateExpression(wipConnection.page,
+ "document.getElementById('profile_path').textContent");
+
+ expect(result, contains(_userDataDirName));
+ });
+ });
+ }
});
}
+String _openTabUrl(String url) => '/json/new?$url';
+
+Future<String> _evaluateExpression(WipPage page, String expression) async {
+ var result = '';
+ while (result.isEmpty) {
+ await Future.delayed(Duration(milliseconds: 100));
+ var wipResponse = await page.sendCommand(
+ 'Runtime.evaluate',
+ params: {'expression': expression},
+ );
+ var value = wipResponse.json['result']['result']['value'];
+ result = (value != null && value is String) ? value : '';
+ }
+ return result;
+}
+
const _googleUrl = 'https://www.google.com/';
+const _chromeVersionUrl = 'chrome://version/';
+const _userDataDirName = 'data dir';