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 @@ class Chrome { /// 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 startWithDebugPort( List 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 @@ class Chrome { '--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 @@ import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; void main() { Chrome? chrome; - Future launchChromeWithDebugPort({int port = 0}) async { - chrome = await Chrome.startWithDebugPort([_googleUrl], debugPort: port); + Future connectToTab(String url) async { + var tab = await chrome!.chromeConnection.getTab((t) => t.url.contains(url)); + expect(tab, isNotNull); + return tab!.connect(); + } + + Future openTab(String url) async { + await chrome!.chromeConnection.getUrl(_openTabUrl(url)); + } + + Future launchChromeWithDebugPort( + {int port = 0, String? userDataDir, bool signIn = false}) async { + chrome = await Chrome.startWithDebugPort([_googleUrl], + debugPort: port, userDataDir: userDataDir, signIn: signIn); } Future 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', () async { + await launchChrome(); + expect(chrome, isNull); + }); - test('can launch chrome with debug port', () async { - await launchChromeWithDebugPort(); - expect(chrome, isNotNull); - }); + test('can launch chrome with debug port', () async { + await launchChromeWithDebugPort(); + expect(chrome, isNotNull); + }); - test('debugger is working', () async { - await launchChromeWithDebugPort(); - var tabs = await chrome!.chromeConnection.getTabs(); - expect( - tabs, - contains(const TypeMatcher() - .having((t) => t.url, 'url', _googleUrl))); - }); + test('has a working debugger', () async { + await launchChromeWithDebugPort(); + var tabs = await chrome!.chromeConnection.getTabs(); + expect( + tabs, + contains(const TypeMatcher() + .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('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 provide a specific debug port', () async { - var port = await findUnusedPort(); - await launchChromeWithDebugPort(port: port); - expect(chrome!.debugPort, port); + group('chrome with user data dir', () { + late Directory dataDir; + + 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.delayed(const Duration(milliseconds: 100)); + dataDir.deleteSync(recursive: true); + break; + } catch (_) { + if (attempts > 3) rethrow; + } + } + }); + + 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() + .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 _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';