From 87522e778a6487111931458755e701f1c4b717d9 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Thu, 20 Jul 2023 10:53:15 +0200 Subject: [PATCH] feat: add executablePath to InstalledBrowser (#10594) --- docs/browsers-api/browsers.install.md | 10 +-- docs/browsers-api/browsers.install_1.md | 25 ++++++++ .../browsers-api/browsers.installedbrowser.md | 21 ++++--- docs/browsers-api/index.md | 13 ++-- packages/browsers/src/Cache.ts | 63 +++++++++++++++---- packages/browsers/src/install.ts | 52 +++++++-------- .../browsers/test/src/chrome/install.spec.ts | 4 ++ .../test/src/chromedriver/install.spec.ts | 1 + .../browsers/tools/downloadTestBrowsers.mjs | 4 +- 9 files changed, 134 insertions(+), 59 deletions(-) create mode 100644 docs/browsers-api/browsers.install_1.md diff --git a/docs/browsers-api/browsers.install.md b/docs/browsers-api/browsers.install.md index 936cf5f536b..8b9936c075d 100644 --- a/docs/browsers-api/browsers.install.md +++ b/docs/browsers-api/browsers.install.md @@ -8,15 +8,17 @@ sidebar_label: install ```typescript export declare function install( - options: InstallOptions + options: InstallOptions & { + unpack?: true; + } ): Promise; ``` ## Parameters -| Parameter | Type | Description | -| --------- | ---------------------------------------------- | ----------- | -| options | [InstallOptions](./browsers.installoptions.md) | | +| Parameter | Type | Description | +| --------- | ----------------------------------------------------------------------- | ----------- | +| options | [InstallOptions](./browsers.installoptions.md) & { unpack?: true; } | | **Returns:** diff --git a/docs/browsers-api/browsers.install_1.md b/docs/browsers-api/browsers.install_1.md new file mode 100644 index 00000000000..f44fdab2eb7 --- /dev/null +++ b/docs/browsers-api/browsers.install_1.md @@ -0,0 +1,25 @@ +--- +sidebar_label: install_1 +--- + +# install() function + +#### Signature: + +```typescript +export declare function install( + options: InstallOptions & { + unpack: false; + } +): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --------- | ----------------------------------------------------------------------- | ----------- | +| options | [InstallOptions](./browsers.installoptions.md) & { unpack: false; } | | + +**Returns:** + +Promise<string> diff --git a/docs/browsers-api/browsers.installedbrowser.md b/docs/browsers-api/browsers.installedbrowser.md index a7cc2dc894f..48ad1a90fab 100644 --- a/docs/browsers-api/browsers.installedbrowser.md +++ b/docs/browsers-api/browsers.installedbrowser.md @@ -2,19 +2,24 @@ sidebar_label: InstalledBrowser --- -# InstalledBrowser interface +# InstalledBrowser class #### Signature: ```typescript -export interface InstalledBrowser +export declare class InstalledBrowser ``` +## Remarks + +The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `InstalledBrowser` class. + ## Properties -| Property | Modifiers | Type | Description | Default | -| -------- | --------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| browser | | [Browser](./browsers.browser.md) | | | -| buildId | | string | | | -| path | | string | Path to the root of the installation folder. Use [computeExecutablePath()](./browsers.computeexecutablepath.md) to get the path to the executable binary. | | -| platform | | [BrowserPlatform](./browsers.browserplatform.md) | | | +| Property | Modifiers | Type | Description | +| -------------- | --------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| browser | | [Browser](./browsers.browser.md) | | +| buildId | | string | | +| executablePath | readonly | string | | +| path | readonly | string | Path to the root of the installation folder. Use [computeExecutablePath()](./browsers.computeexecutablepath.md) to get the path to the executable binary. | +| platform | | [BrowserPlatform](./browsers.browserplatform.md) | | diff --git a/docs/browsers-api/index.md b/docs/browsers-api/index.md index dc9ff1ef432..11cac42d5e6 100644 --- a/docs/browsers-api/index.md +++ b/docs/browsers-api/index.md @@ -33,11 +33,12 @@ The programmatic API allows installing and launching browsers from your code. Se ## Classes -| Class | Description | -| ------------------------------------------ | ----------- | -| [CLI](./browsers.cli.md) | | -| [Process](./browsers.process.md) | | -| [TimeoutError](./browsers.timeouterror.md) | | +| Class | Description | +| -------------------------------------------------- | ----------- | +| [CLI](./browsers.cli.md) | | +| [InstalledBrowser](./browsers.installedbrowser.md) | | +| [Process](./browsers.process.md) | | +| [TimeoutError](./browsers.timeouterror.md) | | ## Enumerations @@ -58,6 +59,7 @@ The programmatic API allows installing and launching browsers from your code. Se | [detectBrowserPlatform()](./browsers.detectbrowserplatform.md) | | | [getInstalledBrowsers(options)](./browsers.getinstalledbrowsers.md) | Returns metadata about browsers installed in the cache directory. | | [install(options)](./browsers.install.md) | | +| [install(options)](./browsers.install_1.md) | | | [launch(opts)](./browsers.launch.md) | | | [makeProgressCallback(browser, buildId)](./browsers.makeprogresscallback.md) | | | [resolveBuildId(browser, platform, tag)](./browsers.resolvebuildid.md) | | @@ -68,7 +70,6 @@ The programmatic API allows installing and launching browsers from your code. Se | Interface | Description | | ------------------------------------------------------------------------ | ----------- | | [GetInstalledBrowsersOptions](./browsers.getinstalledbrowsersoptions.md) | | -| [InstalledBrowser](./browsers.installedbrowser.md) | | | [InstallOptions](./browsers.installoptions.md) | | | [LaunchOptions](./browsers.launchoptions.md) | | | [Options](./browsers.options.md) | | diff --git a/packages/browsers/src/Cache.ts b/packages/browsers/src/Cache.ts index 7a588396490..8312283ba1b 100644 --- a/packages/browsers/src/Cache.ts +++ b/packages/browsers/src/Cache.ts @@ -18,19 +18,53 @@ import fs from 'fs'; import path from 'path'; import {Browser, BrowserPlatform} from './browser-data/browser-data.js'; +import {computeExecutablePath} from './launch.js'; /** * @public */ -export interface InstalledBrowser { +export class InstalledBrowser { + browser: Browser; + buildId: string; + platform: BrowserPlatform; + + #cache: Cache; + + /** + * @internal + */ + constructor( + cache: Cache, + browser: Browser, + buildId: string, + platform: BrowserPlatform + ) { + this.#cache = cache; + this.browser = browser; + this.buildId = buildId; + this.platform = platform; + } + /** * Path to the root of the installation folder. Use * {@link computeExecutablePath} to get the path to the executable binary. */ - path: string; - browser: Browser; - buildId: string; - platform: BrowserPlatform; + get path(): string { + return this.#cache.installationDir( + this.browser, + this.platform, + this.buildId + ); + } + + get executablePath(): string { + return computeExecutablePath({ + cacheDir: this.#cache.rootDir, + platform: this.platform, + browser: this.browser, + buildId: this.buildId, + }); + } } /** @@ -54,6 +88,13 @@ export class Cache { this.#rootDir = rootDir; } + /** + * @internal + */ + get rootDir(): string { + return this.#rootDir; + } + browserRoot(browser: Browser): string { return path.join(this.#rootDir, browser); } @@ -106,14 +147,14 @@ export class Cache { if (!result) { return null; } - return { - path: path.join(this.browserRoot(browser), file), + return new InstalledBrowser( + this, browser, - platform: result.platform, - buildId: result.buildId, - }; + result.buildId, + result.platform as BrowserPlatform + ); }) - .filter((item): item is InstalledBrowser => { + .filter((item: InstalledBrowser | null): item is InstalledBrowser => { return item !== null; }); }); diff --git a/packages/browsers/src/install.ts b/packages/browsers/src/install.ts index 6be5d5e5b89..237871c3a5d 100644 --- a/packages/browsers/src/install.ts +++ b/packages/browsers/src/install.ts @@ -100,9 +100,15 @@ export interface InstallOptions { /** * @public */ +export function install( + options: InstallOptions & {unpack?: true} +): Promise; +export function install( + options: InstallOptions & {unpack: false} +): Promise; export async function install( options: InstallOptions -): Promise { +): Promise { options.platform ??= detectBrowserPlatform(); options.unpack ??= true; if (!options.platform) { @@ -118,8 +124,8 @@ export async function install( ); const fileName = url.toString().split('/').pop(); assert(fileName, `A malformed download URL was found: ${url}.`); - const structure = new Cache(options.cacheDir); - const browserRoot = structure.browserRoot(options.browser); + const cache = new Cache(options.cacheDir); + const browserRoot = cache.browserRoot(options.browser); const archivePath = path.join(browserRoot, fileName); if (!existsSync(browserRoot)) { await mkdir(browserRoot, {recursive: true}); @@ -127,37 +133,27 @@ export async function install( if (!options.unpack) { if (existsSync(archivePath)) { - return { - path: archivePath, - browser: options.browser, - platform: options.platform, - buildId: options.buildId, - }; + return archivePath; } debugInstall(`Downloading binary from ${url}`); debugTime('download'); await downloadFile(url, archivePath, options.downloadProgressCallback); debugTimeEnd('download'); - return { - path: archivePath, - browser: options.browser, - platform: options.platform, - buildId: options.buildId, - }; + return archivePath; } - const outputPath = structure.installationDir( + const outputPath = cache.installationDir( options.browser, options.platform, options.buildId ); if (existsSync(outputPath)) { - return { - path: outputPath, - browser: options.browser, - platform: options.platform, - buildId: options.buildId, - }; + return new InstalledBrowser( + cache, + options.browser, + options.buildId, + options.platform + ); } try { debugInstall(`Downloading binary from ${url}`); @@ -180,12 +176,12 @@ export async function install( await unlink(archivePath); } } - return { - path: outputPath, - browser: options.browser, - platform: options.platform, - buildId: options.buildId, - }; + return new InstalledBrowser( + cache, + options.browser, + options.buildId, + options.platform + ); } /** diff --git a/packages/browsers/test/src/chrome/install.spec.ts b/packages/browsers/test/src/chrome/install.spec.ts index 7dc3e7ebaf0..f20ceabb15e 100644 --- a/packages/browsers/test/src/chrome/install.spec.ts +++ b/packages/browsers/test/src/chrome/install.spec.ts @@ -104,6 +104,10 @@ describe('Chrome install', () => { 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 () { diff --git a/packages/browsers/test/src/chromedriver/install.spec.ts b/packages/browsers/test/src/chromedriver/install.spec.ts index fb725de010e..6270887171e 100644 --- a/packages/browsers/test/src/chromedriver/install.spec.ts +++ b/packages/browsers/test/src/chromedriver/install.spec.ts @@ -98,5 +98,6 @@ describe('ChromeDriver install', () => { }); assert.strictEqual(browser.path, expectedOutputPath); assert.ok(fs.existsSync(expectedOutputPath)); + assert.ok(fs.existsSync(browser.executablePath)); }); }); diff --git a/packages/browsers/tools/downloadTestBrowsers.mjs b/packages/browsers/tools/downloadTestBrowsers.mjs index 9ee861f878f..40be6eaffb3 100644 --- a/packages/browsers/tools/downloadTestBrowsers.mjs +++ b/packages/browsers/tools/downloadTestBrowsers.mjs @@ -59,7 +59,7 @@ for (const version of Object.keys(versions)) { continue; } - const result = await install({ + const archivePath = await install({ browser, buildId, platform, @@ -70,7 +70,7 @@ for (const version of Object.keys(versions)) { fs.mkdirSync(path.dirname(targetPath), { recursive: true, }); - fs.copyFileSync(result.path, targetPath); + fs.copyFileSync(archivePath, targetPath); } }