feat: update Chrome browser binaries (#9917)

This commit is contained in:
Alex Rudenko 2023-03-27 12:01:06 +02:00 committed by GitHub
parent 95c99e84b8
commit fcb233ce94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 684 additions and 211 deletions

View File

@ -15,6 +15,7 @@
*/ */
import * as chrome from './chrome.js'; import * as chrome from './chrome.js';
import * as chromium from './chromium.js';
import * as firefox from './firefox.js'; import * as firefox from './firefox.js';
import { import {
Browser, Browser,
@ -26,13 +27,13 @@ import {
export const downloadUrls = { export const downloadUrls = {
[Browser.CHROME]: chrome.resolveDownloadUrl, [Browser.CHROME]: chrome.resolveDownloadUrl,
[Browser.CHROMIUM]: chrome.resolveDownloadUrl, [Browser.CHROMIUM]: chromium.resolveDownloadUrl,
[Browser.FIREFOX]: firefox.resolveDownloadUrl, [Browser.FIREFOX]: firefox.resolveDownloadUrl,
}; };
export const executablePathByBrowser = { export const executablePathByBrowser = {
[Browser.CHROME]: chrome.relativeExecutablePath, [Browser.CHROME]: chrome.relativeExecutablePath,
[Browser.CHROMIUM]: chrome.relativeExecutablePath, [Browser.CHROMIUM]: chromium.relativeExecutablePath,
[Browser.FIREFOX]: firefox.relativeExecutablePath, [Browser.FIREFOX]: firefox.relativeExecutablePath,
}; };
@ -50,10 +51,15 @@ export async function resolveBuildId(
return await firefox.resolveBuildId('FIREFOX_NIGHTLY'); return await firefox.resolveBuildId('FIREFOX_NIGHTLY');
} }
case Browser.CHROME: case Browser.CHROME:
switch (tag as BrowserTag) {
case BrowserTag.LATEST:
// In CfT beta is the latest version.
return await chrome.resolveBuildId(platform, 'beta');
}
case Browser.CHROMIUM: case Browser.CHROMIUM:
switch (tag as BrowserTag) { switch (tag as BrowserTag) {
case BrowserTag.LATEST: case BrowserTag.LATEST:
return await chrome.resolveBuildId(platform, 'latest'); return await chromium.resolveBuildId(platform, 'latest');
} }
} }
// We assume the tag is the buildId if it didn't match any keywords. // We assume the tag is the buildId if it didn't match any keywords.
@ -84,6 +90,7 @@ export function resolveSystemExecutablePath(
'System browser detection is not supported for Firefox yet.' 'System browser detection is not supported for Firefox yet.'
); );
case Browser.CHROME: case Browser.CHROME:
return chromium.resolveSystemExecutablePath(platform, channel);
case Browser.CHROMIUM: case Browser.CHROMIUM:
return chrome.resolveSystemExecutablePath(platform, channel); return chrome.resolveSystemExecutablePath(platform, channel);
} }

View File

@ -20,43 +20,43 @@ import {httpRequest} from '../httpUtil.js';
import {BrowserPlatform, ChromeReleaseChannel} from './types.js'; import {BrowserPlatform, ChromeReleaseChannel} from './types.js';
function archive(platform: BrowserPlatform, buildId: string): string {
switch (platform) {
case BrowserPlatform.LINUX:
return 'chrome-linux';
case BrowserPlatform.MAC_ARM:
case BrowserPlatform.MAC:
return 'chrome-mac';
case BrowserPlatform.WIN32:
case BrowserPlatform.WIN64:
// Windows archive name changed at r591479.
return parseInt(buildId, 10) > 591479 ? 'chrome-win' : 'chrome-win32';
}
}
function folder(platform: BrowserPlatform): string { function folder(platform: BrowserPlatform): string {
switch (platform) { switch (platform) {
case BrowserPlatform.LINUX: case BrowserPlatform.LINUX:
return 'Linux_x64'; return 'linux64';
case BrowserPlatform.MAC_ARM: case BrowserPlatform.MAC_ARM:
return 'Mac_Arm'; return 'mac-arm64';
case BrowserPlatform.MAC: case BrowserPlatform.MAC:
return 'Mac'; return 'mac-x64';
case BrowserPlatform.WIN32: case BrowserPlatform.WIN32:
return 'Win'; return 'win32';
case BrowserPlatform.WIN64: case BrowserPlatform.WIN64:
return 'Win_x64'; return 'win64';
}
}
function chromiumDashPlatform(platform: BrowserPlatform): string {
switch (platform) {
case BrowserPlatform.LINUX:
return 'linux';
case BrowserPlatform.MAC_ARM:
return 'mac';
case BrowserPlatform.MAC:
return 'mac';
case BrowserPlatform.WIN32:
return 'win';
case BrowserPlatform.WIN64:
return 'win64';
} }
} }
export function resolveDownloadUrl( export function resolveDownloadUrl(
platform: BrowserPlatform, platform: BrowserPlatform,
buildId: string, buildId: string,
baseUrl = 'https://storage.googleapis.com/chromium-browser-snapshots' baseUrl = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'
): string { ): string {
return `${baseUrl}/${folder(platform)}/${buildId}/${archive( return `${baseUrl}/${buildId}/${folder(platform)}/chrome-${folder(
platform, platform
buildId
)}.zip`; )}.zip`;
} }
@ -68,30 +68,29 @@ export function relativeExecutablePath(
case BrowserPlatform.MAC: case BrowserPlatform.MAC:
case BrowserPlatform.MAC_ARM: case BrowserPlatform.MAC_ARM:
return path.join( return path.join(
'chrome-mac', 'chrome-' + folder(platform),
'Chromium.app', 'Google Chrome for Testing.app',
'Contents', 'Contents',
'MacOS', 'MacOS',
'Chromium' 'Google Chrome for Testing'
); );
case BrowserPlatform.LINUX: case BrowserPlatform.LINUX:
return path.join('chrome-linux', 'chrome'); return path.join('chrome-linux64', 'chrome');
case BrowserPlatform.WIN32: case BrowserPlatform.WIN32:
case BrowserPlatform.WIN64: case BrowserPlatform.WIN64:
return path.join('chrome-win', 'chrome.exe'); return path.join('chrome-' + folder(platform), 'chrome.exe');
} }
} }
export async function resolveBuildId( export async function resolveBuildId(
platform: BrowserPlatform, platform: BrowserPlatform,
// We will need it for other channels/keywords. channel: 'beta' | 'stable' = 'beta'
_channel: 'latest' = 'latest'
): Promise<string> { ): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const request = httpRequest( const request = httpRequest(
new URL( new URL(
`https://storage.googleapis.com/chromium-browser-snapshots/${folder( `https://chromiumdash.appspot.com/fetch_releases?platform=${chromiumDashPlatform(
platform platform
)}/LAST_CHANGE` )}&channel=${channel}`
), ),
'GET', 'GET',
response => { response => {
@ -104,7 +103,8 @@ export async function resolveBuildId(
}); });
response.on('end', () => { response.on('end', () => {
try { try {
return resolve(String(data)); const response = JSON.parse(String(data));
return resolve(response[0].version);
} catch { } catch {
return reject(new Error('Chrome version not found')); return reject(new Error('Chrome version not found'));
} }

View File

@ -0,0 +1,121 @@
/**
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import path from 'path';
import {httpRequest} from '../httpUtil.js';
import {BrowserPlatform} from './types.js';
export {resolveSystemExecutablePath} from './chrome.js';
function archive(platform: BrowserPlatform, buildId: string): string {
switch (platform) {
case BrowserPlatform.LINUX:
return 'chrome-linux';
case BrowserPlatform.MAC_ARM:
case BrowserPlatform.MAC:
return 'chrome-mac';
case BrowserPlatform.WIN32:
case BrowserPlatform.WIN64:
// Windows archive name changed at r591479.
return parseInt(buildId, 10) > 591479 ? 'chrome-win' : 'chrome-win32';
}
}
function folder(platform: BrowserPlatform): string {
switch (platform) {
case BrowserPlatform.LINUX:
return 'Linux_x64';
case BrowserPlatform.MAC_ARM:
return 'Mac_Arm';
case BrowserPlatform.MAC:
return 'Mac';
case BrowserPlatform.WIN32:
return 'Win';
case BrowserPlatform.WIN64:
return 'Win_x64';
}
}
export function resolveDownloadUrl(
platform: BrowserPlatform,
buildId: string,
baseUrl = 'https://storage.googleapis.com/chromium-browser-snapshots'
): string {
return `${baseUrl}/${folder(platform)}/${buildId}/${archive(
platform,
buildId
)}.zip`;
}
export function relativeExecutablePath(
platform: BrowserPlatform,
_buildId: string
): string {
switch (platform) {
case BrowserPlatform.MAC:
case BrowserPlatform.MAC_ARM:
return path.join(
'chrome-mac',
'Chromium.app',
'Contents',
'MacOS',
'Chromium'
);
case BrowserPlatform.LINUX:
return path.join('chrome-linux', 'chrome');
case BrowserPlatform.WIN32:
case BrowserPlatform.WIN64:
return path.join('chrome-win', 'chrome.exe');
}
}
export async function resolveBuildId(
platform: BrowserPlatform,
// We will need it for other channels/keywords.
_channel: 'latest' = 'latest'
): Promise<string> {
return new Promise((resolve, reject) => {
const request = httpRequest(
new URL(
`https://storage.googleapis.com/chromium-browser-snapshots/${folder(
platform
)}/LAST_CHANGE`
),
'GET',
response => {
let data = '';
if (response.statusCode && response.statusCode >= 400) {
return reject(new Error(`Got status code ${response.statusCode}`));
}
response.on('data', chunk => {
data += chunk;
});
response.on('end', () => {
try {
return resolve(String(data));
} catch {
return reject(new Error('Chrome version not found'));
}
});
},
false
);
request.on('error', err => {
reject(err);
});
});
}

View File

@ -48,7 +48,7 @@ export function httpRequest(
protocol: url.protocol, protocol: url.protocol,
hostname: url.hostname, hostname: url.hostname,
port: url.port, port: url.port,
path: url.pathname, path: url.pathname + url.search,
method, method,
headers: keepAlive ? {Connection: 'keep-alive'} : undefined, headers: keepAlive ? {Connection: 'keep-alive'} : undefined,
}; };

View File

@ -0,0 +1,120 @@
/**
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import assert from 'assert';
import path from 'path';
import {
BrowserPlatform,
ChromeReleaseChannel,
} from '../../../lib/cjs/browser-data/browser-data.js';
import {
resolveDownloadUrl,
relativeExecutablePath,
resolveSystemExecutablePath,
} 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.`));
});
});

View File

@ -18,41 +18,22 @@ import assert from 'assert';
import fs from 'fs'; import fs from 'fs';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import * as readline from 'readline';
import {Writable, Readable} from 'stream';
import {CLI} from '../../lib/cjs/CLI.js'; import {CLI} from '../../../lib/cjs/CLI.js';
import {Cache} from '../../lib/cjs/main.js'; import {createMockedReadlineInterface} from '../utils.js';
import {testChromeBuildId} from '../versions.js';
import {testChromeBuildId, testFirefoxBuildId} from './versions.js'; describe('Chrome CLI', function () {
describe('CLI', function () {
this.timeout(90000); this.timeout(90000);
let tmpDir = '/tmp/puppeteer-browsers-test'; let tmpDir = '/tmp/puppeteer-browsers-test';
function createMockedInterface(input: string) {
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,
});
}
beforeEach(() => { beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test')); tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
}); });
afterEach(async () => { afterEach(async () => {
new Cache(tmpDir).clear(); await new CLI(tmpDir, createMockedReadlineInterface('yes')).run([
await new CLI(tmpDir, createMockedInterface('yes')).run([
'npx', 'npx',
'@puppeteer/browsers', '@puppeteer/browsers',
'clear', 'clear',
@ -60,7 +41,7 @@ describe('CLI', function () {
]); ]);
}); });
it('should download Chromium binaries', async () => { it('should download Chrome binaries', async () => {
await new CLI(tmpDir).run([ await new CLI(tmpDir).run([
'npx', 'npx',
'@puppeteer/browsers', '@puppeteer/browsers',
@ -75,12 +56,13 @@ describe('CLI', function () {
tmpDir, tmpDir,
'chrome', 'chrome',
`linux-${testChromeBuildId}`, `linux-${testChromeBuildId}`,
'chrome-linux' 'chrome-linux64',
'chrome'
) )
) )
); );
await new CLI(tmpDir, createMockedInterface('no')).run([ await new CLI(tmpDir, createMockedReadlineInterface('no')).run([
'npx', 'npx',
'@puppeteer/browsers', '@puppeteer/browsers',
'clear', 'clear',
@ -92,40 +74,15 @@ describe('CLI', function () {
tmpDir, tmpDir,
'chrome', 'chrome',
`linux-${testChromeBuildId}`, `linux-${testChromeBuildId}`,
'chrome-linux' 'chrome-linux64',
'chrome'
) )
) )
); );
}); });
it('should download Firefox binaries', async () => { // Skipped because the current latest is not published yet.
await new CLI(tmpDir).run([ it.skip('should download latest Chrome binaries', async () => {
'npx',
'@puppeteer/browsers',
'install',
`firefox@${testFirefoxBuildId}`,
`--path=${tmpDir}`,
'--platform=linux',
]);
assert.ok(
fs.existsSync(
path.join(tmpDir, 'firefox', `linux-${testFirefoxBuildId}`, 'firefox')
)
);
});
it('should download latest Firefox binaries', async () => {
await new CLI(tmpDir).run([
'npx',
'@puppeteer/browsers',
'install',
`firefox@latest`,
`--path=${tmpDir}`,
'--platform=linux',
]);
});
it('should download latest Chrome binaries', async () => {
await new CLI(tmpDir).run([ await new CLI(tmpDir).run([
'npx', 'npx',
'@puppeteer/browsers', '@puppeteer/browsers',

View File

@ -27,15 +27,14 @@ import {
Browser, Browser,
BrowserPlatform, BrowserPlatform,
Cache, Cache,
} from '../../lib/cjs/main.js'; } from '../../../lib/cjs/main.js';
import {testChromeBuildId} from '../versions.js';
import {testChromeBuildId, testFirefoxBuildId} from './versions.js';
/** /**
* Tests in this spec use real download URLs and unpack live browser archives * Tests in this spec use real download URLs and unpack live browser archives
* so it requires the network access. * so it requires the network access.
*/ */
describe('fetch', () => { describe('Chrome fetch', () => {
let tmpDir = '/tmp/puppeteer-browsers-test'; let tmpDir = '/tmp/puppeteer-browsers-test';
beforeEach(() => { beforeEach(() => {
@ -96,47 +95,6 @@ describe('fetch', () => {
assert.ok(fs.existsSync(expectedOutputPath)); assert.ok(fs.existsSync(expectedOutputPath));
}); });
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 fetch({
cacheDir: tmpDir,
browser: Browser.FIREFOX,
platform: BrowserPlatform.LINUX,
buildId: testFirefoxBuildId,
});
assert.strictEqual(browser.path, expectedOutputPath);
assert.ok(fs.existsSync(expectedOutputPath));
});
// Fetch 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(120000);
const expectedOutputPath = path.join(
tmpDir,
'firefox',
`${BrowserPlatform.MAC}-${testFirefoxBuildId}`
);
assert.strictEqual(fs.existsSync(expectedOutputPath), false);
const browser = await fetch({
cacheDir: tmpDir,
browser: Browser.FIREFOX,
platform: BrowserPlatform.MAC,
buildId: testFirefoxBuildId,
});
assert.strictEqual(browser.path, expectedOutputPath);
assert.ok(fs.existsSync(expectedOutputPath));
}
);
describe('with proxy', () => { describe('with proxy', () => {
const proxyUrl = new URL(`http://localhost:54321`); const proxyUrl = new URL(`http://localhost:54321`);
let proxyServer: http.Server; let proxyServer: http.Server;
@ -205,7 +163,7 @@ describe('fetch', () => {
true true
); );
assert.deepStrictEqual(proxiedRequestUrls, [ assert.deepStrictEqual(proxiedRequestUrls, [
'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1083080/chrome-linux.zip', 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/linux64/chrome-linux64.zip',
]); ]);
}); });
@ -226,7 +184,7 @@ describe('fetch', () => {
assert.strictEqual(browser.path, expectedOutputPath); assert.strictEqual(browser.path, expectedOutputPath);
assert.ok(fs.existsSync(expectedOutputPath)); assert.ok(fs.existsSync(expectedOutputPath));
assert.deepStrictEqual(proxiedRequestUrls, [ assert.deepStrictEqual(proxiedRequestUrls, [
'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1083080/chrome-linux.zip', 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/linux64/chrome-linux64.zip',
]); ]);
}); });
}); });

View File

@ -27,11 +27,10 @@ import {
Browser, Browser,
BrowserPlatform, BrowserPlatform,
Cache, Cache,
} from '../../lib/cjs/main.js'; } from '../../../lib/cjs/main.js';
import {testChromeBuildId} from '../versions.js';
import {testChromeBuildId, testFirefoxBuildId} from './versions.js'; describe('Chrome', () => {
describe('launcher', () => {
it('should compute executable path for Chrome', () => { it('should compute executable path for Chrome', () => {
assert.strictEqual( assert.strictEqual(
computeExecutablePath({ computeExecutablePath({
@ -40,35 +39,11 @@ describe('launcher', () => {
buildId: '123', buildId: '123',
cacheDir: 'cache', cacheDir: 'cache',
}), }),
path.join('cache', 'chrome', 'linux-123', 'chrome-linux', 'chrome') path.join('cache', 'chrome', 'linux-123', 'chrome-linux64', 'chrome')
); );
}); });
it('should compute executable path for Chromium', () => { describe('launcher', function () {
assert.strictEqual(
computeExecutablePath({
browser: Browser.CHROMIUM,
platform: BrowserPlatform.LINUX,
buildId: '123',
cacheDir: 'cache',
}),
path.join('cache', 'chromium', 'linux-123', 'chrome-linux', 'chrome')
);
});
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('Chrome', function () {
this.timeout(60000); this.timeout(60000);
let tmpDir = '/tmp/puppeteer-browsers-test'; let tmpDir = '/tmp/puppeteer-browsers-test';
@ -127,38 +102,4 @@ describe('launcher', () => {
assert.ok(url.startsWith('ws://127.0.0.1:9222/devtools/browser')); assert.ok(url.startsWith('ws://127.0.0.1:9222/devtools/browser'));
}); });
}); });
describe('Firefox', function () {
this.timeout(60000);
let tmpDir = '/tmp/puppeteer-browsers-test';
beforeEach(async () => {
tmpDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'puppeteer-browsers-test')
);
await fetch({
cacheDir: tmpDir,
browser: Browser.FIREFOX,
buildId: testFirefoxBuildId,
});
});
afterEach(() => {
new Cache(tmpDir).clear();
});
it('should launch a Firefox browser', async () => {
const executablePath = computeExecutablePath({
cacheDir: tmpDir,
browser: Browser.FIREFOX,
buildId: testFirefoxBuildId,
});
const process = launch({
executablePath,
args: [`--user-data-dir=${path.join(tmpDir, 'profile')}`],
});
await process.close();
});
});
}); });

View File

@ -20,14 +20,14 @@ import path from 'path';
import { import {
BrowserPlatform, BrowserPlatform,
ChromeReleaseChannel, ChromeReleaseChannel,
} from '../../lib/cjs/browser-data/browser-data.js'; } from '../../../lib/cjs/browser-data/browser-data.js';
import { import {
resolveDownloadUrl, resolveDownloadUrl,
relativeExecutablePath, relativeExecutablePath,
resolveSystemExecutablePath, resolveSystemExecutablePath,
} from '../../lib/cjs/browser-data/chrome.js'; } from '../../../lib/cjs/browser-data/chromium.js';
describe('Chrome', () => { describe('Chromium', () => {
it('should resolve download URLs', () => { it('should resolve download URLs', () => {
assert.strictEqual( assert.strictEqual(
resolveDownloadUrl(BrowserPlatform.LINUX, '1083080'), resolveDownloadUrl(BrowserPlatform.LINUX, '1083080'),

View File

@ -0,0 +1,105 @@
/**
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import assert from 'assert';
import fs from 'fs';
import os from 'os';
import path from 'path';
import {
CDP_WEBSOCKET_ENDPOINT_REGEX,
computeExecutablePath,
launch,
fetch,
Browser,
BrowserPlatform,
Cache,
} from '../../../lib/cjs/main.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 () {
this.timeout(60000);
let tmpDir = '/tmp/puppeteer-browsers-test';
beforeEach(async () => {
tmpDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'puppeteer-browsers-test')
);
await fetch({
cacheDir: tmpDir,
browser: Browser.CHROMIUM,
buildId: testChromiumBuildId,
});
});
afterEach(() => {
new Cache(tmpDir).clear();
});
it('should launch a Chrome browser', async () => {
const executablePath = computeExecutablePath({
cacheDir: tmpDir,
browser: Browser.CHROMIUM,
buildId: testChromiumBuildId,
});
const process = launch({
executablePath,
args: [
'--headless=new',
'--use-mock-keychain',
'--disable-features=DialMediaRouteProvider',
`--user-data-dir=${path.join(tmpDir, 'profile')}`,
],
});
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: [
'--headless=new',
'--use-mock-keychain',
'--disable-features=DialMediaRouteProvider',
'--remote-debugging-port=9222',
`--user-data-dir=${path.join(tmpDir, 'profile')}`,
],
});
const url = await process.waitForLineOutput(CDP_WEBSOCKET_ENDPOINT_REGEX);
await process.close();
assert.ok(url.startsWith('ws://127.0.0.1:9222/devtools/browser'));
});
});
});

View File

@ -0,0 +1,70 @@
/**
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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} from '../utils.js';
import {testFirefoxBuildId} from '../versions.js';
describe('Firefox CLI', function () {
this.timeout(90000);
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}`,
]);
});
it('should download Firefox binaries', async () => {
await new CLI(tmpDir).run([
'npx',
'@puppeteer/browsers',
'install',
`firefox@${testFirefoxBuildId}`,
`--path=${tmpDir}`,
'--platform=linux',
]);
assert.ok(
fs.existsSync(
path.join(tmpDir, 'firefox', `linux-${testFirefoxBuildId}`, 'firefox')
)
);
});
it('should download latest Firefox binaries', async () => {
await new CLI(tmpDir).run([
'npx',
'@puppeteer/browsers',
'install',
`firefox@latest`,
`--path=${tmpDir}`,
'--platform=linux',
]);
});
});

View File

@ -0,0 +1,80 @@
/**
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import assert from 'assert';
import fs from 'fs';
import os from 'os';
import path from 'path';
import {fetch, Browser, BrowserPlatform, Cache} from '../../../lib/cjs/main.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 fetch', () => {
let tmpDir = '/tmp/puppeteer-browsers-test';
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
});
afterEach(() => {
new Cache(tmpDir).clear();
});
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 fetch({
cacheDir: tmpDir,
browser: Browser.FIREFOX,
platform: BrowserPlatform.LINUX,
buildId: testFirefoxBuildId,
});
assert.strictEqual(browser.path, expectedOutputPath);
assert.ok(fs.existsSync(expectedOutputPath));
});
// Fetch 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(120000);
const expectedOutputPath = path.join(
tmpDir,
'firefox',
`${BrowserPlatform.MAC}-${testFirefoxBuildId}`
);
assert.strictEqual(fs.existsSync(expectedOutputPath), false);
const browser = await fetch({
cacheDir: tmpDir,
browser: Browser.FIREFOX,
platform: BrowserPlatform.MAC,
buildId: testFirefoxBuildId,
});
assert.strictEqual(browser.path, expectedOutputPath);
assert.ok(fs.existsSync(expectedOutputPath));
}
);
});

View File

@ -19,12 +19,12 @@ import fs from 'fs';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import {BrowserPlatform} from '../../lib/cjs/browser-data/browser-data.js'; import {BrowserPlatform} from '../../../lib/cjs/browser-data/browser-data.js';
import { import {
createProfile, createProfile,
relativeExecutablePath, relativeExecutablePath,
resolveDownloadUrl, resolveDownloadUrl,
} from '../../lib/cjs/browser-data/firefox.js'; } from '../../../lib/cjs/browser-data/firefox.js';
describe('Firefox', () => { describe('Firefox', () => {
it('should resolve download URLs', () => { it('should resolve download URLs', () => {

View File

@ -0,0 +1,78 @@
/**
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import assert from 'assert';
import fs from 'fs';
import os from 'os';
import path from 'path';
import {
computeExecutablePath,
launch,
fetch,
Browser,
BrowserPlatform,
Cache,
} from '../../../lib/cjs/main.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(60000);
let tmpDir = '/tmp/puppeteer-browsers-test';
beforeEach(async () => {
tmpDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'puppeteer-browsers-test')
);
await fetch({
cacheDir: tmpDir,
browser: Browser.FIREFOX,
buildId: testFirefoxBuildId,
});
});
afterEach(() => {
new Cache(tmpDir).clear();
});
it('should launch a Firefox browser', async () => {
const executablePath = computeExecutablePath({
cacheDir: tmpDir,
browser: Browser.FIREFOX,
buildId: testFirefoxBuildId,
});
const process = launch({
executablePath,
args: [`--user-data-dir=${path.join(tmpDir, 'profile')}`],
});
await process.close();
});
});
});

View File

@ -0,0 +1,35 @@
/**
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as readline from 'readline';
import {Writable, Readable} from 'stream';
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,
});
}

View File

@ -14,7 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
export const testChromeBuildId = '1083080'; export const testChromeBuildId = '113.0.5672.0';
export const testChromiumBuildId = '1083080';
// TODO: We can add a Cron job to auto-update on change. // TODO: We can add a Cron job to auto-update on change.
// Firefox keeps only `latest` version of Nightly builds. // Firefox keeps only `latest` version of Nightly builds.
export const testFirefoxBuildId = '112.0a1'; export const testFirefoxBuildId = '113.0a1';