From bec2357aeedda42cfaf3096c6293c2f49ceb825e Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 10 Aug 2023 13:00:22 +0200 Subject: [PATCH] feat: allow installing chrome/chromedriver by milestone and version prefix (#10720) --- packages/browsers/src/CLI.ts | 26 +++++++- .../browsers/src/browser-data/browser-data.ts | 45 +++++++------- packages/browsers/src/browser-data/chrome.ts | 61 ++++++++++++++++++- .../browsers/src/browser-data/chromedriver.ts | 10 +-- .../test/src/chrome/chrome-data.spec.ts | 9 +++ .../chromedriver/chromedriver-data.spec.ts | 9 +++ test/TestExpectations.json | 24 ++++---- 7 files changed, 136 insertions(+), 48 deletions(-) diff --git a/packages/browsers/src/CLI.ts b/packages/browsers/src/CLI.ts index 5e6b0c74..5aa10b21 100644 --- a/packages/browsers/src/CLI.ts +++ b/packages/browsers/src/CLI.ts @@ -132,6 +132,26 @@ export class CLI { '$0 install chrome@latest', 'Install the latest available build for the Chrome browser.' ); + yargs.example( + '$0 install chrome@canary', + 'Install the latest available build for the Chrome Canary browser.' + ); + yargs.example( + '$0 install chrome@115', + 'Install the latest available build for Chrome 115.' + ); + yargs.example( + '$0 install chromedriver@canary', + 'Install the latest available build for ChromeDriver Canary.' + ); + yargs.example( + '$0 install chromedriver@115', + 'Install the latest available build for ChromeDriver 115.' + ); + yargs.example( + '$0 install chromedriver@115.0.5790', + 'Install the latest available patch (115.0.5790.X) build for ChromeDriver.' + ); yargs.example( '$0 install chromium@1083080', 'Install the revision 1083080 of the Chromium browser.' @@ -201,15 +221,15 @@ export class CLI { default: false, }); yargs.example( - '$0 launch chrome@1083080', - 'Launch the Chrome browser identified by the revision 1083080.' + '$0 launch chrome@115.0.5790.170', + 'Launch Chrome 115.0.5790.170' ); yargs.example( '$0 launch firefox@112.0a1', 'Launch the Firefox browser identified by the milestone 112.0a1.' ); yargs.example( - '$0 launch chrome@1083080 --detached', + '$0 launch chrome@115.0.5790.170 --detached', 'Launch the browser but detach the sub-processes.' ); yargs.example( diff --git a/packages/browsers/src/browser-data/browser-data.ts b/packages/browsers/src/browser-data/browser-data.ts index ac270cc5..ff02ab47 100644 --- a/packages/browsers/src/browser-data/browser-data.ts +++ b/packages/browsers/src/browser-data/browser-data.ts @@ -72,36 +72,28 @@ export async function resolveBuildId( `${tag} is not supported for ${browser}. Use 'latest' instead.` ); } - case Browser.CHROME: + case Browser.CHROME: { switch (tag as BrowserTag) { case BrowserTag.LATEST: - return await chrome.resolveBuildId( - platform, - ChromeReleaseChannel.CANARY - ); + return await chrome.resolveBuildId(ChromeReleaseChannel.CANARY); case BrowserTag.BETA: - return await chrome.resolveBuildId( - platform, - ChromeReleaseChannel.BETA - ); + return await chrome.resolveBuildId(ChromeReleaseChannel.BETA); case BrowserTag.CANARY: - return await chrome.resolveBuildId( - platform, - ChromeReleaseChannel.CANARY - ); + return await chrome.resolveBuildId(ChromeReleaseChannel.CANARY); case BrowserTag.DEV: - return await chrome.resolveBuildId( - platform, - ChromeReleaseChannel.DEV - ); + return await chrome.resolveBuildId(ChromeReleaseChannel.DEV); case BrowserTag.STABLE: - return await chrome.resolveBuildId( - platform, - ChromeReleaseChannel.STABLE - ); + return await chrome.resolveBuildId(ChromeReleaseChannel.STABLE); + default: + const result = await chrome.resolveBuildId(tag); + if (result) { + return result; + } } - case Browser.CHROMEDRIVER: - switch (tag as BrowserTag) { + return tag; + } + case Browser.CHROMEDRIVER: { + switch (tag) { case BrowserTag.LATEST: case BrowserTag.CANARY: return await chromedriver.resolveBuildId(ChromeReleaseChannel.CANARY); @@ -111,7 +103,14 @@ export async function resolveBuildId( return await chromedriver.resolveBuildId(ChromeReleaseChannel.DEV); case BrowserTag.STABLE: return await chromedriver.resolveBuildId(ChromeReleaseChannel.STABLE); + default: + const result = await chromedriver.resolveBuildId(tag); + if (result) { + return result; + } } + return tag; + } case Browser.CHROMIUM: switch (tag as BrowserTag) { case BrowserTag.LATEST: diff --git a/packages/browsers/src/browser-data/chrome.ts b/packages/browsers/src/browser-data/chrome.ts index 7ccb31ee..30df6768 100644 --- a/packages/browsers/src/browser-data/chrome.ts +++ b/packages/browsers/src/browser-data/chrome.ts @@ -97,11 +97,66 @@ export async function getLastKnownGoodReleaseForChannel( ).channels[channel]; } +export async function getLastKnownGoodReleaseForMilestone( + milestone: string +): Promise<{version: string; revision: string} | undefined> { + const data = (await getJSON( + new URL( + 'https://googlechromelabs.github.io/chrome-for-testing/latest-versions-per-milestone.json' + ) + )) as { + milestones: Record; + }; + return data.milestones[milestone] as + | {version: string; revision: string} + | undefined; +} + +export async function getLastKnownGoodReleaseForBuild( + /** + * @example `112.0.23`, + */ + buildPrefix: string +): Promise<{version: string; revision: string} | undefined> { + const data = (await getJSON( + new URL( + 'https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build.json' + ) + )) as { + builds: Record; + }; + return data.builds[buildPrefix] as + | {version: string; revision: string} + | undefined; +} + export async function resolveBuildId( - _platform: BrowserPlatform, channel: ChromeReleaseChannel -): Promise { - return (await getLastKnownGoodReleaseForChannel(channel)).version; +): Promise; +export async function resolveBuildId( + channel: string +): Promise; +export async function resolveBuildId( + channel: ChromeReleaseChannel | string +): Promise { + if ( + Object.values(ChromeReleaseChannel).includes( + channel as ChromeReleaseChannel + ) + ) { + return ( + await getLastKnownGoodReleaseForChannel(channel as ChromeReleaseChannel) + ).version; + } + if (channel.match(/^\d+$/)) { + // Potentially a milestone. + return (await getLastKnownGoodReleaseForMilestone(channel))?.version; + } + if (channel.match(/^\d+\.\d+\.\d+$/)) { + // Potentially a build prefix without the patch version. + return (await getLastKnownGoodReleaseForBuild(channel))?.version; + } + return; } export function resolveSystemExecutablePath( diff --git a/packages/browsers/src/browser-data/chromedriver.ts b/packages/browsers/src/browser-data/chromedriver.ts index c5c318ef..0518d2ee 100644 --- a/packages/browsers/src/browser-data/chromedriver.ts +++ b/packages/browsers/src/browser-data/chromedriver.ts @@ -15,8 +15,7 @@ */ import path from 'path'; -import {getLastKnownGoodReleaseForChannel} from './chrome.js'; -import {BrowserPlatform, ChromeReleaseChannel} from './types.js'; +import {BrowserPlatform} from './types.js'; function folder(platform: BrowserPlatform): string { switch (platform) { @@ -63,8 +62,5 @@ export function relativeExecutablePath( return path.join('chromedriver-' + folder(platform), 'chromedriver.exe'); } } -export async function resolveBuildId( - channel: ChromeReleaseChannel -): Promise { - return (await getLastKnownGoodReleaseForChannel(channel)).version; -} + +export {resolveBuildId} from './chrome.js'; diff --git a/packages/browsers/test/src/chrome/chrome-data.spec.ts b/packages/browsers/test/src/chrome/chrome-data.spec.ts index c0404baa..ef06380f 100644 --- a/packages/browsers/test/src/chrome/chrome-data.spec.ts +++ b/packages/browsers/test/src/chrome/chrome-data.spec.ts @@ -25,6 +25,7 @@ import { resolveDownloadUrl, relativeExecutablePath, resolveSystemExecutablePath, + resolveBuildId, } from '../../../lib/cjs/browser-data/chrome.js'; describe('Chrome', () => { @@ -117,4 +118,12 @@ describe('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/packages/browsers/test/src/chromedriver/chromedriver-data.spec.ts b/packages/browsers/test/src/chromedriver/chromedriver-data.spec.ts index e29f1a81..0b2cf4e8 100644 --- a/packages/browsers/test/src/chromedriver/chromedriver-data.spec.ts +++ b/packages/browsers/test/src/chromedriver/chromedriver-data.spec.ts @@ -21,6 +21,7 @@ import {BrowserPlatform} from '../../../lib/cjs/browser-data/browser-data.js'; import { resolveDownloadUrl, relativeExecutablePath, + resolveBuildId, } from '../../../lib/cjs/browser-data/chromedriver.js'; describe('ChromeDriver', () => { @@ -47,6 +48,14 @@ describe('ChromeDriver', () => { ); }); + 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'), diff --git a/test/TestExpectations.json b/test/TestExpectations.json index 362daad7..35eedc20 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -689,6 +689,18 @@ "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should evaluate before anything else on the page", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should work with CSP", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[evaluation.spec] Evaluation specs Page.removeScriptToEvaluateOnNewDocument *", "platforms": ["darwin", "linux", "win32"], @@ -4150,17 +4162,5 @@ "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "chrome", "headless"], "expectations": ["FAIL", "PASS"] - }, - { - "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should evaluate before anything else on the page", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["PASS"] - }, - { - "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should work with CSP", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["PASS"] } ]