diff --git a/docs/src/api/class-browsertype.md b/docs/src/api/class-browsertype.md index 14f95594856ce..a759f109591f8 100644 --- a/docs/src/api/class-browsertype.md +++ b/docs/src/api/class-browsertype.md @@ -236,38 +236,6 @@ existing browser state. New contexts created via are not affected. Defaults to `false`. -## async method: BrowserType.connectToWorker -* since: v1.60 -* langs: js -- returns: <[Worker]> - -This method attaches Playwright to an existing JavaScript engine exposing Chrome DevTools Protocol, for example to a Node.js process or an Electron application. - -:::note -This is only supported on `chromium`. -::: - -**Usage** - -```js -const worker = await playwright.chromium.connectToWorker('http://localhost:9229'); -const global = await worker.evaluate(() => globalThis); -``` - -### param: BrowserType.connectToWorker.endpoint -* since: v1.60 -- `endpoint` <[string]> - -A CDP websocket endpoint or http url to connect to. For example `http://localhost:9229/` or `ws://127.0.0.1:9229/something`. - -### option: BrowserType.connectToWorker.timeout -* since: v1.60 -- `timeout` <[float]> - -Maximum time in milliseconds to wait for the connection to be established. Defaults to -`30000` (30 seconds). Pass `0` to disable timeout. - - ## method: BrowserType.executablePath * since: v1.8 - returns: <[string]> diff --git a/docs/src/api/class-worker.md b/docs/src/api/class-worker.md index e010dbdad527d..5fc5992affc23 100644 --- a/docs/src/api/class-worker.md +++ b/docs/src/api/class-worker.md @@ -64,19 +64,6 @@ Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs Emitted when JavaScript within the worker calls one of console API methods, e.g. `console.log` or `console.dir`. -## async method: Worker.disconnect -* since: v1.60 -* langs: js - -Disconnects from a worker that was connected through [`method: BrowserType.connectToWorker`]. Calling this method on any other worker will throw. - -### option: Worker.disconnect.reason -* since: v1.60 -- `reason` <[string]> - -The reason to be reported to the operations interrupted by the worker disconnect. - - ## async method: Worker.evaluate * since: v1.8 - returns: <[Serializable]> diff --git a/docs/src/electron-api/class-electron.md b/docs/src/electron-api/class-electron.md index bf74771e20917..3c11689c40243 100644 --- a/docs/src/electron-api/class-electron.md +++ b/docs/src/electron-api/class-electron.md @@ -2,33 +2,10 @@ * since: v1.9 * langs: js -Playwright supports Electron automation, shipped as a separate package. +Playwright has **experimental** support for Electron automation, exposed as `_electron`. An example of the Electron automation script would be: -```sh -npm i -D @playwright/electron -``` - -After installation, you can write a test or an automation script. - -```js tab=js-test -import { test, expect } from '@playwright/electron'; - -test.use({ appOptions: { args: ['main.js'] } }); - -test('basic test', async ({ app, page }) => { - // Evaluate in the main Electron process. - const appPath = await app.evaluate(async ({ app }) => app.getAppPath()); - console.log(appPath); - - // Interact with the first window via the `page` fixture. - await expect(page).toHaveTitle(/My App/); - await page.click('text=Click me'); - await expect(page.getByRole('heading')).toHaveText('Hello'); -}); -``` - -```js tab=js-library -import { electron } from '@playwright/electron'; +```js +import { _electron as electron } from 'playwright'; (async () => { // Launch Electron app. @@ -68,6 +45,109 @@ If you are not able to launch Electron and it will end up in timeouts during lau * Ensure that `nodeCliInspect` ([FuseV1Options.EnableNodeCliInspectArguments](https://www.electronjs.org/docs/latest/tutorial/fuses#nodecliinspect)) fuse is **not** set to `false`. +**Migrating from v1.59** + +A number of launch options have been removed after v1.59. See below for alternatives. + +* `recordHar` - use [`method: Tracing.startHar`]. + ```js + const electronApp = await electron.launch({ args: ['main.js'] }); + await electronApp.context().tracing.startHar('network.har'); + // ... drive the app ... + await electronApp.context().tracing.stopHar(); + await electronApp.close(); + ``` + +* `recordVideo` - use [`method: Screencast.start`] on each window. + ```js + const electronApp = await electron.launch({ args: ['main.js'] }); + const window = await electronApp.firstWindow(); + await window.screencast.start({ path: 'video.webm' }); + // ... drive the window ... + await window.screencast.stop(); + await electronApp.close(); + ``` + +* `colorScheme` - use [`method: Page.emulateMedia`] on each window. + ```js + const window = await electronApp.firstWindow(); + await window.emulateMedia({ colorScheme: 'dark' }); + ``` + +* `extraHTTPHeaders` - use [`method: BrowserContext.setExtraHTTPHeaders`]. + ```js + await electronApp.context().setExtraHTTPHeaders({ 'X-My-Header': 'value' }); + ``` + +* `geolocation` - use [`method: BrowserContext.setGeolocation`]. + ```js + await electronApp.context().setGeolocation({ latitude: 48.858455, longitude: 2.294474 }); + ``` + +* `httpCredentials` - use [`method: BrowserContext.setHTTPCredentials`]. + ```js + await electronApp.context().setHTTPCredentials({ username: 'user', password: 'pass' }); + ``` + +* `offline` - use [`method: BrowserContext.setOffline`]. + ```js + await electronApp.context().setOffline(true); + ``` + +* `bypassCSP` - disable CSP at the `BrowserWindow` level via Electron's [web preferences](https://www.electronjs.org/docs/latest/api/structures/web-preferences). Note that `webSecurity: false` also disables CORS and the Same-Origin Policy. + + ```js + const win = new BrowserWindow({ + webPreferences: { + webSecurity: false, + }, + }); + ``` + +* `ignoreHTTPSErrors` + + There are several ways to relax HTTPS checks in Electron. Pick the one that matches the scope you need. + + Per-window, allow mixed content through [web preferences](https://www.electronjs.org/docs/latest/api/structures/web-preferences): + + ```js + const win = new BrowserWindow({ + webPreferences: { + allowRunningInsecureContent: true, + }, + }); + ``` + + Process-wide, ignore certificate errors via Chromium command-line switches + (must run before the `ready` event): + + ```js + const { app } = require('electron'); + app.commandLine.appendSwitch('ignore-certificate-errors'); + // Optional: also ignore localhost certificate errors when testing on an IP. + app.commandLine.appendSwitch('allow-insecure-localhost', 'true'); + ``` + + Per-request, accept the certificate manually via the + [`certificate-error`](https://www.electronjs.org/docs/latest/api/app#event-certificate-error) + event: + + ```js + app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { + event.preventDefault(); + callback(true); + }); + ``` + +* `timezoneId` - set an environment variable at the very top of the main file, before any other logic or Chromium windows are initialized. + ```js + // main.js + process.env.TZ = 'Europe/London'; + + const { app } = require('electron'); + // ... rest of your app logic + ``` + ## async method: Electron.launch * since: v1.9 - returns: <[ElectronApplication]> diff --git a/docs/src/electron-api/class-electronapplication.md b/docs/src/electron-api/class-electronapplication.md index 87e7975b352d7..3006801d262f9 100644 --- a/docs/src/electron-api/class-electronapplication.md +++ b/docs/src/electron-api/class-electronapplication.md @@ -7,7 +7,7 @@ obtain the application instance. This instance you can control main electron pro as well as work with Electron windows: ```js -const { _electron: electron } = require('playwright'); +import { _electron as electron } from 'playwright'; (async () => { // Launch Electron app. diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index d36153bd9f644..6c8ba8cba0f87 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -10827,19 +10827,6 @@ export interface Worker { */ prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; - /** - * Disconnects from a worker that was connected through - * [browserType.connectToWorker(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect-to-worker). - * Calling this method on any other worker will throw. - * @param options - */ - disconnect(options?: { - /** - * The reason to be reported to the operations interrupted by the worker disconnect. - */ - reason?: string; - }): Promise; - url(): string; /** @@ -15416,31 +15403,6 @@ export interface BrowserType { * @param options */ connect(options: ConnectOptions & { wsEndpoint?: string }): Promise; - /** - * This method attaches Playwright to an existing JavaScript engine exposing Chrome DevTools Protocol, for example to - * a Node.js process or an Electron application. - * - * **NOTE** This is only supported on `chromium`. - * - * **Usage** - * - * ```js - * const worker = await playwright.chromium.connectToWorker('http://localhost:9229'); - * const global = await worker.evaluate(() => globalThis); - * ``` - * - * @param endpoint A CDP websocket endpoint or http url to connect to. For example `http://localhost:9229/` or - * `ws://127.0.0.1:9229/something`. - * @param options - */ - connectToWorker(endpoint: string, options?: { - /** - * Maximum time in milliseconds to wait for the connection to be established. Defaults to `30000` (30 seconds). Pass - * `0` to disable timeout. - */ - timeout?: number; - }): Promise; - /** * A path where Playwright expects to find a bundled browser executable. */ @@ -16867,7 +16829,7 @@ type ElectronType = typeof import('electron'); * application instance. This instance you can control main electron process as well as work with Electron windows: * * ```js - * const { _electron: electron } = require('playwright'); + * import { _electron as electron } from 'playwright'; * * (async () => { * // Launch Electron app. @@ -22573,9 +22535,38 @@ export interface WebSocket { } /** - * Playwright supports Electron automation, shipped as a separate package. + * Playwright has **experimental** support for Electron automation, exposed as `_electron`. An example of the Electron + * automation script would be: + * + * ```js + * import { _electron as electron } from 'playwright'; + * + * (async () => { + * // Launch Electron app. + * const electronApp = await electron.launch({ args: ['main.js'] }); + * + * // Evaluation expression in the Electron context. + * const appPath = await electronApp.evaluate(async ({ app }) => { + * // This runs in the main Electron process, parameter here is always + * // the result of the require('electron') in the main app script. + * return app.getAppPath(); + * }); + * console.log(appPath); * - * After installation, you can write a test or an automation script. + * // Get the first window that the app opens, wait if necessary. + * const window = await electronApp.firstWindow(); + * // Print the title. + * console.log(await window.title()); + * // Capture a screenshot. + * await window.screenshot({ path: 'intro.png' }); + * // Direct Electron console to Node terminal. + * window.on('console', console.log); + * // Click button. + * await window.click('text=Click me'); + * // Exit app. + * await electronApp.close(); + * })(); + * ``` * * **Supported Electron versions are:** * - v12.2.0+ @@ -22588,6 +22579,127 @@ export interface WebSocket { * - Ensure that `nodeCliInspect` * ([FuseV1Options.EnableNodeCliInspectArguments](https://www.electronjs.org/docs/latest/tutorial/fuses#nodecliinspect)) * fuse is **not** set to `false`. + * + * **Migrating from v1.59** + * + * A number of launch options have been removed after v1.59. See below for alternatives. + * - `recordHar` - use + * [tracing.startHar(path[, options])](https://playwright.dev/docs/api/class-tracing#tracing-start-har). + * + * ```js + * const electronApp = await electron.launch({ args: ['main.js'] }); + * await electronApp.context().tracing.startHar('network.har'); + * // ... drive the app ... + * await electronApp.context().tracing.stopHar(); + * await electronApp.close(); + * ``` + * + * - `recordVideo` - use + * [screencast.start([options])](https://playwright.dev/docs/api/class-screencast#screencast-start) on each + * window. + * + * ```js + * const electronApp = await electron.launch({ args: ['main.js'] }); + * const window = await electronApp.firstWindow(); + * await window.screencast.start({ path: 'video.webm' }); + * // ... drive the window ... + * await window.screencast.stop(); + * await electronApp.close(); + * ``` + * + * - `colorScheme` - use + * [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#page-emulate-media) on each window. + * + * ```js + * const window = await electronApp.firstWindow(); + * await window.emulateMedia({ colorScheme: 'dark' }); + * ``` + * + * - `extraHTTPHeaders` - use + * [browserContext.setExtraHTTPHeaders(headers)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-extra-http-headers). + * + * ```js + * await electronApp.context().setExtraHTTPHeaders({ 'X-My-Header': 'value' }); + * ``` + * + * - `geolocation` - use + * [browserContext.setGeolocation(geolocation)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-geolocation). + * + * ```js + * await electronApp.context().setGeolocation({ latitude: 48.858455, longitude: 2.294474 }); + * ``` + * + * - `httpCredentials` - use + * [browserContext.setHTTPCredentials(httpCredentials)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-http-credentials). + * + * ```js + * await electronApp.context().setHTTPCredentials({ username: 'user', password: 'pass' }); + * ``` + * + * - `offline` - use + * [browserContext.setOffline(offline)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-offline). + * + * ```js + * await electronApp.context().setOffline(true); + * ``` + * + * - `bypassCSP` - disable CSP at the `BrowserWindow` level via Electron's + * [web preferences](https://www.electronjs.org/docs/latest/api/structures/web-preferences). Note that + * `webSecurity: false` also disables CORS and the Same-Origin Policy. + * + * ```js + * const win = new BrowserWindow({ + * webPreferences: { + * webSecurity: false, + * }, + * }); + * ``` + * + * - `ignoreHTTPSErrors` + * + * There are several ways to relax HTTPS checks in Electron. Pick the one that matches the scope you need. + * + * Per-window, allow mixed content through + * [web preferences](https://www.electronjs.org/docs/latest/api/structures/web-preferences): + * + * ```js + * const win = new BrowserWindow({ + * webPreferences: { + * allowRunningInsecureContent: true, + * }, + * }); + * ``` + * + * Process-wide, ignore certificate errors via Chromium command-line switches (must run before the `ready` event): + * + * ```js + * const { app } = require('electron'); + * app.commandLine.appendSwitch('ignore-certificate-errors'); + * // Optional: also ignore localhost certificate errors when testing on an IP. + * app.commandLine.appendSwitch('allow-insecure-localhost', 'true'); + * ``` + * + * Per-request, accept the certificate manually via the + * [`certificate-error`](https://www.electronjs.org/docs/latest/api/app#event-certificate-error) event: + * + * ```js + * app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { + * event.preventDefault(); + * callback(true); + * }); + * ``` + * + * - `timezoneId` - set an environment variable at the very top of the main file, before any other logic or Chromium + * windows are initialized. + * + * ```js + * // main.js + * process.env.TZ = 'Europe/London'; + * + * const { app } = require('electron'); + * // ... rest of your app logic + * ``` + * */ export interface Electron { /** diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index 026fb9dc77df9..2b069c05d36be 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -165,7 +165,7 @@ export class BrowserType extends ChannelOwner imple return browser; } - async connectToWorker(endpoint: string, options: { timeout?: number } = {}): Promise { + async _connectToWorker(endpoint: string, options: { timeout?: number } = {}): Promise { if (this.name() !== 'chromium') throw new Error('Connecting to workers is only supported in Chromium.'); const result = await this._channel.connectToWorker({ diff --git a/packages/playwright-core/src/client/worker.ts b/packages/playwright-core/src/client/worker.ts index 7bed47f6b6f9d..ee98e35779e2e 100644 --- a/packages/playwright-core/src/client/worker.ts +++ b/packages/playwright-core/src/client/worker.ts @@ -51,7 +51,7 @@ export class Worker extends ChannelOwner implements api. [Events.Worker.Console, 'console'], ])); this._channel.on('console', event => { - // Note: we only receive console events here for workers from "chromium.connectToWorker". + // Note: we only receive console events here for workers from "chromium._connectToWorker". this.emit(Events.Worker.Console, new ConsoleMessage(this._platform, event, null, this)); }); this._channel.on('close', () => { @@ -101,7 +101,7 @@ export class Worker extends ChannelOwner implements api. }); } - async disconnect(options: { reason?: string } = {}): Promise { + async _disconnect(options: { reason?: string } = {}): Promise { this._closeReason = options.reason; try { await this._channel.disconnect(options); diff --git a/packages/playwright-core/src/electron/electron.ts b/packages/playwright-core/src/electron/electron.ts index 94800edc612f6..cc493b7ed1222 100644 --- a/packages/playwright-core/src/electron/electron.ts +++ b/packages/playwright-core/src/electron/electron.ts @@ -28,9 +28,10 @@ import { monotonicTime } from '@isomorphic/time'; import { libPath } from '../package'; import type { BrowserWindow } from 'electron'; -import type { Browser, BrowserContext, JSHandle, Page, Worker } from '../../types/types'; +import type { Browser, BrowserContext, JSHandle, Page } from '../../types/types'; import type * as api from '../../types/types'; import type { Playwright } from '../client/playwright'; +import type { Worker } from '../client/worker'; import type childProcess from 'child_process'; const debugLogger = debug('pw:electron'); @@ -171,10 +172,10 @@ export class Electron implements api.Electron { try { const chromium = this._playwright.chromium; const nodeMatch = await nodeMatchPromise; - const worker = await chromium.connectToWorker(nodeMatch[1], { timeout: progress.timeUntilDeadline() }); + const worker = await chromium._connectToWorker(nodeMatch[1], { timeout: progress.timeUntilDeadline() }); // Release the Electron process immediately if the user is debugging it. - debuggerDisconnectPromise.then(() => worker.disconnect()).catch(() => {}); + debuggerDisconnectPromise.then(() => worker._disconnect()).catch(() => {}); const chromeMatch = await Promise.race([chromeMatchPromise, waitForXserverError]); const browser = await chromium.connectOverCDP(chromeMatch[1], { timeout: progress.timeUntilDeadline(), isLocal: true }); @@ -254,7 +255,7 @@ export class ElectronApplication extends EventEmitter implements api.ElectronApp await this._browser.close(); const appHandle = await this._appHandlePromise; await appHandle.evaluate(({ app }) => app.quit()).catch(() => {}); - await this._worker.disconnect(); + await this._worker._disconnect(); } await this._closedPromise; } diff --git a/packages/playwright-core/src/electron/loader.ts b/packages/playwright-core/src/electron/loader.ts index 0e0c25dbdc364..2cc15ec3b51a9 100644 --- a/packages/playwright-core/src/electron/loader.ts +++ b/packages/playwright-core/src/electron/loader.ts @@ -61,7 +61,7 @@ const chromiumSwitches = [ '--disable-sync', ]; -// The new `chromium.connectToWorker`-based client reads these globals via +// The new `chromium._connectToWorker`-based client reads these globals via // the Node debugger to bootstrap the Electron app. (globalThis as any).__playwright_electron = electronModule; diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index d36153bd9f644..6c8ba8cba0f87 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -10827,19 +10827,6 @@ export interface Worker { */ prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; - /** - * Disconnects from a worker that was connected through - * [browserType.connectToWorker(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect-to-worker). - * Calling this method on any other worker will throw. - * @param options - */ - disconnect(options?: { - /** - * The reason to be reported to the operations interrupted by the worker disconnect. - */ - reason?: string; - }): Promise; - url(): string; /** @@ -15416,31 +15403,6 @@ export interface BrowserType { * @param options */ connect(options: ConnectOptions & { wsEndpoint?: string }): Promise; - /** - * This method attaches Playwright to an existing JavaScript engine exposing Chrome DevTools Protocol, for example to - * a Node.js process or an Electron application. - * - * **NOTE** This is only supported on `chromium`. - * - * **Usage** - * - * ```js - * const worker = await playwright.chromium.connectToWorker('http://localhost:9229'); - * const global = await worker.evaluate(() => globalThis); - * ``` - * - * @param endpoint A CDP websocket endpoint or http url to connect to. For example `http://localhost:9229/` or - * `ws://127.0.0.1:9229/something`. - * @param options - */ - connectToWorker(endpoint: string, options?: { - /** - * Maximum time in milliseconds to wait for the connection to be established. Defaults to `30000` (30 seconds). Pass - * `0` to disable timeout. - */ - timeout?: number; - }): Promise; - /** * A path where Playwright expects to find a bundled browser executable. */ @@ -16867,7 +16829,7 @@ type ElectronType = typeof import('electron'); * application instance. This instance you can control main electron process as well as work with Electron windows: * * ```js - * const { _electron: electron } = require('playwright'); + * import { _electron as electron } from 'playwright'; * * (async () => { * // Launch Electron app. @@ -22573,9 +22535,38 @@ export interface WebSocket { } /** - * Playwright supports Electron automation, shipped as a separate package. + * Playwright has **experimental** support for Electron automation, exposed as `_electron`. An example of the Electron + * automation script would be: + * + * ```js + * import { _electron as electron } from 'playwright'; + * + * (async () => { + * // Launch Electron app. + * const electronApp = await electron.launch({ args: ['main.js'] }); + * + * // Evaluation expression in the Electron context. + * const appPath = await electronApp.evaluate(async ({ app }) => { + * // This runs in the main Electron process, parameter here is always + * // the result of the require('electron') in the main app script. + * return app.getAppPath(); + * }); + * console.log(appPath); * - * After installation, you can write a test or an automation script. + * // Get the first window that the app opens, wait if necessary. + * const window = await electronApp.firstWindow(); + * // Print the title. + * console.log(await window.title()); + * // Capture a screenshot. + * await window.screenshot({ path: 'intro.png' }); + * // Direct Electron console to Node terminal. + * window.on('console', console.log); + * // Click button. + * await window.click('text=Click me'); + * // Exit app. + * await electronApp.close(); + * })(); + * ``` * * **Supported Electron versions are:** * - v12.2.0+ @@ -22588,6 +22579,127 @@ export interface WebSocket { * - Ensure that `nodeCliInspect` * ([FuseV1Options.EnableNodeCliInspectArguments](https://www.electronjs.org/docs/latest/tutorial/fuses#nodecliinspect)) * fuse is **not** set to `false`. + * + * **Migrating from v1.59** + * + * A number of launch options have been removed after v1.59. See below for alternatives. + * - `recordHar` - use + * [tracing.startHar(path[, options])](https://playwright.dev/docs/api/class-tracing#tracing-start-har). + * + * ```js + * const electronApp = await electron.launch({ args: ['main.js'] }); + * await electronApp.context().tracing.startHar('network.har'); + * // ... drive the app ... + * await electronApp.context().tracing.stopHar(); + * await electronApp.close(); + * ``` + * + * - `recordVideo` - use + * [screencast.start([options])](https://playwright.dev/docs/api/class-screencast#screencast-start) on each + * window. + * + * ```js + * const electronApp = await electron.launch({ args: ['main.js'] }); + * const window = await electronApp.firstWindow(); + * await window.screencast.start({ path: 'video.webm' }); + * // ... drive the window ... + * await window.screencast.stop(); + * await electronApp.close(); + * ``` + * + * - `colorScheme` - use + * [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#page-emulate-media) on each window. + * + * ```js + * const window = await electronApp.firstWindow(); + * await window.emulateMedia({ colorScheme: 'dark' }); + * ``` + * + * - `extraHTTPHeaders` - use + * [browserContext.setExtraHTTPHeaders(headers)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-extra-http-headers). + * + * ```js + * await electronApp.context().setExtraHTTPHeaders({ 'X-My-Header': 'value' }); + * ``` + * + * - `geolocation` - use + * [browserContext.setGeolocation(geolocation)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-geolocation). + * + * ```js + * await electronApp.context().setGeolocation({ latitude: 48.858455, longitude: 2.294474 }); + * ``` + * + * - `httpCredentials` - use + * [browserContext.setHTTPCredentials(httpCredentials)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-http-credentials). + * + * ```js + * await electronApp.context().setHTTPCredentials({ username: 'user', password: 'pass' }); + * ``` + * + * - `offline` - use + * [browserContext.setOffline(offline)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-offline). + * + * ```js + * await electronApp.context().setOffline(true); + * ``` + * + * - `bypassCSP` - disable CSP at the `BrowserWindow` level via Electron's + * [web preferences](https://www.electronjs.org/docs/latest/api/structures/web-preferences). Note that + * `webSecurity: false` also disables CORS and the Same-Origin Policy. + * + * ```js + * const win = new BrowserWindow({ + * webPreferences: { + * webSecurity: false, + * }, + * }); + * ``` + * + * - `ignoreHTTPSErrors` + * + * There are several ways to relax HTTPS checks in Electron. Pick the one that matches the scope you need. + * + * Per-window, allow mixed content through + * [web preferences](https://www.electronjs.org/docs/latest/api/structures/web-preferences): + * + * ```js + * const win = new BrowserWindow({ + * webPreferences: { + * allowRunningInsecureContent: true, + * }, + * }); + * ``` + * + * Process-wide, ignore certificate errors via Chromium command-line switches (must run before the `ready` event): + * + * ```js + * const { app } = require('electron'); + * app.commandLine.appendSwitch('ignore-certificate-errors'); + * // Optional: also ignore localhost certificate errors when testing on an IP. + * app.commandLine.appendSwitch('allow-insecure-localhost', 'true'); + * ``` + * + * Per-request, accept the certificate manually via the + * [`certificate-error`](https://www.electronjs.org/docs/latest/api/app#event-certificate-error) event: + * + * ```js + * app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { + * event.preventDefault(); + * callback(true); + * }); + * ``` + * + * - `timezoneId` - set an environment variable at the very top of the main file, before any other logic or Chromium + * windows are initialized. + * + * ```js + * // main.js + * process.env.TZ = 'Europe/London'; + * + * const { app } = require('electron'); + * // ... rest of your app logic + * ``` + * */ export interface Electron { /** diff --git a/packages/playwright-electron/class-electronfixtures.md b/packages/playwright-electron/class-electronfixtures.md index 8e77102543f6e..c5a1eea6bbce5 100644 --- a/packages/playwright-electron/class-electronfixtures.md +++ b/packages/playwright-electron/class-electronfixtures.md @@ -4,11 +4,33 @@ The `@playwright/electron` package exposes a `test` object with a set of fixtures tailored for Electron automation. Fixtures are used to establish the environment for each test, giving the test everything it needs and nothing else. -```js +Below is an example config and a test file. + +```js title="playwright.config.ts" +import { defineConfig } from '@playwright/electron'; + +export default defineConfig({ + use: { + appOptions: { + args: ['main.js'], + env: { NODE_ENV: 'test' }, + }, + }, +}); +``` + +```js title="example.spec.ts" import { test, expect } from '@playwright/electron'; -test('basic test', async ({ page }) => { - // ... +test('basic test', async ({ app, page }) => { + // Evaluate in the main Electron process. + const appPath = await app.evaluate(async ({ app }) => app.getAppPath()); + console.log(appPath); + + // Interact with the first window via the `page` fixture. + await expect(page).toHaveTitle(/My App/); + await page.click('text=Click me'); + await expect(page.getByRole('heading')).toHaveText('Hello'); }); ``` diff --git a/tests/library/chromium/connect-to-worker.spec.ts b/tests/library/chromium/connect-to-worker.spec.ts index 750f999eabab6..43067608e7884 100644 --- a/tests/library/chromium/connect-to-worker.spec.ts +++ b/tests/library/chromium/connect-to-worker.spec.ts @@ -22,7 +22,7 @@ test('should connect, evaluate, receive console and disconnect', async ({ browse const child = childProcess({ command: [process.execPath, '--inspect-brk=0', '-e', 'console.log("hello from node"); setTimeout(() => {}, 1e9)'] }); await child.waitForOutput('Debugger listening on ws://'); const endpoint = child.output.match(/Debugger listening on (ws:\/\/\S+)/)![1]; - const worker = await browserType.connectToWorker(endpoint); + const worker = await (browserType as any)._connectToWorker(endpoint); // Script runs after connect due to --inspect-brk, so listen before evaluating. const messagePromise = worker.waitForEvent('console'); const result = await worker.evaluate(() => 1 + 1); @@ -32,7 +32,7 @@ test('should connect, evaluate, receive console and disconnect', async ({ browse expect(message.type()).toBe('log'); // Disconnect and receive close event. const closePromise = worker.waitForEvent('close'); - await worker.disconnect(); + await worker._disconnect(); await closePromise; }); @@ -40,7 +40,7 @@ test('should receive close when node process exits', async ({ browserType, child const child = childProcess({ command: [process.execPath, '--inspect-brk=0', '-e', 'setTimeout(() => {}, 1e9)'] }); await child.waitForOutput('Debugger listening on ws://'); const endpoint = child.output.match(/Debugger listening on (ws:\/\/\S+)/)![1]; - const worker = await browserType.connectToWorker(endpoint); + const worker = await (browserType as any)._connectToWorker(endpoint); const closePromise = worker.waitForEvent('close'); child.process.kill(); await closePromise;