From ec201744f077987b288e3dff52c0906fe700f6fb Mon Sep 17 00:00:00 2001 From: jrandolf <101637635+jrandolf@users.noreply.github.com> Date: Fri, 21 Oct 2022 15:09:21 +0200 Subject: [PATCH] feat: use configuration files (#9140) This PR adds configurations files to `puppeteer`'s methods for configuration. Under the hood, `puppeteer` relies on https://www.npmjs.com/package/cosmiconfig which resolves several formats of configuration: - a `puppeteer` property in package.json - a `.puppeteerrc` file in JSON or YAML format - a `.puppeteerrc.json`, `.puppeteerrc.yaml`, `.puppeteerrc.yml`, `.puppeteerrc.js`, or `.puppeteerrc.cjs` file - a `puppeteer.config.js` or `puppeteer.config.cjs` CommonJS module exporting an object Documentation will be added later. Fixed: #9128 --- docs/api/index.md | 3 +- .../puppeteer.browserfetcher._constructor_.md | 8 +- ...puppeteer.browserfetcher.localrevisions.md | 6 +- docs/api/puppeteer.browserfetcher.md | 2 +- docs/api/puppeteer.browserfetcheroptions.md | 13 +- .../puppeteer.browserfetcheroptions.path.md | 2 +- ...browserfetcheroptions.usemacosarmbinary.md | 15 ++ ...puppeteer.configuration.browserrevision.md | 13 + .../puppeteer.configuration.cachedirectory.md | 13 + .../puppeteer.configuration.defaultproduct.md | 13 + .../puppeteer.configuration.downloadhost.md | 13 + .../puppeteer.configuration.downloadpath.md | 13 + .../puppeteer.configuration.executablepath.md | 13 + .../puppeteer.configuration.experiments.md | 15 ++ docs/api/puppeteer.configuration.loglevel.md | 13 + docs/api/puppeteer.configuration.md | 26 ++ .../puppeteer.configuration.skipdownload.md | 13 + ...peteer.configuration.temporarydirectory.md | 13 + docs/api/puppeteer.connect.md | 2 +- docs/api/puppeteer.createbrowserfetcher.md | 4 +- docs/api/puppeteer.executablepath.md | 6 +- docs/api/puppeteer.launch.md | 2 +- .../puppeteer.productlauncher.defaultargs.md | 2 +- ...uppeteer.productlauncher.executablepath.md | 16 +- docs/api/puppeteer.productlauncher.launch.md | 2 +- docs/api/puppeteer.productlauncher.md | 24 +- docs/api/puppeteer.productlauncher.product.md | 4 +- ...teer.puppeteernode.createbrowserfetcher.md | 8 +- .../puppeteer.puppeteernode.defaultproduct.md | 13 + .../puppeteer.puppeteernode.executablepath.md | 14 +- ...eteer.puppeteernode.lastlaunchedproduct.md | 13 + docs/api/puppeteer.puppeteernode.launch.md | 12 +- docs/api/puppeteer.puppeteernode.md | 22 +- docs/api/puppeteer.puppeteernode.product.md | 8 +- package-lock.json | 88 ++----- .../{constants.ts => common/Configuration.ts} | 21 +- .../puppeteer-core/src/common/Puppeteer.ts | 2 +- .../puppeteer-core/src/node/BrowserFetcher.ts | 78 +++--- .../puppeteer-core/src/node/ChromeLauncher.ts | 141 +++++++---- .../src/node/FirefoxLauncher.ts | 74 ++---- .../src/node/ProductLauncher.ts | 238 ++++++------------ .../puppeteer-core/src/node/PuppeteerNode.ts | 214 ++++++++++------ packages/puppeteer-core/src/node/util.ts | 13 - packages/puppeteer-core/src/types.ts | 3 +- packages/puppeteer/install.js | 72 +----- packages/puppeteer/package.json | 1 + packages/puppeteer/src/getConfiguration.ts | 95 +++++++ packages/puppeteer/src/node/install.ts | 73 ++---- packages/puppeteer/src/puppeteer.ts | 21 +- packages/puppeteer/src/types.ts | 4 +- .../puppeteer/configuration/.puppeteerrc.cjs | 8 + test/installation/src/describeInstallation.ts | 5 + .../src/puppeteer-configuration.spec.ts | 43 ++++ test/src/launcher.spec.ts | 70 +++--- test/src/mocha-utils.ts | 8 - 55 files changed, 925 insertions(+), 706 deletions(-) create mode 100644 docs/api/puppeteer.browserfetcheroptions.usemacosarmbinary.md create mode 100644 docs/api/puppeteer.configuration.browserrevision.md create mode 100644 docs/api/puppeteer.configuration.cachedirectory.md create mode 100644 docs/api/puppeteer.configuration.defaultproduct.md create mode 100644 docs/api/puppeteer.configuration.downloadhost.md create mode 100644 docs/api/puppeteer.configuration.downloadpath.md create mode 100644 docs/api/puppeteer.configuration.executablepath.md create mode 100644 docs/api/puppeteer.configuration.experiments.md create mode 100644 docs/api/puppeteer.configuration.loglevel.md create mode 100644 docs/api/puppeteer.configuration.md create mode 100644 docs/api/puppeteer.configuration.skipdownload.md create mode 100644 docs/api/puppeteer.configuration.temporarydirectory.md create mode 100644 docs/api/puppeteer.puppeteernode.defaultproduct.md create mode 100644 docs/api/puppeteer.puppeteernode.lastlaunchedproduct.md rename packages/puppeteer-core/src/{constants.ts => common/Configuration.ts} (60%) delete mode 100644 packages/puppeteer-core/src/node/util.ts create mode 100644 packages/puppeteer/src/getConfiguration.ts create mode 100644 test/installation/assets/puppeteer/configuration/.puppeteerrc.cjs create mode 100644 test/installation/src/puppeteer-configuration.spec.ts diff --git a/docs/api/index.md b/docs/api/index.md index 2fa726570b2..724f9ffc247 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -30,6 +30,7 @@ sidebar_label: API | [Keyboard](./puppeteer.keyboard.md) | Keyboard provides an api for managing a virtual keyboard. The high level api is [Keyboard.type()](./puppeteer.keyboard.type.md), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page. | | [Mouse](./puppeteer.mouse.md) | The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport. | | [Page](./puppeteer.page.md) |

Page provides methods to interact with a single tab or [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium.

:::note

One Browser instance might have multiple Page instances.

:::

| +| [ProductLauncher](./puppeteer.productlauncher.md) | Describes a launcher - a class that is able to create and launch a browser instance. | | [ProtocolError](./puppeteer.protocolerror.md) | ProtocolError is emitted whenever there is an error from the protocol. | | [Puppeteer](./puppeteer.puppeteer.md) |

The main Puppeteer class.

IMPORTANT: if you are using Puppeteer in a Node environment, you will get an instance of [PuppeteerNode](./puppeteer.puppeteernode.md) when you import or require puppeteer. That class extends Puppeteer, so has all the methods documented below as well as all that are defined on [PuppeteerNode](./puppeteer.puppeteernode.md).

| | [PuppeteerNode](./puppeteer.puppeteernode.md) |

Extends the main [Puppeteer](./puppeteer.puppeteer.md) class with Node specific behaviour for fetching and downloading browsers.

If you're using Puppeteer in a Node environment, this is the class you'll get when you run require('puppeteer') (or the equivalent ES import).

| @@ -72,6 +73,7 @@ sidebar_label: API | [CDPSessionOnMessageObject](./puppeteer.cdpsessiononmessageobject.md) | | | [ClickOptions](./puppeteer.clickoptions.md) | | | [CommonEventEmitter](./puppeteer.commoneventemitter.md) | | +| [Configuration](./puppeteer.configuration.md) | | | [ConnectionCallback](./puppeteer.connectioncallback.md) | | | [ConnectionTransport](./puppeteer.connectiontransport.md) | | | [ConnectOptions](./puppeteer.connectoptions.md) | | @@ -102,7 +104,6 @@ sidebar_label: API | [PDFOptions](./puppeteer.pdfoptions.md) | Valid options to configure PDF generation via [Page.pdf()](./puppeteer.page.pdf.md). | | [Point](./puppeteer.point.md) | | | [PressOptions](./puppeteer.pressoptions.md) | | -| [ProductLauncher](./puppeteer.productlauncher.md) | Describes a launcher - a class that is able to create and launch a browser instance. | | [PuppeteerErrors](./puppeteer.puppeteererrors.md) | | | [PuppeteerLaunchOptions](./puppeteer.puppeteerlaunchoptions.md) | | | [RemoteAddress](./puppeteer.remoteaddress.md) | | diff --git a/docs/api/puppeteer.browserfetcher._constructor_.md b/docs/api/puppeteer.browserfetcher._constructor_.md index 6dd5d0ccb4f..833a365a490 100644 --- a/docs/api/puppeteer.browserfetcher._constructor_.md +++ b/docs/api/puppeteer.browserfetcher._constructor_.md @@ -10,12 +10,12 @@ Constructs a browser fetcher for the given options. ```typescript class BrowserFetcher { - constructor(options?: BrowserFetcherOptions); + constructor(options: BrowserFetcherOptions); } ``` ## Parameters -| Parameter | Type | Description | -| --------- | ------------------------------------------------------------- | ----------------- | -| options | [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | (Optional) | +| Parameter | Type | Description | +| --------- | ------------------------------------------------------------- | ----------- | +| options | [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | | diff --git a/docs/api/puppeteer.browserfetcher.localrevisions.md b/docs/api/puppeteer.browserfetcher.localrevisions.md index 8f62f6bc15f..f8ddd72f39a 100644 --- a/docs/api/puppeteer.browserfetcher.localrevisions.md +++ b/docs/api/puppeteer.browserfetcher.localrevisions.md @@ -8,15 +8,15 @@ sidebar_label: BrowserFetcher.localRevisions ```typescript class BrowserFetcher { - localRevisions(): Promise; + localRevisions(): string[]; } ``` **Returns:** -Promise<string\[\]> +string\[\] -A promise with a list of all revision strings (for the current `product`) available locally on disk. +A list of all revision strings (for the current `product`) available locally on disk. ## Remarks diff --git a/docs/api/puppeteer.browserfetcher.md b/docs/api/puppeteer.browserfetcher.md index 84fd68f9cd7..685637ee7ff 100644 --- a/docs/api/puppeteer.browserfetcher.md +++ b/docs/api/puppeteer.browserfetcher.md @@ -21,7 +21,7 @@ BrowserFetcher is not designed to work concurrently with other instances of Brow An example of using BrowserFetcher to download a specific version of Chromium and running Puppeteer against it: ```ts -const browserFetcher = new BrowserFetcher(); +const browserFetcher = new BrowserFetcher({path: 'path/to/download/folder'}); const revisionInfo = await browserFetcher.download('533271'); const browser = await puppeteer.launch({ executablePath: revisionInfo.executablePath, diff --git a/docs/api/puppeteer.browserfetcheroptions.md b/docs/api/puppeteer.browserfetcheroptions.md index 4817cedc73d..aac9f900cda 100644 --- a/docs/api/puppeteer.browserfetcheroptions.md +++ b/docs/api/puppeteer.browserfetcheroptions.md @@ -12,9 +12,10 @@ export interface BrowserFetcherOptions ## Properties -| Property | Modifiers | Type | Description | -| ---------------------------------------------------------- | --------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------ | -| [host?](./puppeteer.browserfetcheroptions.host.md) | | string | (Optional) Determines the host that will be used for downloading. | -| [path?](./puppeteer.browserfetcheroptions.path.md) | | string | (Optional) Determines the path to download browsers to. | -| [platform?](./puppeteer.browserfetcheroptions.platform.md) | | [Platform](./puppeteer.platform.md) | (Optional) Determines which platform the browser will be suited for. | -| [product?](./puppeteer.browserfetcheroptions.product.md) | | 'chrome' \| 'firefox' | (Optional) Determines which product the [BrowserFetcher](./puppeteer.browserfetcher.md) is for. | +| Property | Modifiers | Type | Description | +| ---------------------------------------------------------------------------- | --------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------ | +| [host?](./puppeteer.browserfetcheroptions.host.md) | | string | (Optional) Determines the host that will be used for downloading. | +| [path](./puppeteer.browserfetcheroptions.path.md) | | string | Determines the path to download browsers to. | +| [platform?](./puppeteer.browserfetcheroptions.platform.md) | | [Platform](./puppeteer.platform.md) | (Optional) Determines which platform the browser will be suited for. | +| [product?](./puppeteer.browserfetcheroptions.product.md) | | 'chrome' \| 'firefox' | (Optional) Determines which product the [BrowserFetcher](./puppeteer.browserfetcher.md) is for. | +| [useMacOSARMBinary?](./puppeteer.browserfetcheroptions.usemacosarmbinary.md) | | boolean | (Optional) Enables the use of the Chromium binary for macOS ARM. | diff --git a/docs/api/puppeteer.browserfetcheroptions.path.md b/docs/api/puppeteer.browserfetcheroptions.path.md index 61e7ebfb7e4..e67cd6aceda 100644 --- a/docs/api/puppeteer.browserfetcheroptions.path.md +++ b/docs/api/puppeteer.browserfetcheroptions.path.md @@ -10,6 +10,6 @@ Determines the path to download browsers to. ```typescript interface BrowserFetcherOptions { - path?: string; + path: string; } ``` diff --git a/docs/api/puppeteer.browserfetcheroptions.usemacosarmbinary.md b/docs/api/puppeteer.browserfetcheroptions.usemacosarmbinary.md new file mode 100644 index 00000000000..87ae5ef11a3 --- /dev/null +++ b/docs/api/puppeteer.browserfetcheroptions.usemacosarmbinary.md @@ -0,0 +1,15 @@ +--- +sidebar_label: BrowserFetcherOptions.useMacOSARMBinary +--- + +# BrowserFetcherOptions.useMacOSARMBinary property + +Enables the use of the Chromium binary for macOS ARM. + +**Signature:** + +```typescript +interface BrowserFetcherOptions { + useMacOSARMBinary?: boolean; +} +``` diff --git a/docs/api/puppeteer.configuration.browserrevision.md b/docs/api/puppeteer.configuration.browserrevision.md new file mode 100644 index 00000000000..49b6c20c0e5 --- /dev/null +++ b/docs/api/puppeteer.configuration.browserrevision.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.browserRevision +--- + +# Configuration.browserRevision property + +**Signature:** + +```typescript +interface Configuration { + browserRevision?: string; +} +``` diff --git a/docs/api/puppeteer.configuration.cachedirectory.md b/docs/api/puppeteer.configuration.cachedirectory.md new file mode 100644 index 00000000000..c7443b42365 --- /dev/null +++ b/docs/api/puppeteer.configuration.cachedirectory.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.cacheDirectory +--- + +# Configuration.cacheDirectory property + +**Signature:** + +```typescript +interface Configuration { + cacheDirectory?: string; +} +``` diff --git a/docs/api/puppeteer.configuration.defaultproduct.md b/docs/api/puppeteer.configuration.defaultproduct.md new file mode 100644 index 00000000000..36ffb353023 --- /dev/null +++ b/docs/api/puppeteer.configuration.defaultproduct.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.defaultProduct +--- + +# Configuration.defaultProduct property + +**Signature:** + +```typescript +interface Configuration { + defaultProduct?: Product; +} +``` diff --git a/docs/api/puppeteer.configuration.downloadhost.md b/docs/api/puppeteer.configuration.downloadhost.md new file mode 100644 index 00000000000..9b8ef482d29 --- /dev/null +++ b/docs/api/puppeteer.configuration.downloadhost.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.downloadHost +--- + +# Configuration.downloadHost property + +**Signature:** + +```typescript +interface Configuration { + downloadHost?: string; +} +``` diff --git a/docs/api/puppeteer.configuration.downloadpath.md b/docs/api/puppeteer.configuration.downloadpath.md new file mode 100644 index 00000000000..befa69132a5 --- /dev/null +++ b/docs/api/puppeteer.configuration.downloadpath.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.downloadPath +--- + +# Configuration.downloadPath property + +**Signature:** + +```typescript +interface Configuration { + downloadPath?: string; +} +``` diff --git a/docs/api/puppeteer.configuration.executablepath.md b/docs/api/puppeteer.configuration.executablepath.md new file mode 100644 index 00000000000..8c7fe082d28 --- /dev/null +++ b/docs/api/puppeteer.configuration.executablepath.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.executablePath +--- + +# Configuration.executablePath property + +**Signature:** + +```typescript +interface Configuration { + executablePath?: string; +} +``` diff --git a/docs/api/puppeteer.configuration.experiments.md b/docs/api/puppeteer.configuration.experiments.md new file mode 100644 index 00000000000..9f4ea2348f8 --- /dev/null +++ b/docs/api/puppeteer.configuration.experiments.md @@ -0,0 +1,15 @@ +--- +sidebar_label: Configuration.experiments +--- + +# Configuration.experiments property + +**Signature:** + +```typescript +interface Configuration { + experiments?: { + macArmChromiumEnabled?: boolean; + }; +} +``` diff --git a/docs/api/puppeteer.configuration.loglevel.md b/docs/api/puppeteer.configuration.loglevel.md new file mode 100644 index 00000000000..c66570dadfb --- /dev/null +++ b/docs/api/puppeteer.configuration.loglevel.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.logLevel +--- + +# Configuration.logLevel property + +**Signature:** + +```typescript +interface Configuration { + logLevel?: 'silent' | 'error' | 'warn'; +} +``` diff --git a/docs/api/puppeteer.configuration.md b/docs/api/puppeteer.configuration.md new file mode 100644 index 00000000000..632baa79a8c --- /dev/null +++ b/docs/api/puppeteer.configuration.md @@ -0,0 +1,26 @@ +--- +sidebar_label: Configuration +--- + +# Configuration interface + +**Signature:** + +```typescript +export interface Configuration +``` + +## Properties + +| Property | Modifiers | Type | Description | +| ---------------------------------------------------------------------- | --------- | ------------------------------------ | ----------------- | +| [browserRevision?](./puppeteer.configuration.browserrevision.md) | | string | (Optional) | +| [cacheDirectory?](./puppeteer.configuration.cachedirectory.md) | | string | (Optional) | +| [defaultProduct?](./puppeteer.configuration.defaultproduct.md) | | [Product](./puppeteer.product.md) | (Optional) | +| [downloadHost?](./puppeteer.configuration.downloadhost.md) | | string | (Optional) | +| [downloadPath?](./puppeteer.configuration.downloadpath.md) | | string | (Optional) | +| [executablePath?](./puppeteer.configuration.executablepath.md) | | string | (Optional) | +| [experiments?](./puppeteer.configuration.experiments.md) | | { macArmChromiumEnabled?: boolean; } | (Optional) | +| [logLevel?](./puppeteer.configuration.loglevel.md) | | 'silent' \| 'error' \| 'warn' | (Optional) | +| [skipDownload?](./puppeteer.configuration.skipdownload.md) | | boolean | (Optional) | +| [temporaryDirectory?](./puppeteer.configuration.temporarydirectory.md) | | string | (Optional) | diff --git a/docs/api/puppeteer.configuration.skipdownload.md b/docs/api/puppeteer.configuration.skipdownload.md new file mode 100644 index 00000000000..d3ef56cdcfb --- /dev/null +++ b/docs/api/puppeteer.configuration.skipdownload.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.skipDownload +--- + +# Configuration.skipDownload property + +**Signature:** + +```typescript +interface Configuration { + skipDownload?: boolean; +} +``` diff --git a/docs/api/puppeteer.configuration.temporarydirectory.md b/docs/api/puppeteer.configuration.temporarydirectory.md new file mode 100644 index 00000000000..27c298aad9f --- /dev/null +++ b/docs/api/puppeteer.configuration.temporarydirectory.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.temporaryDirectory +--- + +# Configuration.temporaryDirectory property + +**Signature:** + +```typescript +interface Configuration { + temporaryDirectory?: string; +} +``` diff --git a/docs/api/puppeteer.connect.md b/docs/api/puppeteer.connect.md index 8cda9ef142d..a2dad8d61cc 100644 --- a/docs/api/puppeteer.connect.md +++ b/docs/api/puppeteer.connect.md @@ -9,5 +9,5 @@ sidebar_label: connect ```typescript connect: ( options: import('puppeteer-core/internal/common/Puppeteer.js').ConnectOptions -) => Promise; +) => Promise; ``` diff --git a/docs/api/puppeteer.createbrowserfetcher.md b/docs/api/puppeteer.createbrowserfetcher.md index 1af8a3b1009..62b786c57d0 100644 --- a/docs/api/puppeteer.createbrowserfetcher.md +++ b/docs/api/puppeteer.createbrowserfetcher.md @@ -8,6 +8,8 @@ sidebar_label: createBrowserFetcher ```typescript createBrowserFetcher: ( - options: import('puppeteer-core/internal/node/BrowserFetcher.js').BrowserFetcherOptions + options: Partial< + import('puppeteer-core/internal/node/BrowserFetcher.js').BrowserFetcherOptions + > ) => import('puppeteer-core/internal/node/BrowserFetcher.js').BrowserFetcher; ``` diff --git a/docs/api/puppeteer.executablepath.md b/docs/api/puppeteer.executablepath.md index 44cc2c6c0de..74e299a9c4d 100644 --- a/docs/api/puppeteer.executablepath.md +++ b/docs/api/puppeteer.executablepath.md @@ -7,5 +7,9 @@ sidebar_label: executablePath **Signature:** ```typescript -executablePath: (channel?: string | undefined) => string; +executablePath: ( + channel?: + | import('puppeteer-core/internal/node/LaunchOptions.js').ChromeReleaseChannel + | undefined +) => string; ``` diff --git a/docs/api/puppeteer.launch.md b/docs/api/puppeteer.launch.md index 52e36c03040..6813e636f18 100644 --- a/docs/api/puppeteer.launch.md +++ b/docs/api/puppeteer.launch.md @@ -11,5 +11,5 @@ launch: ( options?: | import('puppeteer-core/internal/node/PuppeteerNode.js').PuppeteerLaunchOptions | undefined -) => Promise; +) => Promise; ``` diff --git a/docs/api/puppeteer.productlauncher.defaultargs.md b/docs/api/puppeteer.productlauncher.defaultargs.md index fe79cbeac2d..8a4483bf7c0 100644 --- a/docs/api/puppeteer.productlauncher.defaultargs.md +++ b/docs/api/puppeteer.productlauncher.defaultargs.md @@ -7,7 +7,7 @@ sidebar_label: ProductLauncher.defaultArgs **Signature:** ```typescript -interface ProductLauncher { +class ProductLauncher { defaultArgs(object: BrowserLaunchArgumentOptions): string[]; } ``` diff --git a/docs/api/puppeteer.productlauncher.executablepath.md b/docs/api/puppeteer.productlauncher.executablepath.md index 8223e888806..67a19789418 100644 --- a/docs/api/puppeteer.productlauncher.executablepath.md +++ b/docs/api/puppeteer.productlauncher.executablepath.md @@ -2,12 +2,22 @@ sidebar_label: ProductLauncher.executablePath --- -# ProductLauncher.executablePath property +# ProductLauncher.executablePath() method **Signature:** ```typescript -interface ProductLauncher { - executablePath: (path?: any) => string; +class ProductLauncher { + executablePath(channel?: ChromeReleaseChannel): string; } ``` + +## Parameters + +| Parameter | Type | Description | +| --------- | ----------------------------------------------------------- | ----------------- | +| channel | [ChromeReleaseChannel](./puppeteer.chromereleasechannel.md) | (Optional) | + +**Returns:** + +string diff --git a/docs/api/puppeteer.productlauncher.launch.md b/docs/api/puppeteer.productlauncher.launch.md index bad57b91dcf..15755638f7e 100644 --- a/docs/api/puppeteer.productlauncher.launch.md +++ b/docs/api/puppeteer.productlauncher.launch.md @@ -7,7 +7,7 @@ sidebar_label: ProductLauncher.launch **Signature:** ```typescript -interface ProductLauncher { +class ProductLauncher { launch(object: PuppeteerNodeLaunchOptions): Promise; } ``` diff --git a/docs/api/puppeteer.productlauncher.md b/docs/api/puppeteer.productlauncher.md index 0cfab0965a5..ac12b8c266c 100644 --- a/docs/api/puppeteer.productlauncher.md +++ b/docs/api/puppeteer.productlauncher.md @@ -2,26 +2,30 @@ sidebar_label: ProductLauncher --- -# ProductLauncher interface +# ProductLauncher class Describes a launcher - a class that is able to create and launch a browser instance. **Signature:** ```typescript -export interface ProductLauncher +export declare class ProductLauncher ``` +## 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 `ProductLauncher` class. + ## Properties -| Property | Modifiers | Type | Description | -| --------------------------------------------------------------- | --------- | --------------------------------- | ----------- | -| [executablePath](./puppeteer.productlauncher.executablepath.md) | | (path?: any) => string | | -| [product](./puppeteer.productlauncher.product.md) | | [Product](./puppeteer.product.md) | | +| Property | Modifiers | Type | Description | +| ------------------------------------------------- | --------------------- | --------------------------------- | ----------- | +| [product](./puppeteer.productlauncher.product.md) | readonly | [Product](./puppeteer.product.md) | | ## Methods -| Method | Description | -| ----------------------------------------------------------------- | ----------- | -| [defaultArgs(object)](./puppeteer.productlauncher.defaultargs.md) | | -| [launch(object)](./puppeteer.productlauncher.launch.md) | | +| Method | Modifiers | Description | +| ------------------------------------------------------------------------ | --------- | ----------- | +| [defaultArgs(object)](./puppeteer.productlauncher.defaultargs.md) | | | +| [executablePath(channel)](./puppeteer.productlauncher.executablepath.md) | | | +| [launch(object)](./puppeteer.productlauncher.launch.md) | | | diff --git a/docs/api/puppeteer.productlauncher.product.md b/docs/api/puppeteer.productlauncher.product.md index aa8bc881a44..c1710bc9056 100644 --- a/docs/api/puppeteer.productlauncher.product.md +++ b/docs/api/puppeteer.productlauncher.product.md @@ -7,7 +7,7 @@ sidebar_label: ProductLauncher.product **Signature:** ```typescript -interface ProductLauncher { - product: Product; +class ProductLauncher { + get product(): Product; } ``` diff --git a/docs/api/puppeteer.puppeteernode.createbrowserfetcher.md b/docs/api/puppeteer.puppeteernode.createbrowserfetcher.md index 5614f3dfec4..3f9abada4e5 100644 --- a/docs/api/puppeteer.puppeteernode.createbrowserfetcher.md +++ b/docs/api/puppeteer.puppeteernode.createbrowserfetcher.md @@ -12,15 +12,15 @@ sidebar_label: PuppeteerNode.createBrowserFetcher ```typescript class PuppeteerNode { - createBrowserFetcher(options: BrowserFetcherOptions): BrowserFetcher; + createBrowserFetcher(options: Partial): BrowserFetcher; } ``` ## Parameters -| Parameter | Type | Description | -| --------- | ------------------------------------------------------------- | -------------------------------------------------------------------------- | -| options | [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | Set of configurable options to specify the settings of the BrowserFetcher. | +| Parameter | Type | Description | +| --------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| options | Partial<[BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md)> | Set of configurable options to specify the settings of the BrowserFetcher. | **Returns:** diff --git a/docs/api/puppeteer.puppeteernode.defaultproduct.md b/docs/api/puppeteer.puppeteernode.defaultproduct.md new file mode 100644 index 00000000000..cd8297e2576 --- /dev/null +++ b/docs/api/puppeteer.puppeteernode.defaultproduct.md @@ -0,0 +1,13 @@ +--- +sidebar_label: PuppeteerNode.defaultProduct +--- + +# PuppeteerNode.defaultProduct property + +**Signature:** + +```typescript +class PuppeteerNode { + get defaultProduct(): Product; +} +``` diff --git a/docs/api/puppeteer.puppeteernode.executablepath.md b/docs/api/puppeteer.puppeteernode.executablepath.md index 0534045055b..e27c61d1d11 100644 --- a/docs/api/puppeteer.puppeteernode.executablepath.md +++ b/docs/api/puppeteer.puppeteernode.executablepath.md @@ -8,22 +8,18 @@ sidebar_label: PuppeteerNode.executablePath ```typescript class PuppeteerNode { - executablePath(channel?: string): string; + executablePath(channel?: ChromeReleaseChannel): string; } ``` ## Parameters -| Parameter | Type | Description | -| --------- | ------ | ----------------- | -| channel | string | (Optional) | +| Parameter | Type | Description | +| --------- | ----------------------------------------------------------- | ----------------- | +| channel | [ChromeReleaseChannel](./puppeteer.chromereleasechannel.md) | (Optional) | **Returns:** string -A path where Puppeteer expects to find the bundled browser. The browser binary might not be there if the download was skipped with the `PUPPETEER_SKIP_DOWNLOAD` environment variable. - -## Remarks - -**NOTE** `puppeteer.executablePath()` is affected by the `PUPPETEER_EXECUTABLE_PATH` and `PUPPETEER_CHROMIUM_REVISION` environment variables. +The executable path. diff --git a/docs/api/puppeteer.puppeteernode.lastlaunchedproduct.md b/docs/api/puppeteer.puppeteernode.lastlaunchedproduct.md new file mode 100644 index 00000000000..f66b0430372 --- /dev/null +++ b/docs/api/puppeteer.puppeteernode.lastlaunchedproduct.md @@ -0,0 +1,13 @@ +--- +sidebar_label: PuppeteerNode.lastLaunchedProduct +--- + +# PuppeteerNode.lastLaunchedProduct property + +**Signature:** + +```typescript +class PuppeteerNode { + get lastLaunchedProduct(): Product; +} +``` diff --git a/docs/api/puppeteer.puppeteernode.launch.md b/docs/api/puppeteer.puppeteernode.launch.md index 2c72d334b7e..8ceb33cee68 100644 --- a/docs/api/puppeteer.puppeteernode.launch.md +++ b/docs/api/puppeteer.puppeteernode.launch.md @@ -4,7 +4,7 @@ sidebar_label: PuppeteerNode.launch # PuppeteerNode.launch() method -Launches puppeteer and launches a browser instance with given arguments and options when specified. +Launches a browser instance with given arguments and options when specified. **Signature:** @@ -16,19 +16,17 @@ class PuppeteerNode { ## Parameters -| Parameter | Type | Description | -| --------- | --------------------------------------------------------------- | -------------------------------------------------------------------- | -| options | [PuppeteerLaunchOptions](./puppeteer.puppeteerlaunchoptions.md) | (Optional) Set of configurable options to set on the browser. | +| Parameter | Type | Description | +| --------- | --------------------------------------------------------------- | ---------------------------------------------------------- | +| options | [PuppeteerLaunchOptions](./puppeteer.puppeteerlaunchoptions.md) | (Optional) Options to configure launching behavior. | **Returns:** Promise<[Browser](./puppeteer.browser.md)> -Promise which resolves to browser instance. - ## Remarks -**NOTE** Puppeteer can also be used to control the Chrome browser, but it works best with the version of Chromium it is bundled with. There is no guarantee it will work with any other version. Use `executablePath` option with extreme caution. If Google Chrome (rather than Chromium) is preferred, a [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested. In `puppeteer.launch([options])`, any mention of Chromium also applies to Chrome. See [this article](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [This article](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users. +Puppeteer can also be used to control the Chrome browser, but it works best with the version of Chromium it is bundled with. There is no guarantee it will work with any other version. Use `executablePath` option with extreme caution. If Google Chrome (rather than Chromium) is preferred, a [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested. In , any mention of Chromium also applies to Chrome. See [this article](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [This article](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users. ## Example diff --git a/docs/api/puppeteer.puppeteernode.md b/docs/api/puppeteer.puppeteernode.md index f264d67c131..b75b5f7105a 100644 --- a/docs/api/puppeteer.puppeteernode.md +++ b/docs/api/puppeteer.puppeteernode.md @@ -44,16 +44,18 @@ Once you have created a `page` you have access to a large API to interact with t ## Properties -| Property | Modifiers | Type | Description | -| ----------------------------------------------- | --------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------- | -| [product](./puppeteer.puppeteernode.product.md) | readonly | string | The name of the browser that is under automation ("chrome" or "firefox") | +| Property | Modifiers | Type | Description | +| ----------------------------------------------------------------------- | --------------------- | --------------------------------- | ----------- | +| [defaultProduct](./puppeteer.puppeteernode.defaultproduct.md) | readonly | [Product](./puppeteer.product.md) | | +| [lastLaunchedProduct](./puppeteer.puppeteernode.lastlaunchedproduct.md) | readonly | [Product](./puppeteer.product.md) | | +| [product](./puppeteer.puppeteernode.product.md) | readonly | string | | ## Methods -| Method | Modifiers | Description | -| ---------------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------- | -| [connect(options)](./puppeteer.puppeteernode.connect.md) | | This method attaches Puppeteer to an existing browser instance. | -| [createBrowserFetcher(options)](./puppeteer.puppeteernode.createbrowserfetcher.md) | | | -| [defaultArgs(options)](./puppeteer.puppeteernode.defaultargs.md) | | | -| [executablePath(channel)](./puppeteer.puppeteernode.executablepath.md) | | | -| [launch(options)](./puppeteer.puppeteernode.launch.md) | | Launches puppeteer and launches a browser instance with given arguments and options when specified. | +| Method | Modifiers | Description | +| ---------------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------- | +| [connect(options)](./puppeteer.puppeteernode.connect.md) | | This method attaches Puppeteer to an existing browser instance. | +| [createBrowserFetcher(options)](./puppeteer.puppeteernode.createbrowserfetcher.md) | | | +| [defaultArgs(options)](./puppeteer.puppeteernode.defaultargs.md) | | | +| [executablePath(channel)](./puppeteer.puppeteernode.executablepath.md) | | | +| [launch(options)](./puppeteer.puppeteernode.launch.md) | | Launches a browser instance with given arguments and options when specified. | diff --git a/docs/api/puppeteer.puppeteernode.product.md b/docs/api/puppeteer.puppeteernode.product.md index ec57a72f579..e5205645bf7 100644 --- a/docs/api/puppeteer.puppeteernode.product.md +++ b/docs/api/puppeteer.puppeteernode.product.md @@ -4,7 +4,9 @@ sidebar_label: PuppeteerNode.product # PuppeteerNode.product property -The name of the browser that is under automation (`"chrome"` or `"firefox"`) +> Warning: This API is now obsolete. +> +> Do not use as this field as it does not take into account multiple browsers of different types. Use or . **Signature:** @@ -13,7 +15,3 @@ class PuppeteerNode { get product(): string; } ``` - -## Remarks - -The product is set by the `PUPPETEER_PRODUCT` environment variable or the `product` option in `puppeteer.launch([options])` and defaults to `chrome`. Firefox support is experimental. diff --git a/package-lock.json b/package-lock.json index d450345bb8b..c0cbbc2d1c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,7 +84,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "dependencies": { "@babel/highlight": "^7.10.4" } @@ -93,7 +92,6 @@ "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -102,7 +100,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -116,7 +113,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -128,7 +124,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -142,7 +137,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -150,14 +144,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -166,7 +158,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -175,7 +166,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -1916,8 +1906,7 @@ "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/pixelmatch": { "version": "5.2.4", @@ -2650,7 +2639,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -2901,7 +2889,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -3135,7 +3122,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -4993,7 +4979,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5009,7 +4994,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -5111,8 +5095,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-bigint": { "version": "1.0.4", @@ -5558,8 +5541,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "3.13.1", @@ -5583,8 +5565,7 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "node_modules/json-schema-traverse": { "version": "1.0.0", @@ -5684,8 +5665,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/load-json-file": { "version": "4.0.0", @@ -6568,7 +6548,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -6580,7 +6559,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -6638,7 +6616,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -8344,7 +8321,6 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, "engines": { "node": ">= 6" } @@ -8492,6 +8468,7 @@ "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { + "cosmiconfig": "7.0.1", "https-proxy-agent": "5.0.1", "progress": "2.0.3", "proxy-from-env": "1.1.0", @@ -8550,7 +8527,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -8558,14 +8534,12 @@ "@babel/helper-validator-identifier": { "version": "7.19.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" }, "@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -8576,7 +8550,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -8585,7 +8558,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -8596,7 +8568,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -8604,26 +8575,22 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -9828,8 +9795,7 @@ "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "@types/pixelmatch": { "version": "5.2.4", @@ -10358,8 +10324,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, "camelcase": { "version": "5.3.1", @@ -10547,7 +10512,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -10718,7 +10682,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -11990,7 +11953,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -11999,8 +11961,7 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" } } }, @@ -12083,8 +12044,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "is-bigint": { "version": "1.0.4", @@ -12407,8 +12367,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.13.1", @@ -12429,8 +12388,7 @@ "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema-traverse": { "version": "1.0.0", @@ -12507,8 +12465,7 @@ "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "load-json-file": { "version": "4.0.0", @@ -13171,7 +13128,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "requires": { "callsites": "^3.0.0" } @@ -13180,7 +13136,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -13222,8 +13177,7 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, "pend": { "version": "1.2.0", @@ -13343,6 +13297,7 @@ "puppeteer": { "version": "file:packages/puppeteer", "requires": { + "cosmiconfig": "7.0.1", "https-proxy-agent": "5.0.1", "progress": "2.0.3", "proxy-from-env": "1.1.0", @@ -14494,8 +14449,7 @@ "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "yargs": { "version": "17.6.0", diff --git a/packages/puppeteer-core/src/constants.ts b/packages/puppeteer-core/src/common/Configuration.ts similarity index 60% rename from packages/puppeteer-core/src/constants.ts rename to packages/puppeteer-core/src/common/Configuration.ts index 7df7742001b..3af0dd2ec55 100644 --- a/packages/puppeteer-core/src/constants.ts +++ b/packages/puppeteer-core/src/common/Configuration.ts @@ -14,11 +14,22 @@ * limitations under the License. */ -import {homedir} from 'os'; -import {join} from 'path'; +import {Product} from './Product.js'; /** - * @internal + * @public */ -export const PUPPETEER_CACHE_DIR = - process.env['PUPPETEER_CACHE_DIR'] ?? join(homedir(), '.cache', 'puppeteer'); +export interface Configuration { + browserRevision?: string; + cacheDirectory?: string; + downloadHost?: string; + downloadPath?: string; + executablePath?: string; + defaultProduct?: Product; + temporaryDirectory?: string; + skipDownload?: boolean; + logLevel?: 'silent' | 'error' | 'warn'; + experiments?: { + macArmChromiumEnabled?: boolean; + }; +} diff --git a/packages/puppeteer-core/src/common/Puppeteer.ts b/packages/puppeteer-core/src/common/Puppeteer.ts index dc0e8ee5061..306e1eb9a87 100644 --- a/packages/puppeteer-core/src/common/Puppeteer.ts +++ b/packages/puppeteer-core/src/common/Puppeteer.ts @@ -107,7 +107,7 @@ export class Puppeteer { /** * @internal */ - protected _isPuppeteerCore: boolean; + _isPuppeteerCore: boolean; /** * @internal */ diff --git a/packages/puppeteer-core/src/node/BrowserFetcher.ts b/packages/puppeteer-core/src/node/BrowserFetcher.ts index b3571a2122e..493518ad5e0 100644 --- a/packages/puppeteer-core/src/node/BrowserFetcher.ts +++ b/packages/puppeteer-core/src/node/BrowserFetcher.ts @@ -16,7 +16,7 @@ import {exec as execChildProcess} from 'child_process'; import extractZip from 'extract-zip'; -import {createReadStream, createWriteStream, existsSync} from 'fs'; +import {createReadStream, createWriteStream, existsSync, readdirSync} from 'fs'; import {chmod, mkdir, readdir, unlink} from 'fs/promises'; import * as http from 'http'; import * as https from 'https'; @@ -35,13 +35,8 @@ import * as util from 'util'; import {promisify} from 'util'; import {debug} from '../common/Debug.js'; import {Product} from '../common/Product.js'; -import {PUPPETEER_CACHE_DIR} from '../constants.js'; import {assert} from '../util/assert.js'; -const experimentalChromiumMacArm = - process.env['PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM'] || - process.env['npm_config_puppeteer_experimental_chromium_mac_arm']; - const debugFetcher = debug('puppeteer:fetcher'); const downloadURLs: Record>> = { @@ -140,24 +135,39 @@ function handleArm64(): void { * @public */ export interface BrowserFetcherOptions { + /** + * Determines the path to download browsers to. + */ + path: string; /** * Determines which platform the browser will be suited for. + * + * @defaultValue Auto-detected. */ platform?: Platform; /** * Determines which product the {@link BrowserFetcher} is for. * - * @defaultValue `"chrome"` + * @defaultValue `"chrome"`. */ product?: 'chrome' | 'firefox'; - /** - * Determines the path to download browsers to. - */ - path?: string; /** * Determines the host that will be used for downloading. + * + * @defaultValue Either + * + * - https://storage.googleapis.com or + * - https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central + * */ host?: string; + + /** + * Enables the use of the Chromium binary for macOS ARM. + * + * @experimental + */ + useMacOSARMBinary?: boolean; } /** @@ -196,7 +206,7 @@ export interface BrowserFetcherRevisionInfo { * and running Puppeteer against it: * * ```ts - * const browserFetcher = new BrowserFetcher(); + * const browserFetcher = new BrowserFetcher({path: 'path/to/download/folder'}); * const revisionInfo = await browserFetcher.download('533271'); * const browser = await puppeteer.launch({ * executablePath: revisionInfo.executablePath, @@ -208,23 +218,17 @@ export interface BrowserFetcherRevisionInfo { export class BrowserFetcher { #product: Product; - #downloadFolder: string; + #downloadPath: string; #downloadHost: string; #platform: Platform; /** * Constructs a browser fetcher for the given options. */ - constructor(options: BrowserFetcherOptions = {}) { - this.#product = (options.product || 'chrome').toLowerCase() as Product; - assert( - this.#product === 'chrome' || this.#product === 'firefox', - `Unknown product: "${options.product}"` - ); - - this.#downloadFolder = - options.path || path.join(PUPPETEER_CACHE_DIR, this.#product); - this.#downloadHost = options.host || browserConfig[this.#product].host; + constructor(options: BrowserFetcherOptions) { + this.#product = options.product ?? 'chrome'; + this.#downloadPath = options.path; + this.#downloadHost = options.host ?? browserConfig[this.#product].host; if (options.platform) { this.#platform = options.platform; @@ -235,7 +239,7 @@ export class BrowserFetcher { switch (this.#product) { case 'chrome': this.#platform = - os.arch() === 'arm64' && experimentalChromiumMacArm + os.arch() === 'arm64' && options.useMacOSARMBinary ? 'mac_arm' : 'mac'; break; @@ -342,13 +346,13 @@ export class BrowserFetcher { ); const fileName = url.split('/').pop(); assert(fileName, `A malformed download URL was found: ${url}.`); - const archivePath = path.join(this.#downloadFolder, fileName); + const archivePath = path.join(this.#downloadPath, fileName); const outputPath = this.#getFolderPath(revision); if (existsSync(outputPath)) { return this.revisionInfo(revision); } - if (!existsSync(this.#downloadFolder)) { - await mkdir(this.#downloadFolder, {recursive: true}); + if (!existsSync(this.#downloadPath)) { + await mkdir(this.#downloadPath, {recursive: true}); } // Use system Chromium builds on Linux ARM devices @@ -374,25 +378,21 @@ export class BrowserFetcher { /** * @remarks * This method is affected by the current `product`. - * @returns A promise with a list of all revision strings (for the current `product`) + * @returns A list of all revision strings (for the current `product`) * available locally on disk. */ - async localRevisions(): Promise { - if (!existsSync(this.#downloadFolder)) { + localRevisions(): string[] { + if (!existsSync(this.#downloadPath)) { return []; } - const fileNames = await readdir(this.#downloadFolder); + const fileNames = readdirSync(this.#downloadPath); return fileNames .map(fileName => { return parseFolderPath(this.#product, fileName); }) - .filter( - ( - entry - ): entry is {product: string; platform: string; revision: string} => { - return (entry && entry.platform === this.#platform) ?? false; - } - ) + .filter((entry): entry is Exclude => { + return (entry && entry.platform === this.#platform) ?? false; + }) .map(entry => { return entry.revision; }); @@ -502,7 +502,7 @@ export class BrowserFetcher { } #getFolderPath(revision: string): string { - return path.resolve(this.#downloadFolder, `${this.#platform}-${revision}`); + return path.resolve(this.#downloadPath, `${this.#platform}-${revision}`); } } diff --git a/packages/puppeteer-core/src/node/ChromeLauncher.ts b/packages/puppeteer-core/src/node/ChromeLauncher.ts index 89722d1ebea..0eec179f371 100644 --- a/packages/puppeteer-core/src/node/ChromeLauncher.ts +++ b/packages/puppeteer-core/src/node/ChromeLauncher.ts @@ -1,7 +1,8 @@ -import fs from 'fs'; +import {accessSync} from 'fs'; +import {mkdtemp} from 'fs/promises'; +import os from 'os'; import path from 'path'; import {CDPBrowser} from '../common/Browser.js'; -import {Product} from '../common/Product.js'; import {assert} from '../util/assert.js'; import {BrowserRunner} from './BrowserRunner.js'; import { @@ -9,32 +10,20 @@ import { ChromeReleaseChannel, PuppeteerNodeLaunchOptions, } from './LaunchOptions.js'; -import { - executablePathForChannel, - ProductLauncher, - resolveExecutablePath, -} from './ProductLauncher.js'; -import {tmpdir} from './util.js'; +import {ProductLauncher} from './ProductLauncher.js'; +import {PuppeteerNode} from './PuppeteerNode.js'; /** * @internal */ -export class ChromeLauncher implements ProductLauncher { - /** - * @internal - */ - _preferredRevision: string; - /** - * @internal - */ - _isPuppeteerCore: boolean; - - constructor(preferredRevision: string, isPuppeteerCore: boolean) { - this._preferredRevision = preferredRevision; - this._isPuppeteerCore = isPuppeteerCore; +export class ChromeLauncher extends ProductLauncher { + constructor(puppeteer: PuppeteerNode) { + super(puppeteer, 'chrome'); } - async launch(options: PuppeteerNodeLaunchOptions = {}): Promise { + override async launch( + options: PuppeteerNodeLaunchOptions = {} + ): Promise { const { ignoreDefaultArgs = false, args = [], @@ -93,9 +82,7 @@ export class ChromeLauncher implements ProductLauncher { if (userDataDirIndex < 0) { isTempUserDataDir = true; chromeArguments.push( - `--user-data-dir=${await fs.promises.mkdtemp( - path.join(tmpdir(), 'puppeteer_dev_chrome_profile-') - )}` + `--user-data-dir=${await mkdtemp(this.getProfilePath())}` ); userDataDirIndex = chromeArguments.length - 1; } @@ -104,20 +91,12 @@ export class ChromeLauncher implements ProductLauncher { assert(typeof userDataDir === 'string', '`--user-data-dir` is malformed'); let chromeExecutable = executablePath; - if (channel) { - // executablePath is detected by channel, so it should not be specified by user. + if (!chromeExecutable) { assert( - !chromeExecutable, - '`executablePath` must not be specified when `channel` is given.' + channel || !this.puppeteer._isPuppeteerCore, + `An \`executablePath\` or \`channel\` must be specified for \`puppeteer-core\`` ); - - chromeExecutable = executablePathForChannel(channel); - } else if (!chromeExecutable) { - const {missingText, executablePath} = resolveExecutablePath(this); - if (missingText) { - throw new Error(missingText); - } - chromeExecutable = executablePath; + chromeExecutable = this.executablePath(channel); } const usePipe = chromeArguments.includes('--remote-debugging-pipe'); @@ -143,7 +122,7 @@ export class ChromeLauncher implements ProductLauncher { usePipe, timeout, slowMo, - preferredRevision: this._preferredRevision, + preferredRevision: this.puppeteer.browserRevision, }); browser = await CDPBrowser._create( this.product, @@ -177,7 +156,7 @@ export class ChromeLauncher implements ProductLauncher { return browser; } - defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] { + override defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] { const chromeArguments = [ '--allow-pre-commit-input', '--disable-background-networking', @@ -241,16 +220,88 @@ export class ChromeLauncher implements ProductLauncher { return chromeArguments; } - executablePath(channel?: ChromeReleaseChannel): string { + override executablePath(channel?: ChromeReleaseChannel): string { if (channel) { - return executablePathForChannel(channel); + return this.#executablePathForChannel(channel); } else { - const results = resolveExecutablePath(this); - return results.executablePath; + return this.resolveExecutablePath(); } } - get product(): Product { - return 'chrome'; + /** + * @internal + */ + #executablePathForChannel(channel: ChromeReleaseChannel): string { + const platform = os.platform(); + + let chromePath: string | undefined; + switch (platform) { + case 'win32': + switch (channel) { + case 'chrome': + chromePath = `${process.env['PROGRAMFILES']}\\Google\\Chrome\\Application\\chrome.exe`; + break; + case 'chrome-beta': + chromePath = `${process.env['PROGRAMFILES']}\\Google\\Chrome Beta\\Application\\chrome.exe`; + break; + case 'chrome-canary': + chromePath = `${process.env['PROGRAMFILES']}\\Google\\Chrome SxS\\Application\\chrome.exe`; + break; + case 'chrome-dev': + chromePath = `${process.env['PROGRAMFILES']}\\Google\\Chrome Dev\\Application\\chrome.exe`; + break; + } + break; + case 'darwin': + switch (channel) { + case 'chrome': + chromePath = + '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; + break; + case 'chrome-beta': + chromePath = + '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta'; + break; + case 'chrome-canary': + chromePath = + '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'; + break; + case 'chrome-dev': + chromePath = + '/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev'; + break; + } + break; + case 'linux': + switch (channel) { + case 'chrome': + chromePath = '/opt/google/chrome/chrome'; + break; + case 'chrome-beta': + chromePath = '/opt/google/chrome-beta/chrome'; + break; + case 'chrome-dev': + chromePath = '/opt/google/chrome-unstable/chrome'; + break; + } + break; + } + + if (!chromePath) { + throw new Error( + `Unable to detect browser executable path for '${channel}' on ${platform}.` + ); + } + + // Check if Chrome exists and is accessible. + try { + accessSync(chromePath); + } catch (error) { + throw new Error( + `Could not find Google Chrome executable for channel '${channel}' at '${chromePath}'.` + ); + } + + return chromePath; } } diff --git a/packages/puppeteer-core/src/node/FirefoxLauncher.ts b/packages/puppeteer-core/src/node/FirefoxLauncher.ts index 8dbea633043..c40b77c4d50 100644 --- a/packages/puppeteer-core/src/node/FirefoxLauncher.ts +++ b/packages/puppeteer-core/src/node/FirefoxLauncher.ts @@ -4,7 +4,6 @@ import path from 'path'; import {Browser} from '../api/Browser.js'; import {Browser as BiDiBrowser} from '../common/bidi/Browser.js'; import {CDPBrowser} from '../common/Browser.js'; -import {Product} from '../common/Product.js'; import {assert} from '../util/assert.js'; import {BrowserFetcher} from './BrowserFetcher.js'; import {BrowserRunner} from './BrowserRunner.js'; @@ -12,33 +11,25 @@ import { BrowserLaunchArgumentOptions, PuppeteerNodeLaunchOptions, } from './LaunchOptions.js'; -import {ProductLauncher, resolveExecutablePath} from './ProductLauncher.js'; -import {tmpdir} from './util.js'; +import {ProductLauncher} from './ProductLauncher.js'; +import {PuppeteerNode} from './PuppeteerNode.js'; /** * @internal */ -export class FirefoxLauncher implements ProductLauncher { - /** - * @internal - */ - _preferredRevision: string; - /** - * @internal - */ - _isPuppeteerCore: boolean; - - constructor(preferredRevision: string, isPuppeteerCore: boolean) { - this._preferredRevision = preferredRevision; - this._isPuppeteerCore = isPuppeteerCore; +export class FirefoxLauncher extends ProductLauncher { + constructor(puppeteer: PuppeteerNode) { + super(puppeteer, 'firefox'); } - async launch(options: PuppeteerNodeLaunchOptions = {}): Promise { + override async launch( + options: PuppeteerNodeLaunchOptions = {} + ): Promise { const { ignoreDefaultArgs = false, args = [], dumpio = false, - executablePath = null, + executablePath, pipe = false, env = process.env, handleSIGINT = true, @@ -107,20 +98,15 @@ export class FirefoxLauncher implements ProductLauncher { firefoxArguments.push(userDataDir); } - if (!this._isPuppeteerCore) { - await this._updateRevision(); - } - let firefoxExecutable = executablePath; - if (!executablePath) { - const {missingText, executablePath} = resolveExecutablePath(this); - if (missingText) { - throw new Error(missingText); - } + let firefoxExecutable: string; + if (this.puppeteer._isPuppeteerCore || executablePath) { + assert( + executablePath, + `An \`executablePath\` must be specified for \`puppeteer-core\`` + ); firefoxExecutable = executablePath; - } - - if (!firefoxExecutable) { - throw new Error('firefoxExecutable is not found.'); + } else { + firefoxExecutable = this.executablePath(); } const runner = new BrowserRunner( @@ -145,7 +131,7 @@ export class FirefoxLauncher implements ProductLauncher { const connection = await runner.setupWebDriverBiDiConnection({ timeout, slowMo, - preferredRevision: this._preferredRevision, + preferredRevision: this.puppeteer.browserRevision, }); browser = await BiDiBrowser.create({ connection, @@ -166,7 +152,7 @@ export class FirefoxLauncher implements ProductLauncher { usePipe: pipe, timeout, slowMo, - preferredRevision: this._preferredRevision, + preferredRevision: this.puppeteer.browserRevision, }); browser = await CDPBrowser._create( this.product, @@ -200,28 +186,22 @@ export class FirefoxLauncher implements ProductLauncher { return browser; } - executablePath(): string { - return resolveExecutablePath(this).executablePath; - } - - async _updateRevision(): Promise { + override executablePath(): string { // replace 'latest' placeholder with actual downloaded revision - if (this._preferredRevision === 'latest') { + if (this.puppeteer.browserRevision === 'latest') { const browserFetcher = new BrowserFetcher({ product: this.product, + path: this.puppeteer.defaultDownloadPath!, }); - const localRevisions = await browserFetcher.localRevisions(); + const localRevisions = browserFetcher.localRevisions(); if (localRevisions[0]) { - this._preferredRevision = localRevisions[0]; + this.puppeteer.configuration.browserRevision = localRevisions[0]; } } + return this.resolveExecutablePath(); } - get product(): Product { - return 'firefox'; - } - - defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] { + override defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] { const { devtools = false, headless = !devtools, @@ -503,7 +483,7 @@ export class FirefoxLauncher implements ProductLauncher { async _createProfile(extraPrefs: {[x: string]: unknown}): Promise { const temporaryProfilePath = await fs.promises.mkdtemp( - path.join(tmpdir(), 'puppeteer_dev_firefox_profile-') + this.getProfilePath() ); const prefs = this.defaultPreferences(extraPrefs); diff --git a/packages/puppeteer-core/src/node/ProductLauncher.ts b/packages/puppeteer-core/src/node/ProductLauncher.ts index 1b2dcbd097b..3f1073e887a 100644 --- a/packages/puppeteer-core/src/node/ProductLauncher.ts +++ b/packages/puppeteer-core/src/node/ProductLauncher.ts @@ -13,192 +13,118 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {accessSync, existsSync} from 'fs'; -import os from 'os'; +import {existsSync} from 'fs'; +import os, {tmpdir} from 'os'; +import {join} from 'path'; import {Browser} from '../api/Browser.js'; import {Product} from '../common/Product.js'; import {BrowserFetcher} from './BrowserFetcher.js'; -import {ChromeLauncher} from './ChromeLauncher.js'; -import {FirefoxLauncher} from './FirefoxLauncher.js'; import { BrowserLaunchArgumentOptions, ChromeReleaseChannel, PuppeteerNodeLaunchOptions, } from './LaunchOptions.js'; +import {PuppeteerNode} from './PuppeteerNode.js'; /** * Describes a launcher - a class that is able to create and launch a browser instance. + * * @public */ -export interface ProductLauncher { +export class ProductLauncher { + #product: Product; + + /** + * @internal + */ + puppeteer: PuppeteerNode; + + /** + * @internal + */ + constructor(puppeteer: PuppeteerNode, product: Product) { + this.puppeteer = puppeteer; + this.#product = product; + } + + get product(): Product { + return this.#product; + } + launch(object: PuppeteerNodeLaunchOptions): Promise; - executablePath: (path?: any) => string; + launch(): Promise { + throw new Error('Not implemented'); + } + + executablePath(channel?: ChromeReleaseChannel): string; + executablePath(): string { + throw new Error('Not implemented'); + } + defaultArgs(object: BrowserLaunchArgumentOptions): string[]; - product: Product; -} - -/** - * @internal - */ -export function executablePathForChannel( - channel: ChromeReleaseChannel -): string { - const platform = os.platform(); - - let chromePath: string | undefined; - switch (platform) { - case 'win32': - switch (channel) { - case 'chrome': - chromePath = `${process.env['PROGRAMFILES']}\\Google\\Chrome\\Application\\chrome.exe`; - break; - case 'chrome-beta': - chromePath = `${process.env['PROGRAMFILES']}\\Google\\Chrome Beta\\Application\\chrome.exe`; - break; - case 'chrome-canary': - chromePath = `${process.env['PROGRAMFILES']}\\Google\\Chrome SxS\\Application\\chrome.exe`; - break; - case 'chrome-dev': - chromePath = `${process.env['PROGRAMFILES']}\\Google\\Chrome Dev\\Application\\chrome.exe`; - break; - } - break; - case 'darwin': - switch (channel) { - case 'chrome': - chromePath = - '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; - break; - case 'chrome-beta': - chromePath = - '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta'; - break; - case 'chrome-canary': - chromePath = - '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'; - break; - case 'chrome-dev': - chromePath = - '/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev'; - break; - } - break; - case 'linux': - switch (channel) { - case 'chrome': - chromePath = '/opt/google/chrome/chrome'; - break; - case 'chrome-beta': - chromePath = '/opt/google/chrome-beta/chrome'; - break; - case 'chrome-dev': - chromePath = '/opt/google/chrome-unstable/chrome'; - break; - } - break; + defaultArgs(): string[] { + throw new Error('Not implemented'); } - if (!chromePath) { - throw new Error( - `Unable to detect browser executable path for '${channel}' on ${platform}.` + /** + * @internal + */ + protected getProfilePath(): string { + return join( + this.puppeteer.configuration.temporaryDirectory ?? tmpdir(), + `puppeteer_dev_${this.product}_profile-` ); } - // Check if Chrome exists and is accessible. - try { - accessSync(chromePath); - } catch (error) { - throw new Error( - `Could not find Google Chrome executable for channel '${channel}' at '${chromePath}'.` - ); - } - - return chromePath; -} - -/** - * @internal - */ -export function resolveExecutablePath( - launcher: ChromeLauncher | FirefoxLauncher -): { - executablePath: string; - missingText?: string; -} { - const {product, _isPuppeteerCore, _preferredRevision} = launcher; - let downloadPath: string | undefined; - // puppeteer-core doesn't take into account PUPPETEER_* env variables. - if (!_isPuppeteerCore) { - const executablePath = - process.env['PUPPETEER_EXECUTABLE_PATH'] || - process.env['npm_config_puppeteer_executable_path'] || - process.env['npm_package_config_puppeteer_executable_path']; + /** + * @internal + */ + protected resolveExecutablePath(): string { + const executablePath = this.puppeteer.configuration.executablePath; if (executablePath) { - const missingText = !existsSync(executablePath) - ? 'Tried to use PUPPETEER_EXECUTABLE_PATH env variable to launch browser but did not find any executable at: ' + - executablePath - : undefined; - return {executablePath, missingText}; + if (!existsSync(executablePath)) { + throw new Error( + `Tried to find the browser at the configured path (${executablePath}), but no executable was found.` + ); + } + return executablePath; } + const ubuntuChromiumPath = '/usr/bin/chromium-browser'; if ( - product === 'chrome' && + this.product === 'chrome' && os.platform() !== 'darwin' && os.arch() === 'arm64' && existsSync(ubuntuChromiumPath) ) { - return {executablePath: ubuntuChromiumPath, missingText: undefined}; + return ubuntuChromiumPath; } - downloadPath = - process.env['PUPPETEER_DOWNLOAD_PATH'] || - process.env['npm_config_puppeteer_download_path'] || - process.env['npm_package_config_puppeteer_download_path']; - } - const browserFetcher = new BrowserFetcher({ - product: product, - path: downloadPath, - }); - if (!_isPuppeteerCore) { - let revision = process.env['PUPPETEER_BROWSER_REVISION']; - if (product === 'chrome') { - revision ??= process.env['PUPPETEER_CHROMIUM_REVISION']; + const browserFetcher = new BrowserFetcher({ + product: this.product, + path: this.puppeteer.defaultDownloadPath!, + }); + + const revisionInfo = browserFetcher.revisionInfo( + this.puppeteer.browserRevision + ); + if (!revisionInfo.local) { + if (this.puppeteer.configuration.browserRevision) { + throw new Error( + `Tried to find the browser at the configured path (${revisionInfo.executablePath}) for revision ${this.puppeteer.browserRevision}, but no executable was found.` + ); + } + switch (this.product) { + case 'chrome': + throw new Error( + `Run \`npm install\` to download the correct Chromium revision (${this.puppeteer.browserRevision}).` + ); + case 'firefox': + throw new Error( + `Run \`PUPPETEER_PRODUCT=firefox npm install\` to download a supported Firefox browser binary.` + ); + } } - if (revision) { - const revisionInfo = browserFetcher.revisionInfo(revision); - const missingText = !revisionInfo.local - ? 'Tried to use PUPPETEER_CHROMIUM_REVISION env variable to launch browser but did not find executable at: ' + - revisionInfo.executablePath - : undefined; - return {executablePath: revisionInfo.executablePath, missingText}; - } - } - const revisionInfo = browserFetcher.revisionInfo(_preferredRevision); - - const firefoxHelp = `Run \`PUPPETEER_PRODUCT=firefox npm install\` to download a supported Firefox browser binary.`; - const chromeHelp = `Run \`npm install\` to download the correct Chromium revision (${launcher._preferredRevision}).`; - const missingText = !revisionInfo.local - ? `Could not find expected browser (${product}) locally. ${ - product === 'chrome' ? chromeHelp : firefoxHelp - }` - : undefined; - return {executablePath: revisionInfo.executablePath, missingText}; -} - -/** - * @internal - */ -export function createLauncher( - preferredRevision: string, - isPuppeteerCore: boolean, - product: Product = 'chrome' -): ProductLauncher { - switch (product) { - case 'firefox': - return new FirefoxLauncher(preferredRevision, isPuppeteerCore); - case 'chrome': - return new ChromeLauncher(preferredRevision, isPuppeteerCore); - default: - throw new Error(`Unknown product: ${product}`); + return revisionInfo.executablePath; } } diff --git a/packages/puppeteer-core/src/node/PuppeteerNode.ts b/packages/puppeteer-core/src/node/PuppeteerNode.ts index f52ee071cbd..44ea343ddc1 100644 --- a/packages/puppeteer-core/src/node/PuppeteerNode.ts +++ b/packages/puppeteer-core/src/node/PuppeteerNode.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import {join} from 'path'; import {Browser} from '../api/Browser.js'; import {BrowserConnectOptions} from '../common/BrowserConnector.js'; import {Product} from '../common/Product.js'; @@ -22,10 +23,17 @@ import { ConnectOptions, Puppeteer, } from '../common/Puppeteer.js'; +import {Configuration} from '../common/Configuration.js'; import {PUPPETEER_REVISIONS} from '../revisions.js'; import {BrowserFetcher, BrowserFetcherOptions} from './BrowserFetcher.js'; -import {BrowserLaunchArgumentOptions, LaunchOptions} from './LaunchOptions.js'; -import {createLauncher, ProductLauncher} from './ProductLauncher.js'; +import {ChromeLauncher} from './ChromeLauncher.js'; +import {FirefoxLauncher} from './FirefoxLauncher.js'; +import { + BrowserLaunchArgumentOptions, + ChromeReleaseChannel, + LaunchOptions, +} from './LaunchOptions.js'; +import {ProductLauncher} from './ProductLauncher.js'; /** * @public @@ -74,28 +82,40 @@ export interface PuppeteerLaunchOptions * @public */ export class PuppeteerNode extends Puppeteer { - #launcher?: ProductLauncher; - #productName?: Product; + #_launcher?: ProductLauncher; + #lastLaunchedProduct?: Product; /** * @internal */ - _preferredRevision = PUPPETEER_REVISIONS.chromium; + defaultBrowserRevision: string; + + /** + * @internal + */ + configuration: Configuration = {}; /** * @internal */ constructor( settings: { - preferredRevision?: string; - productName?: Product; + configuration?: Configuration; } & CommonPuppeteerSettings ) { - const {preferredRevision, productName, ...commonSettings} = settings; + const {configuration, ...commonSettings} = settings; super(commonSettings); - this.#productName = productName; - if (preferredRevision) { - this._preferredRevision = preferredRevision; + if (configuration) { + this.configuration = configuration; + } + switch (this.configuration.defaultProduct) { + case 'firefox': + this.defaultBrowserRevision = PUPPETEER_REVISIONS.firefox; + break; + default: + this.configuration.defaultProduct = 'chrome'; + this.defaultBrowserRevision = PUPPETEER_REVISIONS.chromium; + break; } this.connect = this.connect.bind(this); @@ -116,21 +136,8 @@ export class PuppeteerNode extends Puppeteer { } /** - * @internal - */ - get _productName(): Product | undefined { - return this.#productName; - } - set _productName(name: Product | undefined) { - if (this.#productName !== name) { - this._changedProduct = true; - } - this.#productName = name; - } - - /** - * Launches puppeteer and launches a browser instance with given arguments and - * options when specified. + * Launches a browser instance with given arguments and options when + * specified. * * @example * You can use `ignoreDefaultArgs` to filter out `--mute-audio` from default arguments: @@ -142,90 +149,118 @@ export class PuppeteerNode extends Puppeteer { * ``` * * @remarks - * **NOTE** Puppeteer can also be used to control the Chrome browser, but it - * works best with the version of Chromium it is bundled with. There is no - * guarantee it will work with any other version. Use `executablePath` option - * with extreme caution. If Google Chrome (rather than Chromium) is preferred, - * a {@link https://www.google.com/chrome/browser/canary.html | Chrome Canary} + * Puppeteer can also be used to control the Chrome browser, but it works best + * with the version of Chromium it is bundled with. There is no guarantee it + * will work with any other version. Use `executablePath` option with extreme + * caution. If Google Chrome (rather than Chromium) is preferred, a + * {@link https://www.google.com/chrome/browser/canary.html | Chrome Canary} * or * {@link https://www.chromium.org/getting-involved/dev-channel | Dev Channel} - * build is suggested. In `puppeteer.launch([options])`, any mention of - * Chromium also applies to Chrome. See + * build is suggested. In {@link Puppeteer.launch}, any mention of Chromium + * also applies to Chrome. See * {@link https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/ | this article} * for a description of the differences between Chromium and Chrome. * {@link https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md | This article} * describes some differences for Linux users. * - * @param options - Set of configurable options to set on the browser. - * @returns Promise which resolves to browser instance. + * @param options - Options to configure launching behavior. */ launch(options: PuppeteerLaunchOptions = {}): Promise { - if (options.product) { - this._productName = options.product; - } - return this._launcher.launch(options); - } - - /** - * @remarks - * **NOTE** `puppeteer.executablePath()` is affected by the - * `PUPPETEER_EXECUTABLE_PATH` and `PUPPETEER_CHROMIUM_REVISION` environment - * variables. - * - * @returns A path where Puppeteer expects to find the bundled browser. The - * browser binary might not be there if the download was skipped with the - * `PUPPETEER_SKIP_DOWNLOAD` environment variable. - */ - executablePath(channel?: string): string { - return this._launcher.executablePath(channel); + const {product = this.defaultProduct} = options; + this.#lastLaunchedProduct = product; + return this.#launcher.launch(options); } /** * @internal */ - get _launcher(): ProductLauncher { + get #launcher(): ProductLauncher { if ( - !this.#launcher || - this.#launcher.product !== this._productName || - this._changedProduct + this.#_launcher && + this.#_launcher.product === this.lastLaunchedProduct ) { - switch (this._productName) { - case 'firefox': - this._preferredRevision = PUPPETEER_REVISIONS.firefox; - break; - case 'chrome': - default: - this._preferredRevision = PUPPETEER_REVISIONS.chromium; - } - this._changedProduct = false; - this.#launcher = createLauncher( - this._preferredRevision, - this._isPuppeteerCore, - this._productName - ); + return this.#_launcher; } - return this.#launcher; + switch (this.lastLaunchedProduct) { + case 'chrome': + this.defaultBrowserRevision = PUPPETEER_REVISIONS.chromium; + this.#_launcher = new ChromeLauncher(this); + break; + case 'firefox': + this.defaultBrowserRevision = PUPPETEER_REVISIONS.firefox; + this.#_launcher = new FirefoxLauncher(this); + break; + default: + throw new Error(`Unknown product: ${this.#lastLaunchedProduct}`); + } + return this.#_launcher; } /** - * The name of the browser that is under automation (`"chrome"` or - * `"firefox"`) + * @returns The executable path. + */ + executablePath(channel?: ChromeReleaseChannel): string { + return this.#launcher.executablePath(channel); + } + + /** + * @internal + */ + get browserRevision(): string { + return this.configuration.browserRevision ?? this.defaultBrowserRevision!; + } + + /** + * @returns The default download path for puppeteer. For puppeteer-core, this + * code should never be called as it is never defined. * - * @remarks - * The product is set by the `PUPPETEER_PRODUCT` environment variable or the - * `product` option in `puppeteer.launch([options])` and defaults to `chrome`. - * Firefox support is experimental. + * @internal + */ + get defaultDownloadPath(): string | undefined { + return ( + this.configuration.downloadPath ?? + join(this.configuration.cacheDirectory!, this.product) + ); + } + + /** + * @returns The name of the browser that was last launched. + * + * @public + */ + get lastLaunchedProduct(): Product { + return this.#lastLaunchedProduct ?? this.defaultProduct; + } + + /** + * @returns The name of the browser that will be launched by default. For + * `puppeteer`, this is influenced by your configuration. Otherwise, it's + * `chrome`. + * + * @public + */ + get defaultProduct(): Product { + return this.configuration.defaultProduct ?? 'chrome'; + } + + /** + * @deprecated Do not use as this field as it does not take into account + * multiple browsers of different types. Use {@link defaultProduct} or + * {@link lastLaunchedProduct}. + * + * @returns The name of the browser that is under automation. */ get product(): string { - return this._launcher.product; + return this.#launcher.product; } /** * @param options - Set of configurable options to set on the browser. + * * @returns The default flags that Chromium will be launched with. */ defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] { - return this._launcher.defaultArgs(options); + return this.#launcher.defaultArgs(options); } /** @@ -237,7 +272,22 @@ export class PuppeteerNode extends Puppeteer { * * @returns A new BrowserFetcher instance. */ - createBrowserFetcher(options: BrowserFetcherOptions): BrowserFetcher { - return new BrowserFetcher(options); + createBrowserFetcher( + options: Partial + ): BrowserFetcher { + const downloadPath = this.defaultDownloadPath; + if (downloadPath) { + options.path = downloadPath; + } + if (!options.path) { + throw new Error('A `path` must be specified for `puppeteer-core`.'); + } + if (this.configuration.experiments?.macArmChromiumEnabled) { + options.useMacOSARMBinary = true; + } + if (this.configuration.downloadHost) { + options.host = this.configuration.downloadHost; + } + return new BrowserFetcher(options as BrowserFetcherOptions); } } diff --git a/packages/puppeteer-core/src/node/util.ts b/packages/puppeteer-core/src/node/util.ts deleted file mode 100644 index 9a578972c13..00000000000 --- a/packages/puppeteer-core/src/node/util.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {tmpdir as osTmpDir} from 'os'; - -/** - * Gets the temporary directory, either from the environmental variable - * `PUPPETEER_TMP_DIR` or the `os.tmpdir`. - * - * @returns The temporary directory path. - * - * @internal - */ -export const tmpdir = (): string => { - return process.env['PUPPETEER_TMP_DIR'] || osTmpDir(); -}; diff --git a/packages/puppeteer-core/src/types.ts b/packages/puppeteer-core/src/types.ts index fc8ad33f158..089cc0ec9f5 100644 --- a/packages/puppeteer-core/src/types.ts +++ b/packages/puppeteer-core/src/types.ts @@ -9,6 +9,7 @@ export * from './common/Browser.js'; export * from './common/BrowserConnector.js'; export * from './common/BrowserWebSocketTransport.js'; export * from './common/ChromeTargetManager.js'; +export * from './common/Configuration.js'; export * from './common/Connection.js'; export * from './common/ConnectionTransport.js'; export * from './common/ConsoleMessage.js'; @@ -55,7 +56,6 @@ export * from './common/USKeyboardLayout.js'; export * from './common/util.js'; export * from './common/WaitTask.js'; export * from './common/WebWorker.js'; -export * from './constants.js'; export * from './environment.js'; export * from './generated/injected.js'; export * from './generated/version.js'; @@ -67,7 +67,6 @@ export * from './node/LaunchOptions.js'; export * from './node/PipeTransport.js'; export * from './node/ProductLauncher.js'; export * from './node/PuppeteerNode.js'; -export * from './node/util.js'; export * from './puppeteer-core.js'; export * from './revisions.js'; export * from './util/assert.js'; diff --git a/packages/puppeteer/install.js b/packages/puppeteer/install.js index cfd1a502eb0..a8607c569ec 100644 --- a/packages/puppeteer/install.js +++ b/packages/puppeteer/install.js @@ -28,69 +28,13 @@ const path = require('path'); const fs = require('fs'); const {execSync} = require('child_process'); -async function download() { - if (!fs.existsSync(path.join(__dirname, 'lib'))) { - console.log('It seems we are installing from the git repo.'); - console.log('Building install tools from scratch...'); - execSync('npm run build'); - } - - // need to ensure TS is compiled before loading the installer - const { - downloadBrowser, - logPolitely, - } = require('puppeteer/lib/cjs/puppeteer/node/install.js'); - - if (process.env.PUPPETEER_SKIP_DOWNLOAD) { - logPolitely( - '**INFO** Skipping browser download. "PUPPETEER_SKIP_DOWNLOAD" environment variable was found.' - ); - return; - } - if ( - process.env.NPM_CONFIG_PUPPETEER_SKIP_DOWNLOAD || - process.env.npm_config_puppeteer_skip_download - ) { - logPolitely( - '**INFO** Skipping browser download. "PUPPETEER_SKIP_DOWNLOAD" was set in npm config.' - ); - return; - } - if ( - process.env.NPM_PACKAGE_CONFIG_PUPPETEER_SKIP_DOWNLOAD || - process.env.npm_package_config_puppeteer_skip_download - ) { - logPolitely( - '**INFO** Skipping browser download. "PUPPETEER_SKIP_DOWNLOAD" was set in project config.' - ); - return; - } - if (process.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD) { - logPolitely( - '**INFO** Skipping browser download. "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" environment variable was found.' - ); - return; - } - if ( - process.env.NPM_CONFIG_PUPPETEER_SKIP_CHROMIUM_DOWNLOAD || - process.env.npm_config_puppeteer_skip_chromium_download - ) { - logPolitely( - '**INFO** Skipping browser download. "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" was set in npm config.' - ); - return; - } - if ( - process.env.NPM_PACKAGE_CONFIG_PUPPETEER_SKIP_CHROMIUM_DOWNLOAD || - process.env.npm_package_config_puppeteer_skip_chromium_download - ) { - logPolitely( - '**INFO** Skipping browser download. "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" was set in project config.' - ); - return; - } - - downloadBrowser(); +// Need to ensure TS is compiled before loading the installer +if (!fs.existsSync(path.join(__dirname, 'lib'))) { + console.log('It seems we are installing from the git repo.'); + console.log('Building install tools from scratch...'); + execSync('npm run build --workspace puppeteer'); } -download(); +const {downloadBrowser} = require('puppeteer/internal/node/install.js'); + +downloadBrowser(); diff --git a/packages/puppeteer/package.json b/packages/puppeteer/package.json index b8b7c2a89ce..25f9b86dc73 100644 --- a/packages/puppeteer/package.json +++ b/packages/puppeteer/package.json @@ -135,6 +135,7 @@ "author": "The Chromium Authors", "license": "Apache-2.0", "dependencies": { + "cosmiconfig": "7.0.1", "https-proxy-agent": "5.0.1", "progress": "2.0.3", "proxy-from-env": "1.1.0", diff --git a/packages/puppeteer/src/getConfiguration.ts b/packages/puppeteer/src/getConfiguration.ts new file mode 100644 index 00000000000..4eb8acaa102 --- /dev/null +++ b/packages/puppeteer/src/getConfiguration.ts @@ -0,0 +1,95 @@ +import {cosmiconfigSync} from 'cosmiconfig'; +import {homedir} from 'os'; +import {join} from 'path'; +import {Configuration, Product} from 'puppeteer-core'; + +/** + * @internal + */ +function isSupportedProduct(product: unknown): product is Product { + switch (product) { + case 'chrome': + case 'firefox': + return true; + default: + return false; + } +} + +/** + * @internal + */ +export const getConfiguration = (): Configuration => { + const result = cosmiconfigSync('puppeteer').search(); + const configuration: Configuration = result ? result.config : {}; + + // Merging environment variables. + configuration.browserRevision = + process.env['PUPPETEER_CHROMIUM_REVISION'] ?? + process.env['PUPPETEER_BROWSER_REVISION'] ?? + process.env['npm_config_puppeteer_browser_revision'] ?? + process.env['npm_package_config_puppeteer_browser_revision'] ?? + configuration.browserRevision; + configuration.cacheDirectory = + process.env['PUPPETEER_CACHE_DIR'] ?? + process.env['npm_config_puppeteer_cache_dir'] ?? + process.env['npm_package_config_puppeteer_cache_dir'] ?? + configuration.cacheDirectory ?? + join(homedir(), '.cache', 'puppeteer'); + configuration.downloadHost = + process.env['PUPPETEER_DOWNLOAD_HOST'] ?? + process.env['npm_config_puppeteer_download_host'] ?? + process.env['npm_package_config_puppeteer_download_host'] ?? + configuration.downloadHost; + configuration.downloadPath = + process.env['PUPPETEER_DOWNLOAD_PATH'] ?? + process.env['npm_config_puppeteer_download_path'] ?? + process.env['npm_package_config_puppeteer_download_path'] ?? + configuration.downloadPath; + configuration.executablePath = + process.env['PUPPETEER_EXECUTABLE_PATH'] ?? + process.env['npm_config_puppeteer_executable_path'] ?? + process.env['npm_package_config_puppeteer_executable_path'] ?? + configuration.executablePath; + configuration.defaultProduct = (process.env['PUPPETEER_PRODUCT'] ?? + process.env['npm_config_puppeteer_product'] ?? + process.env['npm_package_config_puppeteer_product'] ?? + configuration.defaultProduct ?? + 'chrome') as Product; + configuration.temporaryDirectory = + process.env['PUPPETEER_TMP_DIR'] ?? + process.env['npm_config_puppeteer_tmp_dir'] ?? + process.env['npm_package_config_puppeteer_tmp_dir'] ?? + configuration.temporaryDirectory; + + configuration.experiments ??= {}; + configuration.experiments.macArmChromiumEnabled = Boolean( + process.env['PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM'] ?? + process.env['npm_config_puppeteer_experimental_chromium_mac_arm'] ?? + process.env[ + 'npm_package_config_puppeteer_experimental_chromium_mac_arm' + ] ?? + configuration.experiments.macArmChromiumEnabled + ); + + configuration.skipDownload = Boolean( + process.env['PUPPETEER_SKIP_DOWNLOAD'] ?? + process.env['npm_config_puppeteer_skip_download'] ?? + process.env['npm_package_config_puppeteer_skip_download'] ?? + process.env['PUPPETEER_SKIP_CHROMIUM_DOWNLOAD'] ?? + process.env['npm_config_puppeteer_skip_chromium_download'] ?? + process.env['npm_package_config_puppeteer_skip_chromium_download'] ?? + configuration.skipDownload + ); + configuration.logLevel = (process.env['PUPPETEER_LOGLEVEL'] ?? + process.env['npm_config_LOGLEVEL'] ?? + process.env['npm_package_config_LOGLEVEL'] ?? + configuration.logLevel) as 'silent' | 'error' | 'warn'; + + // Validate configuration. + if (!isSupportedProduct(configuration.defaultProduct)) { + throw new Error(`Unsupported product ${configuration.defaultProduct}`); + } + + return configuration; +}; diff --git a/packages/puppeteer/src/node/install.ts b/packages/puppeteer/src/node/install.ts index 476056d9478..6fa27c7924d 100644 --- a/packages/puppeteer/src/node/install.ts +++ b/packages/puppeteer/src/node/install.ts @@ -16,13 +16,13 @@ import https, {RequestOptions} from 'https'; import createHttpsProxyAgent, {HttpsProxyAgentOptions} from 'https-proxy-agent'; +import {join} from 'path'; import ProgressBar from 'progress'; import {getProxyForUrl} from 'proxy-from-env'; import {BrowserFetcher} from 'puppeteer-core'; -import {PuppeteerNode} from 'puppeteer-core/internal/node/PuppeteerNode.js'; import {PUPPETEER_REVISIONS} from 'puppeteer-core/internal/revisions.js'; import URL from 'url'; -import puppeteer from '../puppeteer.js'; +import {getConfiguration} from '../getConfiguration.js'; /** * @internal @@ -32,61 +32,40 @@ const supportedProducts = { firefox: 'Firefox Nightly', } as const; -/** - * @internal - */ -function getProduct(input: string): 'chrome' | 'firefox' { - if (input !== 'chrome' && input !== 'firefox') { - throw new Error(`Unsupported product ${input}`); - } - return input; -} - /** * @internal */ export async function downloadBrowser(): Promise { - const downloadHost = - process.env['PUPPETEER_DOWNLOAD_HOST'] || - process.env['npm_config_puppeteer_download_host'] || - process.env['npm_package_config_puppeteer_download_host']; - const product = getProduct( - process.env['PUPPETEER_PRODUCT'] || - process.env['npm_config_puppeteer_product'] || - process.env['npm_package_config_puppeteer_product'] || - 'chrome' - ); - const downloadPath = - process.env['PUPPETEER_DOWNLOAD_PATH'] || - process.env['npm_config_puppeteer_download_path'] || - process.env['npm_package_config_puppeteer_download_path']; + const configuration = getConfiguration(); + if (configuration.skipDownload) { + logPolitely('**INFO** Skipping browser download as instructed.'); + } + + const product = configuration.defaultProduct!; const browserFetcher = new BrowserFetcher({ product, - host: downloadHost, - path: downloadPath, + host: configuration.downloadHost, + path: + configuration.downloadPath ?? + join(configuration.cacheDirectory!, product), }); - const revision = await getRevision(); - await fetchBinary(revision); - async function getRevision(): Promise { - if (product === 'chrome') { - return ( - process.env['PUPPETEER_CHROMIUM_REVISION'] || - process.env['npm_config_puppeteer_chromium_revision'] || - PUPPETEER_REVISIONS.chromium - ); - } else if (product === 'firefox') { - (puppeteer as PuppeteerNode)._preferredRevision = - PUPPETEER_REVISIONS.firefox; - return getFirefoxNightlyVersion().catch(error => { - console.error(error); - process.exit(1); - }); - } else { - throw new Error(`Unsupported product ${product}`); + let revision = configuration.browserRevision; + + if (!revision) { + switch (product) { + case 'chrome': + revision = PUPPETEER_REVISIONS.chromium; + break; + case 'firefox': + revision = PUPPETEER_REVISIONS.firefox; + revision = await getFirefoxNightlyVersion(); + break; } } + await fetchBinary(revision); + function fetchBinary(revision: string) { const revisionInfo = browserFetcher.revisionInfo(revision); @@ -222,7 +201,7 @@ export async function downloadBrowser(): Promise { /** * @internal */ -export function logPolitely(toBeLogged: unknown): void { +function logPolitely(toBeLogged: unknown): void { const logLevel = process.env['npm_config_loglevel'] || ''; const logLevelDisplay = ['silent', 'error', 'warn'].indexOf(logLevel) > -1; diff --git a/packages/puppeteer/src/puppeteer.ts b/packages/puppeteer/src/puppeteer.ts index b31a54ec3c9..3c377abcf2d 100644 --- a/packages/puppeteer/src/puppeteer.ts +++ b/packages/puppeteer/src/puppeteer.ts @@ -19,37 +19,24 @@ export * from 'puppeteer-core/internal/common/Device.js'; export * from 'puppeteer-core/internal/common/Errors.js'; export * from 'puppeteer-core/internal/common/PredefinedNetworkConditions.js'; export * from 'puppeteer-core/internal/common/Puppeteer.js'; -export * from 'puppeteer-core/internal/node/BrowserFetcher.js'; /** * @deprecated Use the query handler API defined on {@link Puppeteer} */ export * from 'puppeteer-core/internal/common/QueryHandler.js'; +export * from 'puppeteer-core/internal/node/BrowserFetcher.js'; export {LaunchOptions} from 'puppeteer-core/internal/node/LaunchOptions.js'; -import {Product} from 'puppeteer-core'; import {PuppeteerNode} from 'puppeteer-core/internal/node/PuppeteerNode.js'; -import {PUPPETEER_REVISIONS} from 'puppeteer-core/internal/revisions.js'; +import {getConfiguration} from './getConfiguration.js'; -const productName = (process.env['PUPPETEER_PRODUCT'] || - process.env['npm_config_puppeteer_product'] || - process.env['npm_package_config_puppeteer_product']) as Product; - -let preferredRevision: string; -switch (productName) { - case 'firefox': - preferredRevision = PUPPETEER_REVISIONS.firefox; - break; - default: - preferredRevision = PUPPETEER_REVISIONS.chromium; -} +const configuration = getConfiguration(); /** * @public */ const puppeteer = new PuppeteerNode({ - preferredRevision, isPuppeteerCore: false, - productName, + configuration, }); export const { diff --git a/packages/puppeteer/src/types.ts b/packages/puppeteer/src/types.ts index 41e0ffac476..3bb1ff15655 100644 --- a/packages/puppeteer/src/types.ts +++ b/packages/puppeteer/src/types.ts @@ -9,6 +9,7 @@ export * from 'puppeteer-core/internal/common/Browser.js'; export * from 'puppeteer-core/internal/common/BrowserConnector.js'; export * from 'puppeteer-core/internal/common/BrowserWebSocketTransport.js'; export * from 'puppeteer-core/internal/common/ChromeTargetManager.js'; +export * from 'puppeteer-core/internal/common/Configuration.js'; export * from 'puppeteer-core/internal/common/Connection.js'; export * from 'puppeteer-core/internal/common/ConnectionTransport.js'; export * from 'puppeteer-core/internal/common/ConsoleMessage.js'; @@ -55,7 +56,6 @@ export * from 'puppeteer-core/internal/common/USKeyboardLayout.js'; export * from 'puppeteer-core/internal/common/util.js'; export * from 'puppeteer-core/internal/common/WaitTask.js'; export * from 'puppeteer-core/internal/common/WebWorker.js'; -export * from 'puppeteer-core/internal/constants.js'; export * from 'puppeteer-core/internal/environment.js'; export * from 'puppeteer-core/internal/generated/injected.js'; export * from 'puppeteer-core/internal/generated/version.js'; @@ -67,10 +67,10 @@ export * from 'puppeteer-core/internal/node/LaunchOptions.js'; export * from 'puppeteer-core/internal/node/PipeTransport.js'; export * from 'puppeteer-core/internal/node/ProductLauncher.js'; export * from 'puppeteer-core/internal/node/PuppeteerNode.js'; -export * from 'puppeteer-core/internal/node/util.js'; export * from 'puppeteer-core/internal/revisions.js'; export * from 'puppeteer-core/internal/util/assert.js'; export * from 'puppeteer-core/internal/util/DebuggableDeferredPromise.js'; export * from 'puppeteer-core/internal/util/DeferredPromise.js'; export * from 'puppeteer-core/internal/util/ErrorLike.js'; +export * from './getConfiguration.js'; export * from './puppeteer.js'; diff --git a/test/installation/assets/puppeteer/configuration/.puppeteerrc.cjs b/test/installation/assets/puppeteer/configuration/.puppeteerrc.cjs new file mode 100644 index 00000000000..8be5af00373 --- /dev/null +++ b/test/installation/assets/puppeteer/configuration/.puppeteerrc.cjs @@ -0,0 +1,8 @@ +const {join} = require('path'); + +/** + * @type {import("puppeteer").PuppeteerConfiguration} + */ +module.exports = { + cacheDirectory: join(__dirname, '.cache', 'puppeteer'), +}; diff --git a/test/installation/src/describeInstallation.ts b/test/installation/src/describeInstallation.ts index 3d1c3f89256..ebf0e4cf69c 100644 --- a/test/installation/src/describeInstallation.ts +++ b/test/installation/src/describeInstallation.ts @@ -51,6 +51,7 @@ export interface DescribeInstallationOptions { * This should be idempotent. */ env?: ((cwd: string) => NodeJS.ProcessEnv) | NodeJS.ProcessEnv; + before?: (cwd: string) => Promise; } export interface DescribeInstallationContext { @@ -92,6 +93,10 @@ export const describeInstallation = ( }; } env = {...process.env, ...getEnv(sandbox)}; + + if (options.before) { + await options.before(sandbox); + } }); after(async () => { diff --git a/test/installation/src/puppeteer-configuration.spec.ts b/test/installation/src/puppeteer-configuration.spec.ts new file mode 100644 index 00000000000..03e0169da53 --- /dev/null +++ b/test/installation/src/puppeteer-configuration.spec.ts @@ -0,0 +1,43 @@ +/** + * Copyright 2022 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 {readdir, writeFile} from 'fs/promises'; +import {join} from 'path'; +import {describeInstallation} from './describeInstallation.js'; +import {readAsset} from './util.js'; + +describeInstallation( + '`puppeteer` with configuration', + { + dependencies: ['puppeteer-core', 'puppeteer'], + before: async cwd => { + await writeFile( + join(cwd, '.puppeteerrc.cjs'), + await readAsset('puppeteer', 'configuration', '.puppeteerrc.cjs') + ); + }, + }, + ({itEvaluates}) => { + itEvaluates('properly', async cwd => { + const files = await readdir(join(cwd, '.cache', 'puppeteer')); + assert.equal(files.length, 1); + assert.equal(files[0], 'chrome'); + + return readAsset('puppeteer', 'basic.js'); + }); + } +); diff --git a/test/src/launcher.spec.ts b/test/src/launcher.spec.ts index 2da6679ed29..477d0236e05 100644 --- a/test/src/launcher.spec.ts +++ b/test/src/launcher.spec.ts @@ -20,7 +20,6 @@ import os from 'os'; import path from 'path'; import {BrowserFetcher, TimeoutError} from 'puppeteer'; import {Page} from 'puppeteer-core/internal/api/Page.js'; -import {Product} from 'puppeteer-core/internal/common/Product.js'; import rimraf from 'rimraf'; import sinon from 'sinon'; import {TLSSocket} from 'tls'; @@ -259,7 +258,8 @@ describe('Launcher specs', function () { const testTmpDir = await fs.promises.mkdtemp( path.join(os.tmpdir(), 'puppeteer_test_chrome_profile-') ); - process.env['PUPPETEER_TMP_DIR'] = testTmpDir; + const oldTmpDir = puppeteer.configuration.temporaryDirectory; + puppeteer.configuration.temporaryDirectory = testTmpDir; // Path should be empty before starting the browser. expect(fs.readdirSync(testTmpDir).length).toEqual(0); @@ -277,8 +277,9 @@ describe('Launcher specs', function () { await browser.close(); // Profile should be deleted after closing the browser expect(fs.readdirSync(testTmpDir).length).toEqual(0); + // Restore env var - process.env['PUPPETEER_TMP_DIR'] = ''; + puppeteer.configuration.temporaryDirectory = oldTmpDir; }); it('userDataDir option restores preferences', async () => { const {defaultBrowserOptions, puppeteer} = getTestState(); @@ -624,21 +625,6 @@ describe('Launcher specs', function () { }); describe('Puppeteer.launch', function () { - let productName!: Product; - - before(async () => { - const {puppeteer} = getTestState(); - productName = puppeteer._productName!; - }); - - after(async () => { - const {puppeteer} = getTestState(); - // @ts-expect-error launcher is a private property that users can't - // touch, but for testing purposes we need to reset it. - puppeteer._lazyLauncher = undefined; - puppeteer._productName = productName; - }); - itOnlyRegularInstall('should be able to launch Chrome', async () => { const {puppeteer} = getTestState(); const browser = await puppeteer.launch({product: 'chrome'}); @@ -890,26 +876,29 @@ describe('Launcher specs', function () { const executablePath = puppeteer.executablePath('chrome'); expect(executablePath).toBeTruthy(); }); - describe('when PUPPETEER_EXECUTABLE_PATH is set', () => { + describe('when executable path is configured', () => { const sandbox = sinon.createSandbox(); beforeEach(() => { - process.env['PUPPETEER_EXECUTABLE_PATH'] = ''; + const {puppeteer} = getTestState(); sandbox - .stub(process.env, 'PUPPETEER_EXECUTABLE_PATH') + .stub(puppeteer.configuration, 'executablePath') .value('SOME_CUSTOM_EXECUTABLE'); }); afterEach(() => { - return sandbox.restore(); + sandbox.restore(); }); - it('its value is returned', async () => { + it('its value is used', async () => { const {puppeteer} = getTestState(); - - const executablePath = puppeteer.executablePath(); - - expect(executablePath).toEqual('SOME_CUSTOM_EXECUTABLE'); + try { + puppeteer.executablePath(); + } catch (error) { + expect((error as Error).message).toContain( + 'SOME_CUSTOM_EXECUTABLE' + ); + } }); }); @@ -930,26 +919,29 @@ describe('Launcher specs', function () { osArchStub.restore(); fsExistsStub.restore(); }); - describe('and PUPPETEER_EXECUTABLE_PATH is set', () => { + describe('and the executable path is configured', () => { const sandbox = sinon.createSandbox(); beforeEach(() => { - process.env['PUPPETEER_EXECUTABLE_PATH'] = ''; + const {puppeteer} = getTestState(); sandbox - .stub(process.env, 'PUPPETEER_EXECUTABLE_PATH') + .stub(puppeteer.configuration, 'executablePath') .value('SOME_CUSTOM_EXECUTABLE'); }); afterEach(() => { - return sandbox.restore(); + sandbox.restore(); }); - it('its value is returned', async () => { + it('its value is used', async () => { const {puppeteer} = getTestState(); - - const executablePath = puppeteer.executablePath(); - - expect(executablePath).toEqual('SOME_CUSTOM_EXECUTABLE'); + try { + puppeteer.executablePath(); + } catch (error) { + expect((error as Error).message).toContain( + 'SOME_CUSTOM_EXECUTABLE' + ); + } }); }); }); @@ -961,9 +953,9 @@ describe('Launcher specs', function () { const fsExistsStub = sinon.stub(fs, 'existsSync'); fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(false); - const executablePath = puppeteer.executablePath(); - - expect(executablePath).not.toEqual('/usr/bin/chromium-browser'); + expect(() => { + return puppeteer.executablePath(); + }).toThrowError(); osPlatformStub.restore(); osArchStub.restore(); diff --git a/test/src/mocha-utils.ts b/test/src/mocha-utils.ts index 9980b51cc70..56da13b6d3a 100644 --- a/test/src/mocha-utils.ts +++ b/test/src/mocha-utils.ts @@ -101,14 +101,6 @@ const defaultBrowserOptions = Object.assign( `WARN: running ${product} tests with ${defaultBrowserOptions.executablePath}` ); } else { - // TODO(jackfranklin): declare updateRevision in some form for the Firefox - // launcher. - if (product === 'firefox') { - // @ts-expect-error _updateRevision is defined on the FF launcher - // but not the Chrome one. The types need tidying so that TS can infer that - // properly and not error here. - await puppeteer._launcher._updateRevision(); - } const executablePath = puppeteer.executablePath(); if (!fs.existsSync(executablePath)) { throw new Error(