From 9877c0271396fe79c4bb1054b4ed12734d5b0e73 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Thu, 20 Jan 2022 11:56:03 -0800 Subject: [PATCH 1/6] 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 --- CHANGELOG.md | 4 +++ lib/src/chrome.dart | 15 +++++++-- test/chrome_test.dart | 78 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66736db..230e51b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 1.0.1-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 - Migrate to null-safety. diff --git a/lib/src/chrome.dart b/lib/src/chrome.dart index 1391884..0182522 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/test/chrome_test.dart b/test/chrome_test.dart index 0263225..1f8b3d7 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,8 +13,20 @@ 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 { @@ -54,6 +67,67 @@ void main() { 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', () { + setUpAll(() { + dataDir = Directory.systemTemp.createTempSync(_userDataDirName); + }); + + tearDownAll(() { + dataDir.deleteSync(recursive: true); + }); + + 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}, + ); + result = wipResponse.json['result']['result']['value'] as String; + } + return result; } const _googleUrl = 'https://www.google.com/'; +const _chromeVersionUrl = 'chrome://version/'; +const _userDataDirName = 'data dir'; From a6699afd6ec4be44510a200a012158ae3eb94c95 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 21 Jan 2022 10:32:52 -0800 Subject: [PATCH 2/6] Addressed CR comments, fixed test failure --- CHANGELOG.md | 6 +++--- pubspec.yaml | 2 +- test/chrome_test.dart | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 230e51b..8a339c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +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. + 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/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 1f8b3d7..2bd5650 100644 --- a/test/chrome_test.dart +++ b/test/chrome_test.dart @@ -123,7 +123,8 @@ Future _evaluateExpression(WipPage page, String expression) async { 'Runtime.evaluate', params: {'expression': expression}, ); - result = wipResponse.json['result']['result']['value'] as String; + var value = wipResponse.json['result']['result']['value']; + result = (value != null && value is String) ? value : ''; } return result; } From 57664371902106a2a5ddcbd9eb41a367b7b7cb2c Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 21 Jan 2022 10:37:45 -0800 Subject: [PATCH 3/6] Addressed more CR comments --- lib/src/chrome.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/chrome.dart b/lib/src/chrome.dart index 0182522..ed6673e 100644 --- a/lib/src/chrome.dart +++ b/lib/src/chrome.dart @@ -66,11 +66,11 @@ class Chrome { /// /// Each url in [urls] will be loaded in a separate tab. /// - /// If [userDataDir] is null, a new temp directory will be + /// 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 + /// 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. From f17d79d3c18bd36e058d96b2fd0763cff4dbe974 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 21 Jan 2022 10:42:56 -0800 Subject: [PATCH 4/6] Addressed CR comments, fixed test flake --- test/chrome_test.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/chrome_test.dart b/test/chrome_test.dart index 2bd5650..a966db1 100644 --- a/test/chrome_test.dart +++ b/test/chrome_test.dart @@ -78,7 +78,14 @@ void main() { }); tearDownAll(() { - dataDir.deleteSync(recursive: true); + var deleted = false; + var attempts = 0; + while (!deleted && attempts++ < 3) { + try { + dataDir.deleteSync(recursive: true); + deleted = true; + } catch (_) {} + } }); test('can launch with debug port', () async { From 0739b4eb14d167f2d4a79ceb7f6ba5bf6bba64a5 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 21 Jan 2022 10:56:14 -0800 Subject: [PATCH 5/6] Create tem dir for eacch test, better flake fix --- test/chrome_test.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/chrome_test.dart b/test/chrome_test.dart index a966db1..2a5a1d0 100644 --- a/test/chrome_test.dart +++ b/test/chrome_test.dart @@ -73,18 +73,21 @@ void main() { for (var signIn in [false, true]) { group('and signIn = $signIn', () { - setUpAll(() { + setUp(() { dataDir = Directory.systemTemp.createTempSync(_userDataDirName); }); - tearDownAll(() { - var deleted = false; + tearDown(() async { var attempts = 0; - while (!deleted && attempts++ < 3) { + while (true) { try { + attempts++; + await Future.delayed(const Duration(milliseconds: 100)); dataDir.deleteSync(recursive: true); - deleted = true; - } catch (_) {} + break; + } catch (_) { + if (attempts > 3) rethrow; + } } }); From b738c8b74e33d8dc1fee47b850a876172d84e044 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 21 Jan 2022 11:10:58 -0800 Subject: [PATCH 6/6] Make closing chrome on each test explicit --- test/chrome_test.dart | 71 +++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/test/chrome_test.dart b/test/chrome_test.dart index 2a5a1d0..0c098a2 100644 --- a/test/chrome_test.dart +++ b/test/chrome_test.dart @@ -33,39 +33,41 @@ void main() { await Chrome.start([_googleUrl]); } - 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('debugger is working', () 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('can provide a specific debug port', () async { - var port = await findUnusedPort(); - await launchChromeWithDebugPort(port: port); - expect(chrome!.debugPort, port); + 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() + .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); + }); }); group('chrome with user data dir', () { @@ -78,6 +80,9 @@ void main() { }); tearDown(() async { + await chrome?.close(); + chrome = null; + var attempts = 0; while (true) { try {