From 06b308e2b1a771a8f1913fe7f83238b61c7b8b44 Mon Sep 17 00:00:00 2001 From: Randolf Date: Wed, 19 Oct 2022 18:01:24 +0200 Subject: [PATCH] feat: use configuration files --- docs/api/index.md | 5 +- .../puppeteer.browserfetcher._constructor_.md | 8 +- ...puppeteer.browserfetcher.localrevisions.md | 4 +- 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.downloadhost.md | 13 + .../puppeteer.configuration.downloadpath.md | 13 + .../puppeteer.configuration.executablepath.md | 13 + .../puppeteer.configuration.experiments.md | 15 ++ docs/api/puppeteer.configuration.md | 24 ++ docs/api/puppeteer.configuration.product.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 +- ...er.installationconfiguration.httpsproxy.md | 13 + ...teer.installationconfiguration.loglevel.md | 13 + .../puppeteer.installationconfiguration.md | 19 ++ ....installationconfiguration.skipdownload.md | 13 + 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 +- docs/api/puppeteer.puppeteerconfiguration.md | 14 ++ ...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 ++----- .../src/{constants.ts => Configuration.ts} | 19 +- .../puppeteer-core/src/common/Puppeteer.ts | 2 +- .../puppeteer-core/src/node/BrowserFetcher.ts | 76 +++--- .../puppeteer-core/src/node/ChromeLauncher.ts | 142 +++++++---- .../src/node/FirefoxLauncher.ts | 74 ++---- .../src/node/ProductLauncher.ts | 234 ++++++------------ .../puppeteer-core/src/node/PuppeteerNode.ts | 209 ++++++++++------ 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 + .../puppeteer/src/PuppeteerConfiguration.ts | 98 ++++++++ packages/puppeteer/src/node/install.ts | 74 +++--- packages/puppeteer/src/puppeteer.ts | 21 +- packages/puppeteer/src/types.ts | 4 +- test/src/launcher.spec.ts | 70 +++--- test/src/mocha-utils.ts | 8 - 55 files changed, 913 insertions(+), 698 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.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.md create mode 100644 docs/api/puppeteer.configuration.product.md create mode 100644 docs/api/puppeteer.configuration.temporarydirectory.md create mode 100644 docs/api/puppeteer.installationconfiguration.httpsproxy.md create mode 100644 docs/api/puppeteer.installationconfiguration.loglevel.md create mode 100644 docs/api/puppeteer.installationconfiguration.md create mode 100644 docs/api/puppeteer.installationconfiguration.skipdownload.md create mode 100644 docs/api/puppeteer.puppeteerconfiguration.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 => Configuration.ts} (65%) delete mode 100644 packages/puppeteer-core/src/node/util.ts create mode 100644 packages/puppeteer/src/PuppeteerConfiguration.ts diff --git a/docs/api/index.md b/docs/api/index.md index 2fa726570b2ee..58ca6f815f538 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) | | @@ -86,6 +88,7 @@ sidebar_label: API | [FrameAddStyleTagOptions](./puppeteer.frameaddstyletagoptions.md) | | | [FrameWaitForFunctionOptions](./puppeteer.framewaitforfunctionoptions.md) | | | [GeolocationOptions](./puppeteer.geolocationoptions.md) | | +| [InstallationConfiguration](./puppeteer.installationconfiguration.md) | | | [InterceptResolutionState](./puppeteer.interceptresolutionstate.md) | | | [InternalNetworkConditions](./puppeteer.internalnetworkconditions.md) | | | [JSCoverageEntry](./puppeteer.jscoverageentry.md) | The CoverageEntry class for JavaScript | @@ -102,7 +105,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) | | @@ -161,6 +163,7 @@ sidebar_label: API | [Platform](./puppeteer.platform.md) | Supported platforms. | | [Product](./puppeteer.product.md) | Supported products. | | [ProtocolLifeCycleEvent](./puppeteer.protocollifecycleevent.md) | | +| [PuppeteerConfiguration](./puppeteer.puppeteerconfiguration.md) | | | [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) | | | [PuppeteerNodeLaunchOptions](./puppeteer.puppeteernodelaunchoptions.md) | Utility type exposed to enable users to define options that can be passed to puppeteer.launch without having to list the set of all types. | | [ResourceType](./puppeteer.resourcetype.md) | Resource types for HTTPRequests as perceived by the rendering engine. | diff --git a/docs/api/puppeteer.browserfetcher._constructor_.md b/docs/api/puppeteer.browserfetcher._constructor_.md index 6dd5d0ccb4f02..833a365a490e1 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 8f62f6bc15fae..8f7a6f6f51e89 100644 --- a/docs/api/puppeteer.browserfetcher.localrevisions.md +++ b/docs/api/puppeteer.browserfetcher.localrevisions.md @@ -8,13 +8,13 @@ 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. diff --git a/docs/api/puppeteer.browserfetcher.md b/docs/api/puppeteer.browserfetcher.md index 84fd68f9cd7ff..685637ee7ff30 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 4817cedc73d52..aac9f900cda8a 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 61e7ebfb7e422..e67cd6aceda10 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 0000000000000..87ae5ef11a3e0 --- /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 0000000000000..49b6c20c0e50c --- /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 0000000000000..c7443b423653f --- /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.downloadhost.md b/docs/api/puppeteer.configuration.downloadhost.md new file mode 100644 index 0000000000000..9b8ef482d2923 --- /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 0000000000000..befa69132a531 --- /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 0000000000000..8c7fe082d285e --- /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 0000000000000..9f4ea2348f84e --- /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.md b/docs/api/puppeteer.configuration.md new file mode 100644 index 0000000000000..17d55d5005f68 --- /dev/null +++ b/docs/api/puppeteer.configuration.md @@ -0,0 +1,24 @@ +--- +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) | +| [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) | +| [product?](./puppeteer.configuration.product.md) | | [Product](./puppeteer.product.md) | (Optional) | +| [temporaryDirectory?](./puppeteer.configuration.temporarydirectory.md) | | string | (Optional) | diff --git a/docs/api/puppeteer.configuration.product.md b/docs/api/puppeteer.configuration.product.md new file mode 100644 index 0000000000000..025416306df06 --- /dev/null +++ b/docs/api/puppeteer.configuration.product.md @@ -0,0 +1,13 @@ +--- +sidebar_label: Configuration.product +--- + +# Configuration.product property + +**Signature:** + +```typescript +interface Configuration { + product?: Product; +} +``` diff --git a/docs/api/puppeteer.configuration.temporarydirectory.md b/docs/api/puppeteer.configuration.temporarydirectory.md new file mode 100644 index 0000000000000..27c298aad9f2e --- /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 8cda9ef142dae..a2dad8d61cc4f 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 1af8a3b1009de..62b786c57d0fd 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 44cc2c6c0deca..74e299a9c4df6 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.installationconfiguration.httpsproxy.md b/docs/api/puppeteer.installationconfiguration.httpsproxy.md new file mode 100644 index 0000000000000..3f043f797cc66 --- /dev/null +++ b/docs/api/puppeteer.installationconfiguration.httpsproxy.md @@ -0,0 +1,13 @@ +--- +sidebar_label: InstallationConfiguration.httpsProxy +--- + +# InstallationConfiguration.httpsProxy property + +**Signature:** + +```typescript +interface InstallationConfiguration { + httpsProxy?: string; +} +``` diff --git a/docs/api/puppeteer.installationconfiguration.loglevel.md b/docs/api/puppeteer.installationconfiguration.loglevel.md new file mode 100644 index 0000000000000..1d76dee904d10 --- /dev/null +++ b/docs/api/puppeteer.installationconfiguration.loglevel.md @@ -0,0 +1,13 @@ +--- +sidebar_label: InstallationConfiguration.logLevel +--- + +# InstallationConfiguration.logLevel property + +**Signature:** + +```typescript +interface InstallationConfiguration { + logLevel?: 'silent' | 'error' | 'warn'; +} +``` diff --git a/docs/api/puppeteer.installationconfiguration.md b/docs/api/puppeteer.installationconfiguration.md new file mode 100644 index 0000000000000..c887cc74fa2b7 --- /dev/null +++ b/docs/api/puppeteer.installationconfiguration.md @@ -0,0 +1,19 @@ +--- +sidebar_label: InstallationConfiguration +--- + +# InstallationConfiguration interface + +**Signature:** + +```typescript +export interface InstallationConfiguration +``` + +## Properties + +| Property | Modifiers | Type | Description | +| ---------------------------------------------------------------------- | --------- | ----------------------------- | ----------------- | +| [httpsProxy?](./puppeteer.installationconfiguration.httpsproxy.md) | | string | (Optional) | +| [logLevel?](./puppeteer.installationconfiguration.loglevel.md) | | 'silent' \| 'error' \| 'warn' | (Optional) | +| [skipDownload?](./puppeteer.installationconfiguration.skipdownload.md) | | boolean | (Optional) | diff --git a/docs/api/puppeteer.installationconfiguration.skipdownload.md b/docs/api/puppeteer.installationconfiguration.skipdownload.md new file mode 100644 index 0000000000000..aa0686fba5399 --- /dev/null +++ b/docs/api/puppeteer.installationconfiguration.skipdownload.md @@ -0,0 +1,13 @@ +--- +sidebar_label: InstallationConfiguration.skipDownload +--- + +# InstallationConfiguration.skipDownload property + +**Signature:** + +```typescript +interface InstallationConfiguration { + skipDownload?: boolean; +} +``` diff --git a/docs/api/puppeteer.launch.md b/docs/api/puppeteer.launch.md index 52e36c0304011..6813e636f1859 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 fe79cbeac2d31..8a4483bf7c061 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 8223e88880642..67a197894180e 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 bad57b91dcf1a..15755638f7eb4 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 0cfab0965a510..ac12b8c266ca2 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 aa8bc881a44b2..c1710bc9056a9 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.puppeteerconfiguration.md b/docs/api/puppeteer.puppeteerconfiguration.md new file mode 100644 index 0000000000000..be84e024566f3 --- /dev/null +++ b/docs/api/puppeteer.puppeteerconfiguration.md @@ -0,0 +1,14 @@ +--- +sidebar_label: PuppeteerConfiguration +--- + +# PuppeteerConfiguration type + +**Signature:** + +```typescript +export declare type PuppeteerConfiguration = CoreConfiguration & + InstallationConfiguration; +``` + +**References:** [InstallationConfiguration](./puppeteer.installationconfiguration.md) diff --git a/docs/api/puppeteer.puppeteernode.createbrowserfetcher.md b/docs/api/puppeteer.puppeteernode.createbrowserfetcher.md index 5614f3dfec4a8..3f9abada4e5b5 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 0000000000000..cd8297e25766d --- /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 0534045055bdd..e27c61d1d11f2 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 0000000000000..f66b0430372d3 --- /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 2c72d334b7e3a..8ceb33cee688e 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 f264d67c13135..b75b5f7105a28 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 ec57a72f57991..e5205645bf7ea 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 d450345bb8bd7..c0cbbc2d1c560 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/Configuration.ts similarity index 65% rename from packages/puppeteer-core/src/constants.ts rename to packages/puppeteer-core/src/Configuration.ts index 7df7742001beb..4ad4cb57de857 100644 --- a/packages/puppeteer-core/src/constants.ts +++ b/packages/puppeteer-core/src/Configuration.ts @@ -14,11 +14,20 @@ * limitations under the License. */ -import {homedir} from 'os'; -import {join} from 'path'; +import {Product} from './common/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; + product?: Product; + temporaryDirectory?: string; + experiments?: { + macArmChromiumEnabled?: boolean; + }; +} diff --git a/packages/puppeteer-core/src/common/Puppeteer.ts b/packages/puppeteer-core/src/common/Puppeteer.ts index dc0e8ee506129..306e1eb9a87cd 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 b3571a2122e2c..1054ab6f6fe5a 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 @@ -377,22 +381,18 @@ export class BrowserFetcher { * @returns A promise with 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 89722d1ebea13..4bb7a1bf712b7 100644 --- a/packages/puppeteer-core/src/node/ChromeLauncher.ts +++ b/packages/puppeteer-core/src/node/ChromeLauncher.ts @@ -1,7 +1,6 @@ -import fs from 'fs'; +import fs, {accessSync} from 'fs'; 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 +8,21 @@ import { ChromeReleaseChannel, PuppeteerNodeLaunchOptions, } from './LaunchOptions.js'; -import { - executablePathForChannel, - ProductLauncher, - resolveExecutablePath, -} from './ProductLauncher.js'; -import {tmpdir} from './util.js'; +import os from 'os'; +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 +81,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 fs.promises.mkdtemp(this.getProfilePath())}` ); userDataDirIndex = chromeArguments.length - 1; } @@ -103,21 +89,15 @@ export class ChromeLauncher implements ProductLauncher { const userDataDir = chromeArguments[userDataDirIndex]!.split('=', 2)[1]; 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. + let chromeExecutable: string; + if (this.puppeteer._isPuppeteerCore || executablePath) { assert( - !chromeExecutable, - '`executablePath` must not be specified when `channel` is given.' + executablePath, + `An \`executablePath\` 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; + } else { + chromeExecutable = this.executablePath(channel); } const usePipe = chromeArguments.includes('--remote-debugging-pipe'); @@ -143,7 +123,7 @@ export class ChromeLauncher implements ProductLauncher { usePipe, timeout, slowMo, - preferredRevision: this._preferredRevision, + preferredRevision: this.puppeteer.browserRevision, }); browser = await CDPBrowser._create( this.product, @@ -177,7 +157,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 +221,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 8dbea6330438f..c40b77c4d5080 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 1b2dcbd097b64..3f1073e887af7 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 { - launch(object: PuppeteerNodeLaunchOptions): Promise; - executablePath: (path?: any) => string; - defaultArgs(object: BrowserLaunchArgumentOptions): string[]; - product: Product; -} +export class ProductLauncher { + #product: Product; -/** - * @internal - */ -export function executablePathForChannel( - channel: ChromeReleaseChannel -): string { - const platform = os.platform(); + /** + * @internal + */ + puppeteer: PuppeteerNode; - 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; + /** + * @internal + */ + constructor(puppeteer: PuppeteerNode, product: Product) { + this.puppeteer = puppeteer; + this.#product = product; } - if (!chromePath) { - throw new Error( - `Unable to detect browser executable path for '${channel}' on ${platform}.` - ); + get product(): Product { + return this.#product; } - // 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}'.` - ); + launch(object: PuppeteerNodeLaunchOptions): Promise; + launch(): Promise { + throw new Error('Not implemented'); } - return chromePath; -} + executablePath(channel?: ChromeReleaseChannel): string; + executablePath(): string { + throw new Error('Not implemented'); + } -/** - * @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']; + defaultArgs(object: BrowserLaunchArgumentOptions): string[]; + defaultArgs(): string[] { + throw new Error('Not implemented'); + } + + /** + * @internal + */ + protected getProfilePath(): string { + return join( + this.puppeteer.configuration.temporaryDirectory ?? tmpdir(), + `puppeteer_dev_${this.product}_profile-` + ); + } + + /** + * @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}; - } - 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']; + return ubuntuChromiumPath; } - 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}; -} + const browserFetcher = new BrowserFetcher({ + product: this.product, + path: this.puppeteer.defaultDownloadPath!, + }); -/** - * @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}`); + 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.` + ); + } + } + return revisionInfo.executablePath; } } diff --git a/packages/puppeteer-core/src/node/PuppeteerNode.ts b/packages/puppeteer-core/src/node/PuppeteerNode.ts index f52ee071cbd7d..81fa8be0cb3ff 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 '../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.product) { + case 'firefox': + this.defaultBrowserRevision = PUPPETEER_REVISIONS.firefox; + break; + default: + this.configuration.product = '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,119 @@ 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; + this.#lastLaunchedProduct = options.product; } - return this._launcher.launch(options); + 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. + * @internal + */ + get #launcher(): ProductLauncher { + if ( + this.#_launcher && + this.#_launcher.product === this.lastLaunchedProduct + ) { + 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; + } + + /** + * @returns The executable path. */ - executablePath(channel?: string): string { - return this._launcher.executablePath(channel); + executablePath(channel?: ChromeReleaseChannel): string { + return this.#launcher.executablePath(channel); } /** * @internal */ - get _launcher(): ProductLauncher { - if ( - !this.#launcher || - this.#launcher.product !== this._productName || - this._changedProduct - ) { - 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; + get browserRevision(): string { + return this.configuration.browserRevision ?? this.defaultBrowserRevision!; } /** - * The name of the browser that is under automation (`"chrome"` or - * `"firefox"`) + * @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.product ?? '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 +273,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 9a578972c1351..0000000000000 --- 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 fc8ad33f158a2..5448dbbf3a947 100644 --- a/packages/puppeteer-core/src/types.ts +++ b/packages/puppeteer-core/src/types.ts @@ -55,7 +55,7 @@ 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 './Configuration.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 cfd1a502eb05a..40152864a71bd 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(); +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'); } -download(); +// Need to ensure TS is compiled before loading the installer +const {downloadBrowser} = require('puppeteer/internal/node/install.js'); + +downloadBrowser(); diff --git a/packages/puppeteer/package.json b/packages/puppeteer/package.json index b8b7c2a89ce25..25f9b86dc732d 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/PuppeteerConfiguration.ts b/packages/puppeteer/src/PuppeteerConfiguration.ts new file mode 100644 index 0000000000000..b883d3c15a1af --- /dev/null +++ b/packages/puppeteer/src/PuppeteerConfiguration.ts @@ -0,0 +1,98 @@ +import {cosmiconfigSync} from 'cosmiconfig'; +import {homedir} from 'os'; +import {join} from 'path'; +import {Configuration as CoreConfiguration, Product} from 'puppeteer-core'; + +/** + * @public + */ +export type PuppeteerConfiguration = CoreConfiguration & + InstallationConfiguration; + +/** + * @public + */ +export interface InstallationConfiguration { + skipDownload?: boolean; + logLevel?: 'silent' | 'error' | 'warn'; + httpsProxy?: string; +} + +/** + * @internal + */ +function isSupportedProduct(product: unknown): product is Product { + switch (product) { + case 'chrome': + case 'firefox': + return true; + default: + return false; + } +} + +/** + * @internal + */ +export const getConfiguration = (): PuppeteerConfiguration => { + const result = cosmiconfigSync('puppeteer').search(); + const configuration: PuppeteerConfiguration = 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.cacheDirectory ??= + process.env['PUPPETEER_CACHE_DIR'] ?? + process.env['npm_config_puppeteer_cache_dir'] ?? + process.env['npm_package_config_puppeteer_cache_dir'] ?? + 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.downloadPath ??= + process.env['PUPPETEER_DOWNLOAD_PATH'] ?? + process.env['npm_config_puppeteer_download_path'] ?? + process.env['npm_package_config_puppeteer_download_path']; + configuration.executablePath ??= + process.env['PUPPETEER_EXECUTABLE_PATH'] ?? + process.env['npm_config_puppeteer_executable_path'] ?? + process.env['npm_package_config_puppeteer_executable_path']; + configuration.temporaryDirectory ??= + process.env['PUPPETEER_TMP_DIR'] ?? + process.env['npm_config_puppeteer_tmp_dir'] ?? + process.env['npm_package_config_puppeteer_tmp_dir']; + + 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.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.product ??= (process.env['PUPPETEER_PRODUCT'] ?? + process.env['npm_config_puppeteer_product'] ?? + process.env['npm_package_config_puppeteer_product'] ?? + 'chrome') as Product; + configuration.logLevel ??= (process.env['PUPPETEER_LOGLEVEL'] ?? + process.env['npm_config_LOGLEVEL'] ?? + process.env['npm_package_config_LOGLEVEL']) as 'silent' | 'error' | 'warn'; + + // Validate configuration. + if (!isSupportedProduct(configuration.product)) { + throw new Error(`Unsupported product ${configuration.product}`); + } + + return configuration; +}; diff --git a/packages/puppeteer/src/node/install.ts b/packages/puppeteer/src/node/install.ts index 476056d9478ca..28175d27603f3 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 '../PuppeteerConfiguration.js'; /** * @internal @@ -35,58 +35,40 @@ const supportedProducts = { /** * @internal */ -function getProduct(input: string): 'chrome' | 'firefox' { - if (input !== 'chrome' && input !== 'firefox') { - throw new Error(`Unsupported product ${input}`); +export async function downloadBrowser(): Promise { + const configuration = getConfiguration(); + if (configuration.skipDownload) { + logPolitely('**INFO** Skipping browser download as instructed.'); } - 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 product = configuration.product!; 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().catch(error => { + console.error(error); + process.exit(1); + }); + break; } } + await fetchBinary(revision); + function fetchBinary(revision: string) { const revisionInfo = browserFetcher.revisionInfo(revision); @@ -222,7 +204,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 b31a54ec3c92d..e333930480ee7 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'; - -const productName = (process.env['PUPPETEER_PRODUCT'] || - process.env['npm_config_puppeteer_product'] || - process.env['npm_package_config_puppeteer_product']) as Product; +import {getConfiguration} from './PuppeteerConfiguration.js'; -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 41e0ffac4767f..286556ed83a8b 100644 --- a/packages/puppeteer/src/types.ts +++ b/packages/puppeteer/src/types.ts @@ -55,7 +55,7 @@ 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/Configuration.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 './puppeteer.js'; +export * from './PuppeteerConfiguration.js'; diff --git a/test/src/launcher.spec.ts b/test/src/launcher.spec.ts index 2da6679ed2923..477d0236e053f 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 9980b51cc7029..56da13b6d3a2a 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(