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.
- package/CHANGELOG.md +8 -0
- package/README.md +48 -5
- package/package.json +1 -1
- package/src/browser/chrome-for-testing-cache.js +198 -0
- package/src/browser/launch-executable.js +203 -0
- package/src/browser/launch-restrictions.json +7 -1
- package/src/browser/launcher.js +78 -65
- package/src/browser/parity.js +6 -1
- package/src/browser/real-browser.js +29 -9
- package/src/browser/webdriver.js +22 -3
- package/src/capture/screenshot.js +16 -1
- package/src/capture/viewport.js +2 -1
- package/src/exports.js +4 -0
- package/src/traces/recorder.js +18 -2
- package/tests/e2e/startup-readiness.e2e.test.js +2 -0
- package/tests/unit/browser/chrome-for-testing-cache.test.js +139 -0
- package/tests/unit/browser/launch-executable.test.js +93 -0
- package/tests/unit/browser/launcher.test.js +235 -31
- package/tests/unit/browser/parity.test.js +19 -0
- package/tests/unit/browser/real-browser.test.js +52 -0
- package/tests/unit/browser/webdriver.test.js +39 -0
- package/tests/unit/traces/issue-162-defaults.test.js +1 -1
- package/tests/unit/traces/issue-166-policy.test.js +1 -1
- package/tests/unit/traces/issue-180-screenshots.test.js +173 -0
|
@@ -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
|
|
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
|
|
61
|
-
{},
|
|
62
|
-
{
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
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,
|
|
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:
|
|
303
|
-
loadEngineModule:
|
|
304
|
-
|
|
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
|
-
/
|
|
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;
|