summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/packages/browsers/test/src
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/packages/browsers/test/src')
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/chrome-headless-shell-data.spec.ts72
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/cli.spec.ts81
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/install.spec.ts93
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chrome/chrome-data.spec.ts119
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chrome/cli.spec.ts94
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chrome/install.spec.ts233
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chrome/launch.spec.ts122
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chromedriver/chromedriver-data.spec.ts71
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chromedriver/cli.spec.ts81
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chromedriver/install.spec.ts93
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chromium/chromium-data.spec.ts62
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/chromium/launch.spec.ts122
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/firefox/cli.spec.ts87
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/firefox/firefox-data.spec.ts97
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/firefox/install.spec.ts75
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/firefox/launch.spec.ts92
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/mocha-utils.ts8
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/tsconfig.json9
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/tsdoc.json15
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/uninstall.spec.ts63
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/utils.ts75
-rw-r--r--remote/test/puppeteer/packages/browsers/test/src/versions.ts11
22 files changed, 1775 insertions, 0 deletions
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/chrome-headless-shell-data.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/chrome-headless-shell-data.spec.ts
new file mode 100644
index 0000000000..65008b5edb
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/chrome-headless-shell-data.spec.ts
@@ -0,0 +1,72 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import path from 'path';
+
+import {BrowserPlatform} from '../../../lib/cjs/browser-data/browser-data.js';
+import {
+ resolveDownloadUrl,
+ relativeExecutablePath,
+ resolveBuildId,
+} from '../../../lib/cjs/browser-data/chrome-headless-shell.js';
+
+describe('chrome-headless-shell', () => {
+ it('should resolve download URLs', () => {
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.LINUX, '118.0.5950.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/118.0.5950.0/linux64/chrome-headless-shell-linux64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC, '118.0.5950.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/118.0.5950.0/mac-x64/chrome-headless-shell-mac-x64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC_ARM, '118.0.5950.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/118.0.5950.0/mac-arm64/chrome-headless-shell-mac-arm64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN32, '118.0.5950.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/118.0.5950.0/win32/chrome-headless-shell-win32.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN64, '118.0.5950.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/118.0.5950.0/win64/chrome-headless-shell-win64.zip'
+ );
+ });
+
+ // TODO: once no new releases happen for the milestone, we can use the exact match.
+ it('should resolve milestones', async () => {
+ assert((await resolveBuildId('118'))?.startsWith('118.0'));
+ });
+
+ it('should resolve build prefix', async () => {
+ assert.strictEqual(await resolveBuildId('118.0.5950'), '118.0.5950.0');
+ });
+
+ it('should resolve executable paths', () => {
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.LINUX, '12372323'),
+ path.join('chrome-headless-shell-linux64', 'chrome-headless-shell')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC, '12372323'),
+ path.join('chrome-headless-shell-mac-x64/', 'chrome-headless-shell')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC_ARM, '12372323'),
+ path.join('chrome-headless-shell-mac-arm64', 'chrome-headless-shell')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN32, '12372323'),
+ path.join('chrome-headless-shell-win32', 'chrome-headless-shell.exe')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN64, '12372323'),
+ path.join('chrome-headless-shell-win64', 'chrome-headless-shell.exe')
+ );
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/cli.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/cli.spec.ts
new file mode 100644
index 0000000000..445d0f700e
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/cli.spec.ts
@@ -0,0 +1,81 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {CLI} from '../../../lib/cjs/CLI.js';
+import {
+ createMockedReadlineInterface,
+ setupTestServer,
+ getServerUrl,
+} from '../utils.js';
+import {testChromeHeadlessShellBuildId} from '../versions.js';
+
+describe('chrome-headless-shell CLI', function () {
+ this.timeout(90000);
+
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(async () => {
+ await new CLI(tmpDir, createMockedReadlineInterface('yes')).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'clear',
+ `--path=${tmpDir}`,
+ `--base-url=${getServerUrl()}`,
+ ]);
+ });
+
+ it('should download chrome-headless-shell binaries', async () => {
+ await new CLI(tmpDir).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'install',
+ `chrome-headless-shell@${testChromeHeadlessShellBuildId}`,
+ `--path=${tmpDir}`,
+ '--platform=linux',
+ `--base-url=${getServerUrl()}`,
+ ]);
+ assert.ok(
+ fs.existsSync(
+ path.join(
+ tmpDir,
+ 'chrome-headless-shell',
+ `linux-${testChromeHeadlessShellBuildId}`,
+ 'chrome-headless-shell-linux64',
+ 'chrome-headless-shell'
+ )
+ )
+ );
+
+ await new CLI(tmpDir, createMockedReadlineInterface('no')).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'clear',
+ `--path=${tmpDir}`,
+ ]);
+ assert.ok(
+ fs.existsSync(
+ path.join(
+ tmpDir,
+ 'chrome-headless-shell',
+ `linux-${testChromeHeadlessShellBuildId}`,
+ 'chrome-headless-shell-linux64',
+ 'chrome-headless-shell'
+ )
+ )
+ );
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/install.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/install.spec.ts
new file mode 100644
index 0000000000..88f9fae7fc
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chrome-headless-shell/install.spec.ts
@@ -0,0 +1,93 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {
+ install,
+ canDownload,
+ Browser,
+ BrowserPlatform,
+ Cache,
+} from '../../../lib/cjs/main.js';
+import {getServerUrl, setupTestServer} from '../utils.js';
+import {testChromeDriverBuildId} from '../versions.js';
+
+/**
+ * Tests in this spec use real download URLs and unpack live browser archives
+ * so it requires the network access.
+ */
+describe('ChromeDriver install', () => {
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(() => {
+ new Cache(tmpDir).clear();
+ });
+
+ it('should check if a buildId can be downloaded', async () => {
+ assert.ok(
+ await canDownload({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMEDRIVER,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeDriverBuildId,
+ baseUrl: getServerUrl(),
+ })
+ );
+ });
+
+ it('should report if a buildId is not downloadable', async () => {
+ assert.strictEqual(
+ await canDownload({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMEDRIVER,
+ platform: BrowserPlatform.LINUX,
+ buildId: 'unknown',
+ baseUrl: getServerUrl(),
+ }),
+ false
+ );
+ });
+
+ it('should download and unpack the binary', async function () {
+ this.timeout(60000);
+ const expectedOutputPath = path.join(
+ tmpDir,
+ 'chromedriver',
+ `${BrowserPlatform.LINUX}-${testChromeDriverBuildId}`
+ );
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ let browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMEDRIVER,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeDriverBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ // Second iteration should be no-op.
+ browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMEDRIVER,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeDriverBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ assert.ok(fs.existsSync(browser.executablePath));
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chrome/chrome-data.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chrome/chrome-data.spec.ts
new file mode 100644
index 0000000000..510afa8454
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chrome/chrome-data.spec.ts
@@ -0,0 +1,119 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import path from 'path';
+
+import {
+ BrowserPlatform,
+ ChromeReleaseChannel,
+} from '../../../lib/cjs/browser-data/browser-data.js';
+import {
+ resolveDownloadUrl,
+ relativeExecutablePath,
+ resolveSystemExecutablePath,
+ resolveBuildId,
+} from '../../../lib/cjs/browser-data/chrome.js';
+
+describe('Chrome', () => {
+ it('should resolve download URLs', () => {
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.LINUX, '113.0.5672.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/linux64/chrome-linux64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC, '113.0.5672.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/mac-x64/chrome-mac-x64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC_ARM, '113.0.5672.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/mac-arm64/chrome-mac-arm64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN32, '113.0.5672.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/win32/chrome-win32.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN64, '113.0.5672.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/win64/chrome-win64.zip'
+ );
+ });
+
+ it('should resolve executable paths', () => {
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.LINUX, '12372323'),
+ path.join('chrome-linux64', 'chrome')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC, '12372323'),
+ path.join(
+ 'chrome-mac-x64',
+ 'Google Chrome for Testing.app',
+ 'Contents',
+ 'MacOS',
+ 'Google Chrome for Testing'
+ )
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC_ARM, '12372323'),
+ path.join(
+ 'chrome-mac-arm64',
+ 'Google Chrome for Testing.app',
+ 'Contents',
+ 'MacOS',
+ 'Google Chrome for Testing'
+ )
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN32, '12372323'),
+ path.join('chrome-win32', 'chrome.exe')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN64, '12372323'),
+ path.join('chrome-win64', 'chrome.exe')
+ );
+ });
+
+ it('should resolve system executable path', () => {
+ process.env['PROGRAMFILES'] = 'C:\\ProgramFiles';
+ try {
+ assert.strictEqual(
+ resolveSystemExecutablePath(
+ BrowserPlatform.WIN32,
+ ChromeReleaseChannel.DEV
+ ),
+ 'C:\\ProgramFiles\\Google\\Chrome Dev\\Application\\chrome.exe'
+ );
+ } finally {
+ delete process.env['PROGRAMFILES'];
+ }
+
+ assert.strictEqual(
+ resolveSystemExecutablePath(
+ BrowserPlatform.MAC,
+ ChromeReleaseChannel.BETA
+ ),
+ '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta'
+ );
+ assert.throws(() => {
+ assert.strictEqual(
+ resolveSystemExecutablePath(
+ BrowserPlatform.LINUX,
+ ChromeReleaseChannel.CANARY
+ ),
+ path.join('chrome-linux', 'chrome')
+ );
+ }, new Error(`Unable to detect browser executable path for 'canary' on linux.`));
+ });
+
+ it('should resolve milestones', async () => {
+ assert.strictEqual(await resolveBuildId('115'), '115.0.5790.170');
+ });
+
+ it('should resolve build prefix', async () => {
+ assert.strictEqual(await resolveBuildId('115.0.5790'), '115.0.5790.170');
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chrome/cli.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chrome/cli.spec.ts
new file mode 100644
index 0000000000..bdda9d9aa9
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chrome/cli.spec.ts
@@ -0,0 +1,94 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {CLI} from '../../../lib/cjs/CLI.js';
+import {
+ createMockedReadlineInterface,
+ setupTestServer,
+ getServerUrl,
+} from '../utils.js';
+import {testChromeBuildId} from '../versions.js';
+
+describe('Chrome CLI', function () {
+ this.timeout(90000);
+
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(async () => {
+ await new CLI(tmpDir, createMockedReadlineInterface('yes')).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'clear',
+ `--path=${tmpDir}`,
+ `--base-url=${getServerUrl()}`,
+ ]);
+ });
+
+ it('should download Chrome binaries', async () => {
+ await new CLI(tmpDir).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'install',
+ `chrome@${testChromeBuildId}`,
+ `--path=${tmpDir}`,
+ '--platform=linux',
+ `--base-url=${getServerUrl()}`,
+ ]);
+ assert.ok(
+ fs.existsSync(
+ path.join(
+ tmpDir,
+ 'chrome',
+ `linux-${testChromeBuildId}`,
+ 'chrome-linux64',
+ 'chrome'
+ )
+ )
+ );
+
+ await new CLI(tmpDir, createMockedReadlineInterface('no')).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'clear',
+ `--path=${tmpDir}`,
+ ]);
+ assert.ok(
+ fs.existsSync(
+ path.join(
+ tmpDir,
+ 'chrome',
+ `linux-${testChromeBuildId}`,
+ 'chrome-linux64',
+ 'chrome'
+ )
+ )
+ );
+ });
+
+ // Skipped because the current latest is not published yet.
+ it.skip('should download latest Chrome binaries', async () => {
+ await new CLI(tmpDir).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'install',
+ `chrome@latest`,
+ `--path=${tmpDir}`,
+ '--platform=linux',
+ `--base-url=${getServerUrl()}`,
+ ]);
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chrome/install.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chrome/install.spec.ts
new file mode 100644
index 0000000000..8103ff3612
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chrome/install.spec.ts
@@ -0,0 +1,233 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import http from 'http';
+import https from 'https';
+import os from 'os';
+import path from 'path';
+
+import {
+ install,
+ canDownload,
+ Browser,
+ BrowserPlatform,
+ Cache,
+} from '../../../lib/cjs/main.js';
+import {getServerUrl, setupTestServer} from '../utils.js';
+import {testChromeBuildId} from '../versions.js';
+
+/**
+ * Tests in this spec use real download URLs and unpack live browser archives
+ * so it requires the network access.
+ */
+describe('Chrome install', () => {
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(() => {
+ new Cache(tmpDir).clear();
+ });
+
+ it('should check if a buildId can be downloaded', async () => {
+ assert.ok(
+ await canDownload({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeBuildId,
+ baseUrl: getServerUrl(),
+ })
+ );
+ });
+
+ it('should report if a buildId is not downloadable', async () => {
+ assert.strictEqual(
+ await canDownload({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: 'unknown',
+ baseUrl: getServerUrl(),
+ }),
+ false
+ );
+ });
+
+ it('should download a buildId that is a zip archive', async function () {
+ this.timeout(60000);
+ const expectedOutputPath = path.join(
+ tmpDir,
+ 'chrome',
+ `${BrowserPlatform.LINUX}-${testChromeBuildId}`
+ );
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ let browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ // Second iteration should be no-op.
+ browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ // Should discover installed browsers.
+ const cache = new Cache(tmpDir);
+ const installed = cache.getInstalledBrowsers();
+ assert.deepStrictEqual(browser, installed[0]);
+ assert.deepStrictEqual(
+ browser!.executablePath,
+ installed[0]?.executablePath
+ );
+ });
+
+ it('throws on invalid URL', async function () {
+ const expectedOutputPath = path.join(
+ tmpDir,
+ 'chrome',
+ `${BrowserPlatform.LINUX}-${testChromeBuildId}`
+ );
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+
+ async function installThatThrows(): Promise<unknown> {
+ try {
+ await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeBuildId,
+ baseUrl: 'https://127.0.0.1',
+ });
+ return undefined;
+ } catch (err) {
+ return err;
+ }
+ }
+ assert.ok(await installThatThrows());
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ });
+
+ describe('with proxy', () => {
+ const proxyUrl = new URL(`http://localhost:54321`);
+ let proxyServer: http.Server;
+ let proxiedRequestUrls: string[] = [];
+ let proxiedRequestHosts: string[] = [];
+
+ beforeEach(() => {
+ proxiedRequestUrls = [];
+ proxiedRequestHosts = [];
+ proxyServer = http
+ .createServer(
+ (
+ originalRequest: http.IncomingMessage,
+ originalResponse: http.ServerResponse
+ ) => {
+ const url = originalRequest.url as string;
+ const proxyRequest = (
+ url.startsWith('http:') ? http : https
+ ).request(
+ url,
+ {
+ method: originalRequest.method,
+ rejectUnauthorized: false,
+ },
+ proxyResponse => {
+ originalResponse.writeHead(
+ proxyResponse.statusCode as number,
+ proxyResponse.headers
+ );
+ proxyResponse.pipe(originalResponse, {end: true});
+ }
+ );
+ originalRequest.pipe(proxyRequest, {end: true});
+ proxiedRequestUrls.push(url);
+ proxiedRequestHosts.push(originalRequest.headers?.host || '');
+ }
+ )
+ .listen({
+ port: proxyUrl.port,
+ hostname: proxyUrl.hostname,
+ });
+
+ process.env['HTTPS_PROXY'] = proxyUrl.toString();
+ process.env['HTTP_PROXY'] = proxyUrl.toString();
+ });
+
+ afterEach(async () => {
+ await new Promise((resolve, reject) => {
+ proxyServer.close(error => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(undefined);
+ }
+ });
+ });
+ delete process.env['HTTP_PROXY'];
+ delete process.env['HTTPS_PROXY'];
+ });
+
+ it('can send canDownload requests via a proxy', async () => {
+ assert.strictEqual(
+ await canDownload({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeBuildId,
+ baseUrl: getServerUrl(),
+ }),
+ true
+ );
+ assert.deepStrictEqual(proxiedRequestUrls, [
+ getServerUrl() + '/113.0.5672.0/linux64/chrome-linux64.zip',
+ ]);
+ assert.deepStrictEqual(proxiedRequestHosts, [
+ getServerUrl().replace('http://', ''),
+ ]);
+ });
+
+ it('can download via a proxy', async function () {
+ this.timeout(120000);
+ const expectedOutputPath = path.join(
+ tmpDir,
+ 'chrome',
+ `${BrowserPlatform.LINUX}-${testChromeBuildId}`
+ );
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ const browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ assert.deepStrictEqual(proxiedRequestUrls, [
+ getServerUrl() + '/113.0.5672.0/linux64/chrome-linux64.zip',
+ ]);
+ assert.deepStrictEqual(proxiedRequestHosts, [
+ getServerUrl().replace('http://', ''),
+ ]);
+ });
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chrome/launch.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chrome/launch.spec.ts
new file mode 100644
index 0000000000..c420d9e0b6
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chrome/launch.spec.ts
@@ -0,0 +1,122 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {
+ CDP_WEBSOCKET_ENDPOINT_REGEX,
+ computeExecutablePath,
+ launch,
+ install,
+ Browser,
+ BrowserPlatform,
+} from '../../../lib/cjs/main.js';
+import {getServerUrl, setupTestServer, clearCache} from '../utils.js';
+import {testChromeBuildId} from '../versions.js';
+
+describe('Chrome', () => {
+ it('should compute executable path for Chrome', () => {
+ assert.strictEqual(
+ computeExecutablePath({
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: '123',
+ cacheDir: '.cache',
+ }),
+ path.join('.cache', 'chrome', 'linux-123', 'chrome-linux64', 'chrome')
+ );
+ });
+
+ describe('launcher', function () {
+ setupTestServer();
+
+ this.timeout(60000);
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(async () => {
+ tmpDir = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'puppeteer-browsers-test')
+ );
+ await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ buildId: testChromeBuildId,
+ baseUrl: getServerUrl(),
+ });
+ });
+
+ afterEach(() => {
+ clearCache(tmpDir);
+ });
+
+ function getArgs() {
+ return [
+ '--allow-pre-commit-input',
+ '--disable-background-networking',
+ '--disable-background-timer-throttling',
+ '--disable-backgrounding-occluded-windows',
+ '--disable-breakpad',
+ '--disable-client-side-phishing-detection',
+ '--disable-component-extensions-with-background-pages',
+ '--disable-component-update',
+ '--disable-default-apps',
+ '--disable-dev-shm-usage',
+ '--disable-extensions',
+ '--disable-features=Translate,BackForwardCache,AcceptCHFrame,MediaRouter,OptimizationHints,DialMediaRouteProvider',
+ '--disable-hang-monitor',
+ '--disable-ipc-flooding-protection',
+ '--disable-popup-blocking',
+ '--disable-prompt-on-repost',
+ '--disable-renderer-backgrounding',
+ '--disable-sync',
+ '--enable-automation',
+ '--enable-features=NetworkServiceInProcess2',
+ '--export-tagged-pdf',
+ '--force-color-profile=srgb',
+ '--headless=new',
+ '--metrics-recording-only',
+ '--no-first-run',
+ '--password-store=basic',
+ '--remote-debugging-port=9222',
+ '--use-mock-keychain',
+ `--user-data-dir=${path.join(tmpDir, 'profile')}`,
+ 'about:blank',
+ ];
+ }
+
+ it('should launch a Chrome browser', async () => {
+ const executablePath = computeExecutablePath({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ buildId: testChromeBuildId,
+ });
+ const process = launch({
+ executablePath,
+ args: getArgs(),
+ });
+ await process.close();
+ });
+
+ it('should allow parsing stderr output of the browser process', async () => {
+ const executablePath = computeExecutablePath({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ buildId: testChromeBuildId,
+ });
+ const process = launch({
+ executablePath,
+ args: getArgs(),
+ });
+ const url = await process.waitForLineOutput(CDP_WEBSOCKET_ENDPOINT_REGEX);
+ await process.close();
+ assert.ok(url.startsWith('ws://127.0.0.1:9222/devtools/browser'));
+ });
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chromedriver/chromedriver-data.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chromedriver/chromedriver-data.spec.ts
new file mode 100644
index 0000000000..62522d88f4
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chromedriver/chromedriver-data.spec.ts
@@ -0,0 +1,71 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import path from 'path';
+
+import {BrowserPlatform} from '../../../lib/cjs/browser-data/browser-data.js';
+import {
+ resolveDownloadUrl,
+ relativeExecutablePath,
+ resolveBuildId,
+} from '../../../lib/cjs/browser-data/chromedriver.js';
+
+describe('ChromeDriver', () => {
+ it('should resolve download URLs', () => {
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.LINUX, '115.0.5763.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/115.0.5763.0/linux64/chromedriver-linux64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC, '115.0.5763.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/115.0.5763.0/mac-x64/chromedriver-mac-x64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC_ARM, '115.0.5763.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/115.0.5763.0/mac-arm64/chromedriver-mac-arm64.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN32, '115.0.5763.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/115.0.5763.0/win32/chromedriver-win32.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN64, '115.0.5763.0'),
+ 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/115.0.5763.0/win64/chromedriver-win64.zip'
+ );
+ });
+
+ it('should resolve milestones', async () => {
+ assert.strictEqual(await resolveBuildId('115'), '115.0.5790.170');
+ });
+
+ it('should resolve build prefix', async () => {
+ assert.strictEqual(await resolveBuildId('115.0.5790'), '115.0.5790.170');
+ });
+
+ it('should resolve executable paths', () => {
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.LINUX, '12372323'),
+ path.join('chromedriver-linux64', 'chromedriver')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC, '12372323'),
+ path.join('chromedriver-mac-x64/', 'chromedriver')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC_ARM, '12372323'),
+ path.join('chromedriver-mac-arm64', 'chromedriver')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN32, '12372323'),
+ path.join('chromedriver-win32', 'chromedriver.exe')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN64, '12372323'),
+ path.join('chromedriver-win64', 'chromedriver.exe')
+ );
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chromedriver/cli.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chromedriver/cli.spec.ts
new file mode 100644
index 0000000000..d407062a88
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chromedriver/cli.spec.ts
@@ -0,0 +1,81 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {CLI} from '../../../lib/cjs/CLI.js';
+import {
+ createMockedReadlineInterface,
+ setupTestServer,
+ getServerUrl,
+} from '../utils.js';
+import {testChromeDriverBuildId} from '../versions.js';
+
+describe('ChromeDriver CLI', function () {
+ this.timeout(90000);
+
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(async () => {
+ await new CLI(tmpDir, createMockedReadlineInterface('yes')).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'clear',
+ `--path=${tmpDir}`,
+ `--base-url=${getServerUrl()}`,
+ ]);
+ });
+
+ it('should download ChromeDriver binaries', async () => {
+ await new CLI(tmpDir).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'install',
+ `chromedriver@${testChromeDriverBuildId}`,
+ `--path=${tmpDir}`,
+ '--platform=linux',
+ `--base-url=${getServerUrl()}`,
+ ]);
+ assert.ok(
+ fs.existsSync(
+ path.join(
+ tmpDir,
+ 'chromedriver',
+ `linux-${testChromeDriverBuildId}`,
+ 'chromedriver-linux64',
+ 'chromedriver'
+ )
+ )
+ );
+
+ await new CLI(tmpDir, createMockedReadlineInterface('no')).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'clear',
+ `--path=${tmpDir}`,
+ ]);
+ assert.ok(
+ fs.existsSync(
+ path.join(
+ tmpDir,
+ 'chromedriver',
+ `linux-${testChromeDriverBuildId}`,
+ 'chromedriver-linux64',
+ 'chromedriver'
+ )
+ )
+ );
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chromedriver/install.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chromedriver/install.spec.ts
new file mode 100644
index 0000000000..88f9fae7fc
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chromedriver/install.spec.ts
@@ -0,0 +1,93 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {
+ install,
+ canDownload,
+ Browser,
+ BrowserPlatform,
+ Cache,
+} from '../../../lib/cjs/main.js';
+import {getServerUrl, setupTestServer} from '../utils.js';
+import {testChromeDriverBuildId} from '../versions.js';
+
+/**
+ * Tests in this spec use real download URLs and unpack live browser archives
+ * so it requires the network access.
+ */
+describe('ChromeDriver install', () => {
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(() => {
+ new Cache(tmpDir).clear();
+ });
+
+ it('should check if a buildId can be downloaded', async () => {
+ assert.ok(
+ await canDownload({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMEDRIVER,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeDriverBuildId,
+ baseUrl: getServerUrl(),
+ })
+ );
+ });
+
+ it('should report if a buildId is not downloadable', async () => {
+ assert.strictEqual(
+ await canDownload({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMEDRIVER,
+ platform: BrowserPlatform.LINUX,
+ buildId: 'unknown',
+ baseUrl: getServerUrl(),
+ }),
+ false
+ );
+ });
+
+ it('should download and unpack the binary', async function () {
+ this.timeout(60000);
+ const expectedOutputPath = path.join(
+ tmpDir,
+ 'chromedriver',
+ `${BrowserPlatform.LINUX}-${testChromeDriverBuildId}`
+ );
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ let browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMEDRIVER,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeDriverBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ // Second iteration should be no-op.
+ browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMEDRIVER,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeDriverBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ assert.ok(fs.existsSync(browser.executablePath));
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chromium/chromium-data.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chromium/chromium-data.spec.ts
new file mode 100644
index 0000000000..601efccc47
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chromium/chromium-data.spec.ts
@@ -0,0 +1,62 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import path from 'path';
+
+import {BrowserPlatform} from '../../../lib/cjs/browser-data/browser-data.js';
+import {
+ resolveDownloadUrl,
+ relativeExecutablePath,
+} from '../../../lib/cjs/browser-data/chromium.js';
+
+describe('Chromium', () => {
+ it('should resolve download URLs', () => {
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.LINUX, '1083080'),
+ 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1083080/chrome-linux.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC, '1083080'),
+ 'https://storage.googleapis.com/chromium-browser-snapshots/Mac/1083080/chrome-mac.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC_ARM, '1083080'),
+ 'https://storage.googleapis.com/chromium-browser-snapshots/Mac_Arm/1083080/chrome-mac.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN32, '1083080'),
+ 'https://storage.googleapis.com/chromium-browser-snapshots/Win/1083080/chrome-win.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN64, '1083080'),
+ 'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/1083080/chrome-win.zip'
+ );
+ });
+
+ it('should resolve executable paths', () => {
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.LINUX, '12372323'),
+ path.join('chrome-linux', 'chrome')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC, '12372323'),
+ path.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC_ARM, '12372323'),
+ path.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN32, '12372323'),
+ path.join('chrome-win', 'chrome.exe')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN64, '12372323'),
+ path.join('chrome-win', 'chrome.exe')
+ );
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/chromium/launch.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/chromium/launch.spec.ts
new file mode 100644
index 0000000000..8cf7c8255b
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/chromium/launch.spec.ts
@@ -0,0 +1,122 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {
+ CDP_WEBSOCKET_ENDPOINT_REGEX,
+ computeExecutablePath,
+ launch,
+ install,
+ Browser,
+ BrowserPlatform,
+} from '../../../lib/cjs/main.js';
+import {getServerUrl, setupTestServer, clearCache} from '../utils.js';
+import {testChromiumBuildId} from '../versions.js';
+
+describe('Chromium', () => {
+ it('should compute executable path for Chromium', () => {
+ assert.strictEqual(
+ computeExecutablePath({
+ browser: Browser.CHROMIUM,
+ platform: BrowserPlatform.LINUX,
+ buildId: '123',
+ cacheDir: '.cache',
+ }),
+ path.join('.cache', 'chromium', 'linux-123', 'chrome-linux', 'chrome')
+ );
+ });
+
+ describe('launcher', function () {
+ setupTestServer();
+
+ this.timeout(120000);
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(async () => {
+ tmpDir = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'puppeteer-browsers-test')
+ );
+ await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMIUM,
+ buildId: testChromiumBuildId,
+ baseUrl: getServerUrl(),
+ });
+ });
+
+ afterEach(() => {
+ clearCache(tmpDir);
+ });
+
+ function getArgs() {
+ return [
+ '--allow-pre-commit-input',
+ '--disable-background-networking',
+ '--disable-background-timer-throttling',
+ '--disable-backgrounding-occluded-windows',
+ '--disable-breakpad',
+ '--disable-client-side-phishing-detection',
+ '--disable-component-extensions-with-background-pages',
+ '--disable-component-update',
+ '--disable-default-apps',
+ '--disable-dev-shm-usage',
+ '--disable-extensions',
+ '--disable-features=Translate,BackForwardCache,AcceptCHFrame,MediaRouter,OptimizationHints,DialMediaRouteProvider',
+ '--disable-hang-monitor',
+ '--disable-ipc-flooding-protection',
+ '--disable-popup-blocking',
+ '--disable-prompt-on-repost',
+ '--disable-renderer-backgrounding',
+ '--disable-sync',
+ '--enable-automation',
+ '--enable-features=NetworkServiceInProcess2',
+ '--export-tagged-pdf',
+ '--force-color-profile=srgb',
+ '--headless=new',
+ '--metrics-recording-only',
+ '--no-first-run',
+ '--password-store=basic',
+ '--remote-debugging-port=9222',
+ '--use-mock-keychain',
+ `--user-data-dir=${path.join(tmpDir, 'profile')}`,
+ 'about:blank',
+ ];
+ }
+
+ it('should launch a Chromium browser', async () => {
+ const executablePath = computeExecutablePath({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMIUM,
+ buildId: testChromiumBuildId,
+ });
+ const process = launch({
+ executablePath,
+ args: getArgs(),
+ });
+ await process.close();
+ });
+
+ it('should allow parsing stderr output of the browser process', async () => {
+ const executablePath = computeExecutablePath({
+ cacheDir: tmpDir,
+ browser: Browser.CHROMIUM,
+ buildId: testChromiumBuildId,
+ });
+ const process = launch({
+ executablePath,
+ args: getArgs(),
+ });
+ const url = await process.waitForLineOutput(CDP_WEBSOCKET_ENDPOINT_REGEX);
+ await process.close();
+ assert.ok(url.startsWith('ws://127.0.0.1:9222/devtools/browser'));
+ });
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/firefox/cli.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/firefox/cli.spec.ts
new file mode 100644
index 0000000000..134b432641
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/firefox/cli.spec.ts
@@ -0,0 +1,87 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import sinon from 'sinon';
+
+import {CLI} from '../../../lib/cjs/CLI.js';
+import * as httpUtil from '../../../lib/cjs/httpUtil.js';
+import {
+ createMockedReadlineInterface,
+ getServerUrl,
+ setupTestServer,
+} from '../utils.js';
+import {testFirefoxBuildId} from '../versions.js';
+
+describe('Firefox CLI', function () {
+ this.timeout(90000);
+
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(async () => {
+ await new CLI(tmpDir, createMockedReadlineInterface('yes')).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'clear',
+ `--path=${tmpDir}`,
+ `--base-url=${getServerUrl()}`,
+ ]);
+
+ sinon.restore();
+ });
+
+ it('should download Firefox binaries', async () => {
+ await new CLI(tmpDir).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'install',
+ `firefox@${testFirefoxBuildId}`,
+ `--path=${tmpDir}`,
+ '--platform=linux',
+ `--base-url=${getServerUrl()}`,
+ ]);
+ assert.ok(
+ fs.existsSync(
+ path.join(tmpDir, 'firefox', `linux-${testFirefoxBuildId}`, 'firefox')
+ )
+ );
+ });
+
+ it('should download latest Firefox binaries', async () => {
+ sinon
+ .stub(httpUtil, 'getJSON')
+ .returns(Promise.resolve({FIREFOX_NIGHTLY: testFirefoxBuildId}));
+ await new CLI(tmpDir).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'install',
+ `firefox@latest`,
+ `--path=${tmpDir}`,
+ '--platform=linux',
+ `--base-url=${getServerUrl()}`,
+ ]);
+
+ await new CLI(tmpDir).run([
+ 'npx',
+ '@puppeteer/browsers',
+ 'install',
+ `firefox`,
+ `--path=${tmpDir}`,
+ '--platform=linux',
+ `--base-url=${getServerUrl()}`,
+ ]);
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/firefox/firefox-data.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/firefox/firefox-data.spec.ts
new file mode 100644
index 0000000000..d0bb056090
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/firefox/firefox-data.spec.ts
@@ -0,0 +1,97 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {BrowserPlatform} from '../../../lib/cjs/browser-data/browser-data.js';
+import {
+ createProfile,
+ relativeExecutablePath,
+ resolveDownloadUrl,
+} from '../../../lib/cjs/browser-data/firefox.js';
+
+describe('Firefox', () => {
+ it('should resolve download URLs', () => {
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.LINUX, '111.0a1'),
+ 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.linux-x86_64.tar.bz2'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC, '111.0a1'),
+ 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.mac.dmg'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.MAC_ARM, '111.0a1'),
+ 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.mac.dmg'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN32, '111.0a1'),
+ 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.win32.zip'
+ );
+ assert.strictEqual(
+ resolveDownloadUrl(BrowserPlatform.WIN64, '111.0a1'),
+ 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.win64.zip'
+ );
+ });
+
+ it('should resolve executable paths', () => {
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.LINUX, '111.0a1'),
+ path.join('firefox', 'firefox')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC, '111.0a1'),
+ path.join('Firefox Nightly.app', 'Contents', 'MacOS', 'firefox')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.MAC_ARM, '111.0a1'),
+ path.join('Firefox Nightly.app', 'Contents', 'MacOS', 'firefox')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN32, '111.0a1'),
+ path.join('firefox', 'firefox.exe')
+ );
+ assert.strictEqual(
+ relativeExecutablePath(BrowserPlatform.WIN64, '111.0a1'),
+ path.join('firefox', 'firefox.exe')
+ );
+ });
+
+ describe('profile', () => {
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'puppeteer-browsers-test')
+ );
+ });
+
+ afterEach(() => {
+ fs.rmSync(tmpDir, {
+ force: true,
+ recursive: true,
+ maxRetries: 5,
+ });
+ });
+
+ it('should create a profile', async () => {
+ await createProfile({
+ preferences: {
+ test: 1,
+ },
+ path: tmpDir,
+ });
+ const text = fs.readFileSync(path.join(tmpDir, 'user.js'), 'utf-8');
+ assert.ok(
+ text.includes(`user_pref("toolkit.startup.max_resumed_crashes", -1);`)
+ ); // default preference.
+ assert.ok(text.includes(`user_pref("test", 1);`)); // custom preference.
+ });
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/firefox/install.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/firefox/install.spec.ts
new file mode 100644
index 0000000000..1bada43729
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/firefox/install.spec.ts
@@ -0,0 +1,75 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {install, Browser, BrowserPlatform} from '../../../lib/cjs/main.js';
+import {setupTestServer, getServerUrl, clearCache} from '../utils.js';
+import {testFirefoxBuildId} from '../versions.js';
+
+/**
+ * Tests in this spec use real download URLs and unpack live browser archives
+ * so it requires the network access.
+ */
+describe('Firefox install', () => {
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(() => {
+ clearCache(tmpDir);
+ });
+
+ it('should download a buildId that is a bzip2 archive', async function () {
+ this.timeout(90000);
+ const expectedOutputPath = path.join(
+ tmpDir,
+ 'firefox',
+ `${BrowserPlatform.LINUX}-${testFirefoxBuildId}`
+ );
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ const browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.FIREFOX,
+ platform: BrowserPlatform.LINUX,
+ buildId: testFirefoxBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ });
+
+ // install relies on the `hdiutil` utility on MacOS.
+ // The utility is not available on other platforms.
+ (os.platform() === 'darwin' ? it : it.skip)(
+ 'should download a buildId that is a dmg archive',
+ async function () {
+ this.timeout(180000);
+ const expectedOutputPath = path.join(
+ tmpDir,
+ 'firefox',
+ `${BrowserPlatform.MAC}-${testFirefoxBuildId}`
+ );
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ const browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.FIREFOX,
+ platform: BrowserPlatform.MAC,
+ buildId: testFirefoxBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+ }
+ );
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/firefox/launch.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/firefox/launch.spec.ts
new file mode 100644
index 0000000000..3c62c87448
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/firefox/launch.spec.ts
@@ -0,0 +1,92 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {
+ computeExecutablePath,
+ launch,
+ install,
+ Browser,
+ BrowserPlatform,
+ createProfile,
+} from '../../../lib/cjs/main.js';
+import {setupTestServer, getServerUrl, clearCache} from '../utils.js';
+import {testFirefoxBuildId} from '../versions.js';
+
+describe('Firefox', () => {
+ it('should compute executable path for Firefox', () => {
+ assert.strictEqual(
+ computeExecutablePath({
+ browser: Browser.FIREFOX,
+ platform: BrowserPlatform.LINUX,
+ buildId: '123',
+ cacheDir: '.cache',
+ }),
+ path.join('.cache', 'firefox', 'linux-123', 'firefox', 'firefox')
+ );
+ });
+
+ describe('launcher', function () {
+ this.timeout(120000);
+
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(async () => {
+ tmpDir = fs.mkdtempSync(
+ path.join(os.tmpdir(), 'puppeteer-browsers-test')
+ );
+ await install({
+ cacheDir: tmpDir,
+ browser: Browser.FIREFOX,
+ buildId: testFirefoxBuildId,
+ baseUrl: getServerUrl(),
+ });
+ });
+
+ afterEach(() => {
+ clearCache(tmpDir);
+ });
+
+ it('should launch a Firefox browser', async () => {
+ const userDataDir = path.join(tmpDir, 'profile');
+ function getArgs(): string[] {
+ const firefoxArguments = ['--no-remote'];
+ switch (os.platform()) {
+ case 'darwin':
+ firefoxArguments.push('--foreground');
+ break;
+ case 'win32':
+ firefoxArguments.push('--wait-for-browser');
+ break;
+ }
+ firefoxArguments.push('--profile', userDataDir);
+ firefoxArguments.push('--headless');
+ firefoxArguments.push('about:blank');
+ return firefoxArguments;
+ }
+ await createProfile(Browser.FIREFOX, {
+ path: userDataDir,
+ preferences: {},
+ });
+ const executablePath = computeExecutablePath({
+ cacheDir: tmpDir,
+ browser: Browser.FIREFOX,
+ buildId: testFirefoxBuildId,
+ });
+ const process = launch({
+ executablePath,
+ args: getArgs(),
+ });
+ await process.close();
+ });
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/mocha-utils.ts b/remote/test/puppeteer/packages/browsers/test/src/mocha-utils.ts
new file mode 100644
index 0000000000..245a0048b2
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/mocha-utils.ts
@@ -0,0 +1,8 @@
+import debug from 'debug';
+
+export const mochaHooks = {
+ async beforeAll(): Promise<void> {
+ // Enable logging for Debug
+ debug.enable('puppeteer:*');
+ },
+};
diff --git a/remote/test/puppeteer/packages/browsers/test/src/tsconfig.json b/remote/test/puppeteer/packages/browsers/test/src/tsconfig.json
new file mode 100644
index 0000000000..03eae4a458
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../../../tsconfig.base.json",
+ "compilerOptions": {
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "outDir": "../build",
+ },
+ "references": [{"path": "../../tsconfig.json"}],
+}
diff --git a/remote/test/puppeteer/packages/browsers/test/src/tsdoc.json b/remote/test/puppeteer/packages/browsers/test/src/tsdoc.json
new file mode 100644
index 0000000000..f5b91f4af6
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/tsdoc.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
+
+ "extends": ["@microsoft/api-extractor/extends/tsdoc-base.json"],
+ "tagDefinitions": [
+ {
+ "tagName": "@license",
+ "syntaxKind": "modifier",
+ "allowMultiple": false
+ }
+ ],
+ "supportForTags": {
+ "@license": true
+ }
+}
diff --git a/remote/test/puppeteer/packages/browsers/test/src/uninstall.spec.ts b/remote/test/puppeteer/packages/browsers/test/src/uninstall.spec.ts
new file mode 100644
index 0000000000..0ef8a20fde
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/uninstall.spec.ts
@@ -0,0 +1,63 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import assert from 'assert';
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+import {
+ install,
+ uninstall,
+ Browser,
+ BrowserPlatform,
+ Cache,
+} from '../../lib/cjs/main.js';
+
+import {getServerUrl, setupTestServer} from './utils.js';
+import {testChromeBuildId} from './versions.js';
+
+describe('common', () => {
+ setupTestServer();
+
+ let tmpDir = '/tmp/puppeteer-browsers-test';
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
+ });
+
+ afterEach(() => {
+ new Cache(tmpDir).clear();
+ });
+
+ it('should uninstall a browser', async function () {
+ this.timeout(60000);
+ const expectedOutputPath = path.join(
+ tmpDir,
+ 'chrome',
+ `${BrowserPlatform.LINUX}-${testChromeBuildId}`
+ );
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ const browser = await install({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeBuildId,
+ baseUrl: getServerUrl(),
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.ok(fs.existsSync(expectedOutputPath));
+
+ await uninstall({
+ cacheDir: tmpDir,
+ browser: Browser.CHROME,
+ platform: BrowserPlatform.LINUX,
+ buildId: testChromeBuildId,
+ });
+ assert.strictEqual(browser.path, expectedOutputPath);
+ assert.strictEqual(fs.existsSync(expectedOutputPath), false);
+ });
+});
diff --git a/remote/test/puppeteer/packages/browsers/test/src/utils.ts b/remote/test/puppeteer/packages/browsers/test/src/utils.ts
new file mode 100644
index 0000000000..bae231423e
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/utils.ts
@@ -0,0 +1,75 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {execSync} from 'child_process';
+import os from 'os';
+import path from 'path';
+import * as readline from 'readline';
+import {Writable, Readable} from 'stream';
+
+import {TestServer} from '@pptr/testserver';
+
+import {isErrorLike} from '../../lib/cjs/launch.js';
+import {Cache} from '../../lib/cjs/main.js';
+
+export function createMockedReadlineInterface(
+ input: string
+): readline.Interface {
+ const readable = Readable.from([input]);
+ const writable = new Writable({
+ write(_chunk, _encoding, callback) {
+ // Suppress the output to keep the test clean
+ callback();
+ },
+ });
+
+ return readline.createInterface({
+ input: readable,
+ output: writable,
+ });
+}
+
+const startServer = async () => {
+ const assetsPath = path.join(__dirname, '..', '.cache', 'server');
+ return await TestServer.create(assetsPath);
+};
+
+interface ServerState {
+ server: TestServer;
+}
+
+const state: Partial<ServerState> = {};
+
+export function setupTestServer(): void {
+ before(async () => {
+ state.server = await startServer();
+ });
+
+ after(async () => {
+ await state.server!.stop();
+ state.server = undefined;
+ });
+}
+
+export function getServerUrl(): string {
+ return `http://localhost:${state.server!.port}`;
+}
+
+export function clearCache(tmpDir: string): void {
+ try {
+ new Cache(tmpDir).clear();
+ } catch (err) {
+ if (os.platform() === 'win32') {
+ console.log(execSync('tasklist').toString('utf-8'));
+ // Sometimes on Windows the folder cannot be removed due to unknown reasons.
+ // We suppress the error to avoud flakiness.
+ if (isErrorLike(err) && err.message.includes('EBUSY')) {
+ return;
+ }
+ }
+ throw err;
+ }
+}
diff --git a/remote/test/puppeteer/packages/browsers/test/src/versions.ts b/remote/test/puppeteer/packages/browsers/test/src/versions.ts
new file mode 100644
index 0000000000..3e13b8fc61
--- /dev/null
+++ b/remote/test/puppeteer/packages/browsers/test/src/versions.ts
@@ -0,0 +1,11 @@
+/**
+ * @license
+ * Copyright 2023 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export const testChromeBuildId = '113.0.5672.0';
+export const testChromiumBuildId = '1083080';
+export const testFirefoxBuildId = '123.0a1';
+export const testChromeDriverBuildId = '115.0.5763.0';
+export const testChromeHeadlessShellBuildId = '118.0.5950.0';