browser-commander 0.32.1 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,139 @@
1
+ import assert from 'node:assert/strict';
2
+ import { chmod, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import path from 'node:path';
5
+ import { after, describe, it } from 'node:test';
6
+ import {
7
+ cacheLocations,
8
+ cachedChromeForTesting,
9
+ chromeForTestingCandidates,
10
+ } from '../../../src/browser/chrome-for-testing-cache.js';
11
+ import { isChromeForTesting } from '../../../src/browser/launch-executable.js';
12
+
13
+ const home = await mkdtemp(path.join(tmpdir(), 'browser-commander-cache-'));
14
+ after(() => rm(home, { recursive: true, force: true }));
15
+
16
+ async function install(...segments) {
17
+ const file = path.join(home, ...segments);
18
+ await mkdir(path.dirname(file), { recursive: true });
19
+ await writeFile(file, '#!/bin/sh\n');
20
+ await chmod(file, 0o755);
21
+ return file;
22
+ }
23
+
24
+ const linux = (env = {}) =>
25
+ cacheLocations({ platform: 'linux', arch: 'x64', home, env });
26
+
27
+ describe('Chrome for Testing in the download caches (issue #179)', () => {
28
+ it('lists the Playwright builds newest revision first', async () => {
29
+ const old = await install(
30
+ '.cache/ms-playwright/chromium-999/chrome-linux64/chrome'
31
+ );
32
+ const recent = await install(
33
+ '.cache/ms-playwright/chromium-1248/chrome-linux64/chrome'
34
+ );
35
+ await mkdir(
36
+ path.join(home, '.cache/ms-playwright/chromium_headless_shell-1300'),
37
+ {
38
+ recursive: true,
39
+ }
40
+ );
41
+ assert.deepEqual(chromeForTestingCandidates('playwright', linux()), [
42
+ recent,
43
+ old,
44
+ ]);
45
+ });
46
+
47
+ it('follows PLAYWRIGHT_BROWSERS_PATH and XDG_CACHE_HOME', async () => {
48
+ const moved = await install('moved/chromium-1300/chrome-linux64/chrome');
49
+ assert.deepEqual(
50
+ chromeForTestingCandidates(
51
+ 'playwright',
52
+ linux({ PLAYWRIGHT_BROWSERS_PATH: path.join(home, 'moved') })
53
+ ),
54
+ [moved]
55
+ );
56
+ assert.deepEqual(
57
+ chromeForTestingCandidates(
58
+ 'playwright',
59
+ linux({ PLAYWRIGHT_BROWSERS_PATH: '0' })
60
+ ),
61
+ []
62
+ );
63
+ const xdg = await install(
64
+ 'xdg/ms-playwright/chromium-1/chrome-linux64/chrome'
65
+ );
66
+ assert.deepEqual(
67
+ chromeForTestingCandidates(
68
+ 'playwright',
69
+ linux({ XDG_CACHE_HOME: path.join(home, 'xdg') })
70
+ ),
71
+ [xdg]
72
+ );
73
+ });
74
+
75
+ it('compares Puppeteer and Selenium versions numerically', async () => {
76
+ const newer = await install(
77
+ '.cache/puppeteer/chrome/linux-131.0.6778.204/chrome-linux64/chrome'
78
+ );
79
+ const older = await install(
80
+ '.cache/puppeteer/chrome/linux-99.0.1.1/chrome-linux64/chrome'
81
+ );
82
+ assert.deepEqual(chromeForTestingCandidates('puppeteer', linux()), [
83
+ newer,
84
+ older,
85
+ ]);
86
+ const selenium = await install(
87
+ '.cache/selenium/chrome/linux64/131.0.6778.85/chrome'
88
+ );
89
+ assert.deepEqual(chromeForTestingCandidates('selenium', linux()), [
90
+ selenium,
91
+ ]);
92
+ for (const file of [newer, older, selenium]) {
93
+ assert.ok(isChromeForTesting(file), file);
94
+ }
95
+ });
96
+
97
+ it('searches the caches each engine uses, in order', () => {
98
+ const machine = { platform: 'linux', arch: 'x64', home, env: {} };
99
+ const cacheOf = (file) =>
100
+ ['ms-playwright', 'puppeteer', 'selenium'].find((cache) =>
101
+ file.split(path.sep).includes(cache)
102
+ );
103
+ const caches = (engine) => [
104
+ ...new Set(cachedChromeForTesting(engine, machine).map(cacheOf)),
105
+ ];
106
+ assert.deepEqual(caches('playwright'), ['ms-playwright']);
107
+ assert.deepEqual(caches('puppeteer'), ['puppeteer']);
108
+ assert.deepEqual(caches('selenium'), [
109
+ 'ms-playwright',
110
+ 'puppeteer',
111
+ 'selenium',
112
+ ]);
113
+ });
114
+
115
+ it('knows the macOS and Windows layouts and skips other platforms', () => {
116
+ assert.equal(
117
+ cacheLocations({ platform: 'darwin', arch: 'arm64', home, env: {} }).name,
118
+ 'mac-arm64'
119
+ );
120
+ assert.equal(
121
+ cacheLocations({ platform: 'win32', arch: 'ia32', home, env: {} })
122
+ .puppeteer,
123
+ 'win32'
124
+ );
125
+ assert.equal(
126
+ cacheLocations({ platform: 'linux', arch: 'arm64', home, env: {} }),
127
+ undefined
128
+ );
129
+ assert.deepEqual(
130
+ cachedChromeForTesting('playwright', {
131
+ platform: 'linux',
132
+ arch: 'arm64',
133
+ home,
134
+ env: {},
135
+ }),
136
+ []
137
+ );
138
+ });
139
+ });
@@ -0,0 +1,93 @@
1
+ // feature-parity: launch.chrome-for-testing@native-typed
2
+ import { describe, it } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+
5
+ import {
6
+ CHROME_FOR_TESTING_RESTRICTIONS,
7
+ defaultRestrictionsFor,
8
+ isChromeForTesting,
9
+ } from '../../../src/browser/launch-executable.js';
10
+ import { resolveRestrictions } from '../../../src/browser/restrictions.js';
11
+ import * as api from '../../../src/index.js';
12
+
13
+ describe('isChromeForTesting (issue #179)', () => {
14
+ it('recognises the Chrome for Testing install layouts', () => {
15
+ for (const executable of [
16
+ // Playwright
17
+ '/home/u/.cache/ms-playwright/chromium-1248/chrome-linux64/chrome',
18
+ '/Users/u/Library/Caches/ms-playwright/chromium-1248/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
19
+ 'C:\\Users\\u\\AppData\\Local\\ms-playwright\\chromium-1248\\chrome-win64\\chrome.exe',
20
+ // Puppeteer and @puppeteer/browsers
21
+ '/home/u/.cache/puppeteer/chrome/linux-131.0.6778.85/chrome-linux64/chrome',
22
+ '/Users/u/.cache/puppeteer/chrome/mac_arm-131.0.6778.85/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
23
+ 'C:\\Users\\u\\.cache\\puppeteer\\chrome\\win32-131.0.6778.85\\chrome-win32\\chrome.exe',
24
+ '/Users/u/Library/Caches/ms-playwright/chromium-1248/chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
25
+ // Selenium Manager
26
+ '/home/u/.cache/selenium/chrome/linux64/131.0.6778.85/chrome',
27
+ 'C:\\Users\\u\\.cache\\selenium\\chrome\\win64\\131.0.6778.85\\chrome.exe',
28
+ // An app copied anywhere keeps its name
29
+ '/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
30
+ ]) {
31
+ assert.equal(isChromeForTesting(executable), true, executable);
32
+ }
33
+ });
34
+
35
+ it('leaves every other browser alone', () => {
36
+ for (const executable of [
37
+ undefined,
38
+ '',
39
+ '/usr/bin/google-chrome',
40
+ '/opt/google/chrome/chrome',
41
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
42
+ 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
43
+ '/usr/bin/chromium',
44
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
45
+ // Playwright's own Chromium builds before Chrome for Testing
46
+ '/home/u/.cache/ms-playwright/chromium-1100/chrome-linux/chrome',
47
+ '/Users/u/Library/Caches/ms-playwright/chromium-1100/chrome-mac/Chromium.app/Contents/MacOS/Chromium',
48
+ // Selenium Manager's Firefox cache
49
+ '/home/u/.cache/selenium/firefox/linux64/133.0/firefox',
50
+ ]) {
51
+ assert.equal(isChromeForTesting(executable), false, String(executable));
52
+ }
53
+ });
54
+ });
55
+
56
+ describe('defaultRestrictionsFor (issue #179)', () => {
57
+ const cft =
58
+ '/home/u/.cache/ms-playwright/chromium-1248/chrome-linux64/chrome';
59
+
60
+ it('gives Chrome for Testing the chrome-for-testing preset', () => {
61
+ assert.deepEqual(defaultRestrictionsFor(cft), [
62
+ 'no-chrome-for-testing-infobar',
63
+ 'mock-keychain',
64
+ ]);
65
+ assert.deepEqual(defaultRestrictionsFor('/usr/bin/google-chrome'), []);
66
+ });
67
+
68
+ it("uses the caller's defaultRestrictions as given", () => {
69
+ assert.deepEqual(defaultRestrictionsFor(cft, []), []);
70
+ assert.deepEqual(
71
+ defaultRestrictionsFor('/usr/bin/google-chrome', ['mock-keychain']),
72
+ ['mock-keychain']
73
+ );
74
+ // The preset itself is never handed out to be mutated.
75
+ defaultRestrictionsFor(cft).push('no-sync');
76
+ assert.deepEqual(CHROME_FOR_TESTING_RESTRICTIONS, [
77
+ 'no-chrome-for-testing-infobar',
78
+ 'mock-keychain',
79
+ ]);
80
+ });
81
+
82
+ it('hides the infobar with --test-type=gpu and skips the keychain prompt', () => {
83
+ assert.deepEqual(resolveRestrictions(['chrome-for-testing']).args, [
84
+ '--test-type=gpu',
85
+ '--use-mock-keychain',
86
+ ]);
87
+ assert.deepEqual(api.LAUNCH_RESTRICTION_PRESETS['chrome-for-testing'], [
88
+ 'no-chrome-for-testing-infobar',
89
+ 'mock-keychain',
90
+ ]);
91
+ assert.equal(api.isChromeForTesting, isChromeForTesting);
92
+ });
93
+ });
@@ -1,6 +1,8 @@
1
- import { describe, it } from 'node:test';
1
+ import { after, describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { access } from 'node:fs/promises';
3
+ import { access, chmod, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import path from 'node:path';
4
6
 
5
7
  import {
6
8
  launchBrowser,
@@ -52,21 +54,59 @@ function fakeRealLauncher(calls, { engine = 'playwright' } = {}) {
52
54
 
53
55
  const systemChrome = async () => '/usr/bin/google-chrome';
54
56
 
57
+ // An executable laid out like the Chrome for Testing Playwright downloads.
58
+ const cftRoot = await mkdtemp(path.join(tmpdir(), 'browser-commander-cft-'));
59
+ const cftChrome = path.join(
60
+ cftRoot,
61
+ 'chromium-1248',
62
+ 'chrome-linux64',
63
+ 'chrome'
64
+ );
65
+ await mkdir(path.dirname(cftChrome), { recursive: true });
66
+ await writeFile(cftChrome, '#!/bin/sh\n');
67
+ await chmod(cftChrome, 0o755);
68
+ after(() => rm(cftRoot, { recursive: true, force: true }));
69
+ const cftEngine = async () => ({ executablePath: () => cftChrome });
70
+ const missingEngine = async () => ({ executablePath: () => '/nonexistent' });
71
+ const noCachedBrowsers = () => [];
72
+ const noSystemChrome = async () => {
73
+ throw new Error('the installed Google Chrome must not be resolved');
74
+ };
75
+
76
+ // A real launch with recorded calls: the result and launchRealBrowser's options.
77
+ async function realLaunch(options, dependencies, calls = []) {
78
+ const result = await launchBrowserWithDependencies(options, {
79
+ launchRealBrowser: fakeRealLauncher(calls),
80
+ settleMs: 0,
81
+ ...dependencies,
82
+ });
83
+ const [, launched] = calls.find(([name]) => name === 'launchRealBrowser');
84
+ return [result, launched];
85
+ }
86
+
55
87
  describe('launchBrowser', () => {
56
- it('starts the real browser by default, with nothing added (issue #103)', async () => {
88
+ it('starts Chrome for Testing by default, never the installed Google Chrome (issue #179)', async () => {
89
+ const [, options] = await realLaunch(
90
+ {},
91
+ { resolveSystem: noSystemChrome, loadEngineModule: cftEngine }
92
+ );
93
+ assert.equal(options.executablePath, cftChrome);
94
+ // No channel is invented: the launch is not Google Chrome's.
95
+ assert.equal('channel' in options, false);
96
+ // The Chrome for Testing defaults are added by launchRealBrowser.
97
+ assert.deepEqual(options.restrictions, []);
98
+ assert.equal('defaultRestrictions' in options, false);
99
+ });
100
+
101
+ it('starts the requested Google Chrome with nothing added (issue #103)', async () => {
57
102
  const calls = [];
58
103
  const googleBefore = process.env.GOOGLE_API_KEY;
59
104
 
60
- const result = await launchBrowserWithDependencies(
61
- {},
62
- {
63
- launchRealBrowser: fakeRealLauncher(calls),
64
- resolveSystem: systemChrome,
65
- settleMs: 0,
66
- }
105
+ const [result, options] = await realLaunch(
106
+ { channel: 'chrome' },
107
+ { resolveSystem: systemChrome },
108
+ calls
67
109
  );
68
-
69
- const [, options] = calls.find(([name]) => name === 'launchRealBrowser');
70
110
  assert.equal(options.engine, 'playwright');
71
111
  assert.equal(options.executablePath, '/usr/bin/google-chrome');
72
112
  assert.equal('slowMo' in options, false);
@@ -91,7 +131,7 @@ describe('launchBrowser', () => {
91
131
  { engine: 'puppeteer', restrictions: ['no-sync'], slowMo: 20 },
92
132
  {
93
133
  launchRealBrowser: fakeRealLauncher(calls, { engine: 'puppeteer' }),
94
- resolveSystem: systemChrome,
134
+ loadEngineModule: cftEngine,
95
135
  settleMs: 0,
96
136
  }
97
137
  );
@@ -109,7 +149,7 @@ describe('launchBrowser', () => {
109
149
  { colorScheme: 'dark' },
110
150
  {
111
151
  launchRealBrowser: fakeRealLauncher(calls),
112
- resolveSystem: systemChrome,
152
+ loadEngineModule: cftEngine,
113
153
  settleMs: 0,
114
154
  }
115
155
  );
@@ -128,7 +168,7 @@ describe('launchBrowser', () => {
128
168
  { fingerprint: { timezone: 'Not/AZone' } },
129
169
  {
130
170
  launchRealBrowser: fakeRealLauncher(calls),
131
- resolveSystem: systemChrome,
171
+ loadEngineModule: cftEngine,
132
172
  settleMs: 0,
133
173
  }
134
174
  ),
@@ -150,6 +190,10 @@ describe('launchBrowser', () => {
150
190
  () => launchBrowser({ restrictions: ['no-such-restriction'] }),
151
191
  /Unknown launch restriction "no-such-restriction"/
152
192
  );
193
+ await assert.rejects(
194
+ () => launchBrowser({ defaultRestrictions: ['no-such-default'] }),
195
+ /Unknown launch restriction "no-such-default"/
196
+ );
153
197
  });
154
198
 
155
199
  describe("launch: 'engine'", () => {
@@ -233,6 +277,100 @@ describe('launchBrowser', () => {
233
277
  assert.deepEqual(calls.at(-1), ['browser.close']);
234
278
  });
235
279
 
280
+ it('adds the Chrome for Testing defaults to its engine launch (issue #179)', async () => {
281
+ const launchWith = async (options, executablePath) => {
282
+ const calls = [];
283
+ const engine = {
284
+ ...fakePlaywright(calls),
285
+ executablePath: () => executablePath,
286
+ };
287
+ const result = await launchBrowserWithDependencies(
288
+ { launch: 'engine', ...options },
289
+ { loadEngineModule: async () => engine, settleMs: 0 }
290
+ );
291
+ await result.close();
292
+ return { args: calls[0][2].args, defaults: result.defaultRestrictions };
293
+ };
294
+
295
+ const cft = await launchWith({}, cftChrome);
296
+ assert.deepEqual(cft.defaults, [
297
+ 'no-chrome-for-testing-infobar',
298
+ 'mock-keychain',
299
+ ]);
300
+ assert.ok(cft.args.includes('--test-type=gpu'));
301
+ assert.ok(cft.args.includes('--use-mock-keychain'));
302
+
303
+ for (const [options, executablePath] of [
304
+ [{}, '/home/u/.cache/ms-playwright/chromium-1100/chrome-linux/chrome'],
305
+ [{ channel: 'chrome' }, cftChrome],
306
+ [{ defaultRestrictions: [] }, cftChrome],
307
+ ]) {
308
+ const other = await launchWith(options, executablePath);
309
+ assert.deepEqual(other.defaults, [], JSON.stringify(options));
310
+ assert.equal(other.args.includes('--test-type=gpu'), false);
311
+ assert.equal(other.args.includes('--use-mock-keychain'), false);
312
+ }
313
+ });
314
+
315
+ it("points a Selenium launch at the engines' Chrome for Testing (issue #179)", async () => {
316
+ const launches = [];
317
+ const launchWebDriver = async (options) => {
318
+ launches.push(options);
319
+ return { driver: {}, page: fakePage(), close: async () => {} };
320
+ };
321
+ await launchBrowserWithDependencies(
322
+ {
323
+ engine: 'selenium',
324
+ launch: 'engine',
325
+ restrictions: ['no-sync'],
326
+ defaultRestrictions: ['mock-keychain'],
327
+ },
328
+ { launchWebDriver, loadEngineModule: cftEngine, settleMs: 0 }
329
+ );
330
+ assert.equal(launches[0].executablePath, cftChrome);
331
+ // launchWebDriver adds the defaults for the browser it resolves.
332
+ assert.deepEqual(launches[0].restrictions, ['no-sync']);
333
+ assert.deepEqual(launches[0].defaultRestrictions, ['mock-keychain']);
334
+
335
+ // An installed browser asked for by channel is left to Selenium.
336
+ await launchBrowserWithDependencies(
337
+ { engine: 'selenium', launch: 'engine', channel: 'chrome' },
338
+ { launchWebDriver, loadEngineModule: cftEngine, settleMs: 0 }
339
+ );
340
+ assert.equal(launches[1].executablePath, undefined);
341
+
342
+ // Selenium Manager's Chrome for Testing comes last...
343
+ const seleniumCft = process.execPath;
344
+ await launchBrowserWithDependencies(
345
+ { engine: 'selenium', launch: 'engine' },
346
+ {
347
+ launchWebDriver,
348
+ loadEngineModule: missingEngine,
349
+ cachedBrowsers: noCachedBrowsers,
350
+ seleniumManagerBrowser: async () => seleniumCft,
351
+ settleMs: 0,
352
+ }
353
+ );
354
+ assert.equal(launches[2].executablePath, seleniumCft);
355
+
356
+ // ...and without any the launch fails instead of letting the driver
357
+ // pick the installed Google Chrome.
358
+ await assert.rejects(
359
+ launchBrowserWithDependencies(
360
+ { engine: 'selenium', launch: 'engine' },
361
+ {
362
+ launchWebDriver,
363
+ loadEngineModule: missingEngine,
364
+ cachedBrowsers: noCachedBrowsers,
365
+ seleniumManagerBrowser: async () => undefined,
366
+ settleMs: 0,
367
+ }
368
+ ),
369
+ /Chrome for Testing is not installed for selenium/
370
+ );
371
+ assert.equal(launches.length, 3);
372
+ });
373
+
236
374
  it("leaves Ctrl+C to the caller with interruption: 'caller' (#174)", async () => {
237
375
  for (const [interruption, expected] of [
238
376
  [undefined, undefined],
@@ -279,34 +417,100 @@ describe('resolveLaunchExecutable', () => {
279
417
  ]);
280
418
  });
281
419
 
282
- it("falls back to the engine's downloaded Chromium without an installed Chrome", async () => {
283
- const notInstalled = async () => {
284
- throw new Error('Chrome is not installed');
285
- };
420
+ it("starts the engine's Chrome for Testing, never the installed Google Chrome", async () => {
286
421
  const executable = await resolveLaunchExecutable(
287
422
  { engine: 'playwright' },
288
- {
289
- resolveSystem: notInstalled,
290
- loadEngineModule: async () => ({
291
- executablePath: () => process.execPath,
292
- }),
293
- }
423
+ { resolveSystem: noSystemChrome, loadEngineModule: cftEngine }
294
424
  );
295
- assert.equal(executable, process.execPath);
425
+ assert.equal(executable, cftChrome);
426
+ });
296
427
 
428
+ it('says how to install Chrome for Testing or ask for Google Chrome without it', async () => {
429
+ const missing = async () => ({
430
+ executablePath: () => '/nonexistent/chrome',
431
+ });
297
432
  await assert.rejects(
298
433
  () =>
299
434
  resolveLaunchExecutable(
300
435
  { engine: 'playwright' },
301
436
  {
302
- resolveSystem: notInstalled,
303
- loadEngineModule: async () => ({
304
- executablePath: () => '/nonexistent/chrome',
305
- }),
437
+ resolveSystem: noSystemChrome,
438
+ loadEngineModule: missing,
439
+ cachedBrowsers: noCachedBrowsers,
440
+ }
441
+ ),
442
+ /Chrome for Testing is not installed for playwright: run `npx playwright install chromium`, or pass channel: 'chrome'/
443
+ );
444
+ await assert.rejects(
445
+ () =>
446
+ resolveLaunchExecutable(
447
+ { engine: 'selenium' },
448
+ {
449
+ resolveSystem: noSystemChrome,
450
+ loadEngineModule: async () => {
451
+ throw new Error('not installed');
452
+ },
453
+ seleniumManagerBrowser: async () => {
454
+ throw new Error('offline');
455
+ },
456
+ cachedBrowsers: noCachedBrowsers,
306
457
  }
307
458
  ),
308
- /Chrome is not installed/
459
+ /run `npx playwright install chromium` or `npx puppeteer browsers install chrome` or `selenium-manager --browser chrome --force-browser-download`/
460
+ );
461
+ });
462
+
463
+ it("falls back to Selenium Manager's Chrome for Testing for Selenium only", async () => {
464
+ const asked = [];
465
+ const seleniumManagerBrowser = async () => {
466
+ asked.push('selenium-manager');
467
+ return cftChrome;
468
+ };
469
+ assert.equal(
470
+ await resolveLaunchExecutable(
471
+ { engine: 'selenium' },
472
+ {
473
+ resolveSystem: noSystemChrome,
474
+ loadEngineModule: missingEngine,
475
+ seleniumManagerBrowser,
476
+ cachedBrowsers: noCachedBrowsers,
477
+ }
478
+ ),
479
+ cftChrome
480
+ );
481
+ await assert.rejects(
482
+ resolveLaunchExecutable(
483
+ { engine: 'playwright' },
484
+ {
485
+ resolveSystem: noSystemChrome,
486
+ loadEngineModule: missingEngine,
487
+ seleniumManagerBrowser,
488
+ cachedBrowsers: noCachedBrowsers,
489
+ }
490
+ ),
491
+ /Chrome for Testing is not installed for playwright/
492
+ );
493
+ assert.deepEqual(asked, ['selenium-manager']);
494
+ });
495
+
496
+ it("falls back to a cached build when the engine's own is not installed", async () => {
497
+ const asked = [];
498
+ const resolved = await resolveLaunchExecutable(
499
+ { engine: 'selenium' },
500
+ {
501
+ resolveSystem: noSystemChrome,
502
+ loadEngineModule: missingEngine,
503
+ cachedBrowsers: (engine) => {
504
+ asked.push(engine);
505
+ return ['/nonexistent/chrome-linux64/chrome', cftChrome];
506
+ },
507
+ seleniumManagerBrowser: async () => {
508
+ throw new Error('Selenium Manager is asked only without a cache');
509
+ },
510
+ }
309
511
  );
512
+ assert.equal(resolved, cftChrome);
513
+ assert.deepEqual(asked, ['selenium']);
310
514
  });
311
515
  });
312
516
 
@@ -264,6 +264,25 @@ describe('measureParity', () => {
264
264
  );
265
265
  });
266
266
 
267
+ it("accepts Chrome for Testing's default switches as requested (issue #179)", async () => {
268
+ const { session, dependencies } = fakes({
269
+ candidateCommandLine: `${CHROME} --user-data-dir=/tmp/c --remote-debugging-port=2 --test-type=gpu --use-mock-keychain about:blank`,
270
+ });
271
+ session.args = [
272
+ '--user-data-dir=/tmp/c',
273
+ '--remote-debugging-port=2',
274
+ '--test-type=gpu',
275
+ '--use-mock-keychain',
276
+ ];
277
+ session.defaultRestrictions = [
278
+ 'no-chrome-for-testing-infobar',
279
+ 'mock-keychain',
280
+ ];
281
+ const report = await measureParity({}, dependencies);
282
+ assert.equal(report.ok, true);
283
+ assert.deepEqual(report.unlisted, []);
284
+ });
285
+
267
286
  it('measures a provided session and leaves it open', async () => {
268
287
  const { calls, session, dependencies } = fakes({
269
288
  candidateCommandLine: `${CHROME} --user-data-dir=/tmp/c --remote-debugging-port=2 about:blank`,
@@ -516,6 +516,58 @@ describe('launchAndConnectRealBrowser', () => {
516
516
  await result.close();
517
517
  });
518
518
 
519
+ it('adds the Chrome for Testing defaults to its command line only (issue #179)', async () => {
520
+ const cft =
521
+ '/home/u/.cache/ms-playwright/chromium-1248/chrome-linux64/chrome';
522
+ const launch = async (executablePath, options = {}) => {
523
+ let spawnedArgs;
524
+ const result = await launchAndConnectRealBrowserWithDependencies(
525
+ { closeTimeout: 50, ...options },
526
+ {
527
+ resolveExecutable: async () => executablePath,
528
+ reservePort: async () => 40006,
529
+ spawnBrowser: (_executablePath, args) => {
530
+ spawnedArgs = args;
531
+ return fakeProcess([]);
532
+ },
533
+ waitForEndpoint: async () => 'http://127.0.0.1:40006',
534
+ connect: async () => ({ browser: null, page: null }),
535
+ }
536
+ );
537
+ await result.close();
538
+ return { args: spawnedArgs, defaults: result.defaultRestrictions };
539
+ };
540
+ const defaultSwitches = (args) =>
541
+ args.filter((arg) =>
542
+ ['--test-type=gpu', '--use-mock-keychain'].includes(arg)
543
+ );
544
+
545
+ const testing = await launch(cft);
546
+ assert.deepEqual(testing.defaults, [
547
+ 'no-chrome-for-testing-infobar',
548
+ 'mock-keychain',
549
+ ]);
550
+ assert.deepEqual(defaultSwitches(testing.args), [
551
+ '--test-type=gpu',
552
+ '--use-mock-keychain',
553
+ ]);
554
+
555
+ // Google Chrome keeps the clean command line of issue #103.
556
+ const chrome = await launch('/opt/google/chrome');
557
+ assert.deepEqual(chrome.defaults, []);
558
+ assert.deepEqual(defaultSwitches(chrome.args), []);
559
+
560
+ // defaultRestrictions replaces the defaults; [] turns them off.
561
+ const off = await launch(cft, { defaultRestrictions: [] });
562
+ assert.deepEqual(defaultSwitches(off.args), []);
563
+ const keychainOnly = await launch(cft, {
564
+ defaultRestrictions: ['mock-keychain'],
565
+ });
566
+ assert.deepEqual(defaultSwitches(keychainOnly.args), [
567
+ '--use-mock-keychain',
568
+ ]);
569
+ });
570
+
519
571
  it('terminates the spawned browser when connection fails', async () => {
520
572
  temporaryDirectory = await makeDedicatedProfile();
521
573
  let killed = false;