summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/packages/browsers/src
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/packages/browsers/src')
-rw-r--r--remote/test/puppeteer/packages/browsers/src/CLI.ts17
-rw-r--r--remote/test/puppeteer/packages/browsers/src/Cache.ts63
-rw-r--r--remote/test/puppeteer/packages/browsers/src/browser-data/browser-data.ts20
-rw-r--r--remote/test/puppeteer/packages/browsers/src/browser-data/chrome-headless-shell.ts4
-rw-r--r--remote/test/puppeteer/packages/browsers/src/browser-data/chrome.ts20
-rw-r--r--remote/test/puppeteer/packages/browsers/src/browser-data/chromedriver.ts4
-rw-r--r--remote/test/puppeteer/packages/browsers/src/browser-data/chromium.ts4
-rw-r--r--remote/test/puppeteer/packages/browsers/src/browser-data/firefox.ts9
-rw-r--r--remote/test/puppeteer/packages/browsers/src/install.ts118
-rw-r--r--remote/test/puppeteer/packages/browsers/src/main.ts1
10 files changed, 233 insertions, 27 deletions
diff --git a/remote/test/puppeteer/packages/browsers/src/CLI.ts b/remote/test/puppeteer/packages/browsers/src/CLI.ts
index 255f5545b4..281f22c9f5 100644
--- a/remote/test/puppeteer/packages/browsers/src/CLI.ts
+++ b/remote/test/puppeteer/packages/browsers/src/CLI.ts
@@ -173,6 +173,18 @@ export class CLI {
'Install the latest available build for the Chrome browser.'
);
yargs.example(
+ '$0 install chrome@stable',
+ 'Install the latest available build for the Chrome browser from the stable channel.'
+ );
+ yargs.example(
+ '$0 install chrome@beta',
+ 'Install the latest available build for the Chrome browser from the beta channel.'
+ );
+ yargs.example(
+ '$0 install chrome@dev',
+ 'Install the latest available build for the Chrome browser from the dev channel.'
+ );
+ yargs.example(
'$0 install chrome@canary',
'Install the latest available build for the Chrome Canary browser.'
);
@@ -238,6 +250,7 @@ export class CLI {
}
args.browser.buildId = pinnedVersion;
}
+ const originalBuildId = args.browser.buildId;
args.browser.buildId = await resolveBuildId(
args.browser.name,
args.platform,
@@ -253,6 +266,10 @@ export class CLI {
args.browser.buildId
),
baseUrl: args.baseUrl,
+ buildIdAlias:
+ originalBuildId !== args.browser.buildId
+ ? originalBuildId
+ : undefined,
});
console.log(
`${args.browser.name}@${
diff --git a/remote/test/puppeteer/packages/browsers/src/Cache.ts b/remote/test/puppeteer/packages/browsers/src/Cache.ts
index 13b465835a..e6b574d9dd 100644
--- a/remote/test/puppeteer/packages/browsers/src/Cache.ts
+++ b/remote/test/puppeteer/packages/browsers/src/Cache.ts
@@ -8,13 +8,18 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
+import debug from 'debug';
+
import {
Browser,
type BrowserPlatform,
executablePathByBrowser,
+ getVersionComparator,
} from './browser-data/browser-data.js';
import {detectBrowserPlatform} from './detectPlatform.js';
+const debugCache = debug('puppeteer:browsers:cache');
+
/**
* @public
*/
@@ -57,6 +62,14 @@ export class InstalledBrowser {
this.buildId
);
}
+
+ readMetadata(): Metadata {
+ return this.#cache.readMetadata(this.browser);
+ }
+
+ writeMetadata(metadata: Metadata): void {
+ this.#cache.writeMetadata(this.browser, metadata);
+ }
}
/**
@@ -80,6 +93,11 @@ export interface ComputeExecutablePathOptions {
buildId: string;
}
+export interface Metadata {
+ // Maps an alias (canary/latest/dev/etc.) to a buildId.
+ aliases: Record<string, string>;
+}
+
/**
* The cache used by Puppeteer relies on the following structure:
*
@@ -112,6 +130,39 @@ export class Cache {
return path.join(this.#rootDir, browser);
}
+ metadataFile(browser: Browser): string {
+ return path.join(this.browserRoot(browser), '.metadata');
+ }
+
+ readMetadata(browser: Browser): Metadata {
+ const metatadaPath = this.metadataFile(browser);
+ if (!fs.existsSync(metatadaPath)) {
+ return {aliases: {}};
+ }
+ // TODO: add type-safe parsing.
+ const data = JSON.parse(fs.readFileSync(metatadaPath, 'utf8'));
+ if (typeof data !== 'object') {
+ throw new Error('.metadata is not an object');
+ }
+ return data;
+ }
+
+ writeMetadata(browser: Browser, metadata: Metadata): void {
+ const metatadaPath = this.metadataFile(browser);
+ fs.mkdirSync(path.dirname(metatadaPath), {recursive: true});
+ fs.writeFileSync(metatadaPath, JSON.stringify(metadata, null, 2));
+ }
+
+ resolveAlias(browser: Browser, alias: string): string | undefined {
+ const metadata = this.readMetadata(browser);
+ if (alias === 'latest') {
+ return Object.values(metadata.aliases || {})
+ .sort(getVersionComparator(browser))
+ .at(-1);
+ }
+ return metadata.aliases[alias];
+ }
+
installationDir(
browser: Browser,
platform: BrowserPlatform,
@@ -134,6 +185,12 @@ export class Cache {
platform: BrowserPlatform,
buildId: string
): void {
+ const metadata = this.readMetadata(browser);
+ for (const alias of Object.keys(metadata.aliases)) {
+ if (metadata.aliases[alias] === buildId) {
+ delete metadata.aliases[alias];
+ }
+ }
fs.rmSync(this.installationDir(browser, platform, buildId), {
force: true,
recursive: true,
@@ -180,6 +237,12 @@ export class Cache {
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
);
}
+ try {
+ options.buildId =
+ this.resolveAlias(options.browser, options.buildId) ?? options.buildId;
+ } catch {
+ debugCache('could not read .metadata file for the browser');
+ }
const installationDir = this.installationDir(
options.browser,
options.platform,
diff --git a/remote/test/puppeteer/packages/browsers/src/browser-data/browser-data.ts b/remote/test/puppeteer/packages/browsers/src/browser-data/browser-data.ts
index 67bb4990b2..3e78030aa7 100644
--- a/remote/test/puppeteer/packages/browsers/src/browser-data/browser-data.ts
+++ b/remote/test/puppeteer/packages/browsers/src/browser-data/browser-data.ts
@@ -43,6 +43,14 @@ export const executablePathByBrowser = {
[Browser.FIREFOX]: firefox.relativeExecutablePath,
};
+export const versionComparators = {
+ [Browser.CHROMEDRIVER]: chromedriver.compareVersions,
+ [Browser.CHROMEHEADLESSSHELL]: chromeHeadlessShell.compareVersions,
+ [Browser.CHROME]: chrome.compareVersions,
+ [Browser.CHROMIUM]: chromium.compareVersions,
+ [Browser.FIREFOX]: firefox.compareVersions,
+};
+
export {Browser, BrowserPlatform, ChromeReleaseChannel};
/**
@@ -185,3 +193,15 @@ export function resolveSystemExecutablePath(
return chrome.resolveSystemExecutablePath(platform, channel);
}
}
+
+/**
+ * Returns a version comparator for the given browser that can be used to sort
+ * browser versions.
+ *
+ * @public
+ */
+export function getVersionComparator(
+ browser: Browser
+): (a: string, b: string) => number {
+ return versionComparators[browser];
+}
diff --git a/remote/test/puppeteer/packages/browsers/src/browser-data/chrome-headless-shell.ts b/remote/test/puppeteer/packages/browsers/src/browser-data/chrome-headless-shell.ts
index b1c6178de8..f5f65cdbd4 100644
--- a/remote/test/puppeteer/packages/browsers/src/browser-data/chrome-headless-shell.ts
+++ b/remote/test/puppeteer/packages/browsers/src/browser-data/chrome-headless-shell.ts
@@ -25,7 +25,7 @@ function folder(platform: BrowserPlatform): string {
export function resolveDownloadUrl(
platform: BrowserPlatform,
buildId: string,
- baseUrl = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'
+ baseUrl = 'https://storage.googleapis.com/chrome-for-testing-public'
): string {
return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`;
}
@@ -66,4 +66,4 @@ export function relativeExecutablePath(
}
}
-export {resolveBuildId} from './chrome.js';
+export {resolveBuildId, compareVersions} from './chrome.js';
diff --git a/remote/test/puppeteer/packages/browsers/src/browser-data/chrome.ts b/remote/test/puppeteer/packages/browsers/src/browser-data/chrome.ts
index c6329255c3..c7975163fc 100644
--- a/remote/test/puppeteer/packages/browsers/src/browser-data/chrome.ts
+++ b/remote/test/puppeteer/packages/browsers/src/browser-data/chrome.ts
@@ -6,6 +6,8 @@
import path from 'path';
+import semver from 'semver';
+
import {getJSON} from '../httpUtil.js';
import {BrowserPlatform, ChromeReleaseChannel} from './types.js';
@@ -28,7 +30,7 @@ function folder(platform: BrowserPlatform): string {
export function resolveDownloadUrl(
platform: BrowserPlatform,
buildId: string,
- baseUrl = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'
+ baseUrl = 'https://storage.googleapis.com/chrome-for-testing-public'
): string {
return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`;
}
@@ -193,3 +195,19 @@ export function resolveSystemExecutablePath(
`Unable to detect browser executable path for '${channel}' on ${platform}.`
);
}
+
+export function compareVersions(a: string, b: string): number {
+ if (!semver.valid(a)) {
+ throw new Error(`Version ${a} is not a valid semver version`);
+ }
+ if (!semver.valid(b)) {
+ throw new Error(`Version ${b} is not a valid semver version`);
+ }
+ if (semver.gt(a, b)) {
+ return 1;
+ } else if (semver.lt(a, b)) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
diff --git a/remote/test/puppeteer/packages/browsers/src/browser-data/chromedriver.ts b/remote/test/puppeteer/packages/browsers/src/browser-data/chromedriver.ts
index 290598d0d7..2f1242b249 100644
--- a/remote/test/puppeteer/packages/browsers/src/browser-data/chromedriver.ts
+++ b/remote/test/puppeteer/packages/browsers/src/browser-data/chromedriver.ts
@@ -25,7 +25,7 @@ function folder(platform: BrowserPlatform): string {
export function resolveDownloadUrl(
platform: BrowserPlatform,
buildId: string,
- baseUrl = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'
+ baseUrl = 'https://storage.googleapis.com/chrome-for-testing-public'
): string {
return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`;
}
@@ -53,4 +53,4 @@ export function relativeExecutablePath(
}
}
-export {resolveBuildId} from './chrome.js';
+export {resolveBuildId, compareVersions} from './chrome.js';
diff --git a/remote/test/puppeteer/packages/browsers/src/browser-data/chromium.ts b/remote/test/puppeteer/packages/browsers/src/browser-data/chromium.ts
index 09cfc987a8..820e76d018 100644
--- a/remote/test/puppeteer/packages/browsers/src/browser-data/chromium.ts
+++ b/remote/test/puppeteer/packages/browsers/src/browser-data/chromium.ts
@@ -86,3 +86,7 @@ export async function resolveBuildId(
)
);
}
+
+export function compareVersions(a: string, b: string): number {
+ return Number(a) - Number(b);
+}
diff --git a/remote/test/puppeteer/packages/browsers/src/browser-data/firefox.ts b/remote/test/puppeteer/packages/browsers/src/browser-data/firefox.ts
index ccc30fa1b5..87e3804c8f 100644
--- a/remote/test/puppeteer/packages/browsers/src/browser-data/firefox.ts
+++ b/remote/test/puppeteer/packages/browsers/src/browser-data/firefox.ts
@@ -235,10 +235,6 @@ function defaultProfilePreferences(
// Disable the GFX sanity window
'media.sanity-test.disabled': true,
- // Prevent various error message on the console
- // jest-puppeteer asserts that no error message is emitted by the console
- 'network.cookie.cookieBehavior': 0,
-
// Disable experimental feature that is only available in Nightly
'network.cookie.sameSite.laxByDefault': false,
@@ -328,3 +324,8 @@ async function writePreferences(options: ProfileOptions): Promise<void> {
await fs.promises.copyFile(prefsPath, prefsBackupPath);
}
}
+
+export function compareVersions(a: string, b: string): number {
+ // TODO: this is a not very reliable check.
+ return parseInt(a.replace('.', ''), 16) - parseInt(b.replace('.', ''), 16);
+}
diff --git a/remote/test/puppeteer/packages/browsers/src/install.ts b/remote/test/puppeteer/packages/browsers/src/install.ts
index 375c75babc..e78b2c3461 100644
--- a/remote/test/puppeteer/packages/browsers/src/install.ts
+++ b/remote/test/puppeteer/packages/browsers/src/install.ts
@@ -11,15 +11,15 @@ import os from 'os';
import path from 'path';
import {
- type Browser,
- type BrowserPlatform,
+ Browser,
+ BrowserPlatform,
downloadUrls,
} from './browser-data/browser-data.js';
import {Cache, InstalledBrowser} from './Cache.js';
import {debug} from './debug.js';
import {detectBrowserPlatform} from './detectPlatform.js';
import {unpackArchive} from './fileUtil.js';
-import {downloadFile, headHttpRequest} from './httpUtil.js';
+import {downloadFile, getJSON, headHttpRequest} from './httpUtil.js';
const debugInstall = debug('puppeteer:browsers:install');
@@ -63,6 +63,13 @@ export interface InstallOptions {
*/
buildId: string;
/**
+ * An alias for the provided `buildId`. It will be used to maintain local
+ * metadata to support aliases in the `launch` command.
+ *
+ * @example 'canary'
+ */
+ buildIdAlias?: string;
+ /**
* Provides information about the progress of the download.
*/
downloadProgressCallback?: (
@@ -74,7 +81,7 @@ export interface InstallOptions {
*
* @defaultValue Either
*
- * - https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing or
+ * - https://storage.googleapis.com/chrome-for-testing-public or
* - https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central
*
*/
@@ -115,6 +122,68 @@ export async function install(
options.buildId,
options.baseUrl
);
+ try {
+ return await installUrl(url, options);
+ } catch (err) {
+ debugInstall(`Error downloading from ${url}.`);
+ switch (options.browser) {
+ case Browser.CHROME:
+ case Browser.CHROMEDRIVER:
+ case Browser.CHROMEHEADLESSSHELL: {
+ debugInstall(
+ `Trying to find download URL via https://googlechromelabs.github.io/chrome-for-testing.`
+ );
+ interface Version {
+ downloads: Record<string, Array<{platform: string; url: string}>>;
+ }
+ const version = (await getJSON(
+ new URL(
+ `https://googlechromelabs.github.io/chrome-for-testing/${options.buildId}.json`
+ )
+ )) as Version;
+ let platform = '';
+ switch (options.platform) {
+ case BrowserPlatform.LINUX:
+ platform = 'linux64';
+ break;
+ case BrowserPlatform.MAC_ARM:
+ platform = 'mac-arm64';
+ break;
+ case BrowserPlatform.MAC:
+ platform = 'mac-x64';
+ break;
+ case BrowserPlatform.WIN32:
+ platform = 'win32';
+ break;
+ case BrowserPlatform.WIN64:
+ platform = 'win64';
+ break;
+ }
+ const url = version.downloads[options.browser]?.find(link => {
+ return link['platform'] === platform;
+ })?.url;
+ if (url) {
+ debugInstall(`Falling back to downloading from ${url}.`);
+ return await installUrl(new URL(url), options);
+ }
+ throw err;
+ }
+ default:
+ throw err;
+ }
+ }
+}
+
+async function installUrl(
+ url: URL,
+ options: InstallOptions
+): Promise<InstalledBrowser | string> {
+ options.platform ??= detectBrowserPlatform();
+ if (!options.platform) {
+ throw new Error(
+ `Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
+ );
+ }
const fileName = url.toString().split('/').pop();
assert(fileName, `A malformed download URL was found: ${url}.`);
const cache = new Cache(options.cacheDir);
@@ -140,15 +209,22 @@ export async function install(
options.platform,
options.buildId
);
- if (existsSync(outputPath)) {
- return new InstalledBrowser(
- cache,
- options.browser,
- options.buildId,
- options.platform
- );
- }
+
try {
+ if (existsSync(outputPath)) {
+ const installedBrowser = new InstalledBrowser(
+ cache,
+ options.browser,
+ options.buildId,
+ options.platform
+ );
+ if (!existsSync(installedBrowser.executablePath)) {
+ throw new Error(
+ `The browser folder (${outputPath}) exists but the executable (${installedBrowser.executablePath}) is missing`
+ );
+ }
+ return installedBrowser;
+ }
debugInstall(`Downloading binary from ${url}`);
try {
debugTime('download');
@@ -164,17 +240,23 @@ export async function install(
} finally {
debugTimeEnd('extract');
}
+ const installedBrowser = new InstalledBrowser(
+ cache,
+ options.browser,
+ options.buildId,
+ options.platform
+ );
+ if (options.buildIdAlias) {
+ const metadata = installedBrowser.readMetadata();
+ metadata.aliases[options.buildIdAlias] = options.buildId;
+ installedBrowser.writeMetadata(metadata);
+ }
+ return installedBrowser;
} finally {
if (existsSync(archivePath)) {
await unlink(archivePath);
}
}
- return new InstalledBrowser(
- cache,
- options.browser,
- options.buildId,
- options.platform
- );
}
/**
diff --git a/remote/test/puppeteer/packages/browsers/src/main.ts b/remote/test/puppeteer/packages/browsers/src/main.ts
index df93de530d..c03c0c0090 100644
--- a/remote/test/puppeteer/packages/browsers/src/main.ts
+++ b/remote/test/puppeteer/packages/browsers/src/main.ts
@@ -37,6 +37,7 @@ export {
BrowserPlatform,
ChromeReleaseChannel,
createProfile,
+ getVersionComparator,
} from './browser-data/browser-data.js';
export {CLI, makeProgressCallback} from './CLI.js';
export {Cache, InstalledBrowser} from './Cache.js';