docs: document electron api (#5229)

This commit is contained in:
Pavel Feldman 2021-02-01 11:43:26 -08:00 committed by GitHub
parent e71ef7949b
commit 1db5ef24a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 664 additions and 336 deletions

View File

@ -1,4 +1,3 @@
# class: Dialog
[Dialog] objects are dispatched by page via the [`event: Page.dialog`] event.

View File

@ -0,0 +1,76 @@
# class: Electron
* langs: js
Playwright has **experimental** support for Electron automation. You can access electron namespace via:
```js
const { _electron } = require('playwright');
```
An example of the Electron automation script would be:
```js
const { _electron: electron } = require('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 (electron) => {
// This runs in the main Electron process, |electron| parameter
// here is always the result of the require('electron') in the main
// app script.
return electron.getAppPath();
});
// 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');
})();
```
Note that since you don't need Playwright to install web browsers when testing Electron, you can omit browser download via setting the following environment variable when installing Playwright:
```sh js
$ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright
```
## async method: Electron.launch
- returns: <[ElectronApplication]>
Launches electron application specified with the [`option: executablePath`].
### option: Electron.launch.executablePath
- `executablePath` <[string]>
Launches given Electron application. If not specified, launches the default Electron
executable installed in this package, located at `node_modules/.bin/electron`.
### option: Electron.launch.args
- `args` <[Array]<[string]>>
Additional arguments to pass to the application when launching. You typically pass the main
script name here.
### option: Electron.launch.cwd
- `cwd` <[string]>
Current working directory to launch application from.
### option: Electron.launch.env
- `env` <[Object]<[string], [string]>>
Specifies environment variables that will be visible to Electron. Defaults to `process.env`.
#### option: Electron.launch.timeout
- `timeout` <[float]>
Maximum time in milliseconds to wait for the application to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.

View File

@ -0,0 +1,129 @@
# class: ElectronApplication
* langs: js
Electron application representation. You can use [`method: Electron.launch`] to
obtain the application instance. This instance you can control main electron process
as well as work with Electron windows:
```js
const { _electron: electron } = require('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 (electron) => {
// This runs in the main Electron process, |electron| parameter
// here is always the result of the require('electron') in the main
// app script.
return electron.getAppPath();
});
// 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');
})();
```
## event: ElectronApplication.close
This event is issued when the application closes.
## event: ElectronApplication.window
- type: <[Page]>
This event is issued for every window that is created **and loaded** in Electron. It contains a [Page] that can
be used for Playwright automation.
## async method: ElectronApplication.close
Closes Electron application.
## method: ElectronApplication.context
- type: <[BrowserContext]>
This method returns browser context that can be used for setting up context-wide routing, etc.
## async method: ElectronApplication.evaluate
- returns: <[Serializable]>
Returns the return value of [`param: expression`].
If the function passed to the [`method: ElectronApplication.evaluate`] returns a [Promise], then
[`method: ElectronApplication.evaluate`] would wait for the promise to resolve and return its value.
If the function passed to the [`method: ElectronApplication.evaluate`] returns a non-[Serializable] value, then
[`method: ElectronApplication.evaluate`] returns `undefined`. Playwright also supports transferring
some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
### param: ElectronApplication.evaluate.expression = %%-evaluate-expression-%%
### param: ElectronApplication.evaluate.arg
- `arg` <[EvaluationArgument]>
Optional argument to pass to [`param: expression`].
## async method: ElectronApplication.evaluateHandle
- returns: <[JSHandle]>
Returns the return value of [`param: expression`] as a [JSHandle].
The only difference between [`method: ElectronApplication.evaluate`] and [`method: ElectronApplication.evaluateHandle`] is that [`method: ElectronApplication.evaluateHandle`] returns [JSHandle].
If the function passed to the [`method: ElectronApplication.evaluateHandle`] returns a [Promise], then
[`method: ElectronApplication.evaluateHandle`] would wait for the promise to resolve and return its value.
### param: ElectronApplication.evaluateHandle.expression = %%-evaluate-expression-%%
### param: ElectronApplication.evaluateHandle.arg
- `arg` <[EvaluationArgument]>
## async method: ElectronApplication.firstWindow
- returns: <[Page]>
Convenience method that waits for the first application window to be opened.
Typically your script will start with:
```js
const electronApp = await electron.launch({
args: ['main.js']
});
const window = await electronApp.firstWindow();
// ...
```
## async method: ElectronApplication.waitForEvent
- returns: <[any]>
Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy value. Will throw an error if the application is closed before the event is fired. Returns the event data value.
```js
const [window] = await Promise.all([
electronApp.waitForEvent('window'),
mainWindow.click('button')
]);
```
### param: ElectronApplication.waitForEvent.event = %%-wait-for-event-event-%%
### param: ElectronApplication.waitForEvent.optionsOrPredicate
* langs: js
- `optionsOrPredicate` <[function]|[Object]>
- `predicate` <[function]> receives the event data and resolves to truthy value when the waiting should resolve.
- `timeout` <[float]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to
disable timeout. The default value can be changed by using the [`method: BrowserContext.setDefaultTimeout`].
Either a predicate that receives an event or an options object. Optional.
## method: ElectronApplication.windows
- returns: <[Array]<[Page]>>
Convenience method that returns all the opened windows.

View File

@ -247,7 +247,7 @@ Optional event-specific initialization properties.
- alias-js: $eval
- returns: <[Serializable]>
Returns the return value of [`param: expression`]
Returns the return value of [`param: expression`].
The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
argument to [`param: expression`]. See [Working with selectors](./selectors.md) for more
@ -291,7 +291,7 @@ Optional argument to pass to [`param: expression`]
- alias-js: $$eval
- returns: <[Serializable]>
Returns the return value of [`param: expression`]
Returns the return value of [`param: expression`].
The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
matched elements as a first argument to [`param: expression`]. See

View File

@ -300,7 +300,7 @@ Optional event-specific initialization properties.
- alias-js: $eval
- returns: <[Serializable]>
Returns the return value of [`param: expression`]
Returns the return value of [`param: expression`].
The method finds an element matching the specified selector within the frame and passes it as a first argument to
[`param: expression`]. See [Working with selectors](./selectors.md) for more details. If no
@ -344,7 +344,7 @@ Optional argument to pass to [`param: expression`]
- alias-js: $$eval
- returns: <[Serializable]>
Returns the return value of [`param: expression`]
Returns the return value of [`param: expression`].
The method finds all elements matching the specified selector within the frame and passes an array of matched elements
as a first argument to [`param: expression`]. See [Working with selectors](./selectors.md) for
@ -379,14 +379,14 @@ Optional argument to pass to [`param: expression`]
## async method: Frame.evaluate
- returns: <[Serializable]>
Returns the return value of [`param: expression`]
Returns the return value of [`param: expression`].
If the function passed to the [`method: Frame.evaluate`] returns a [Promise], then [`method: Frame.evaluate`] would wait for the promise to
resolve and return its value.
If the function passed to the [`method: Frame.evaluate`] returns a non-[Serializable] value, then
[`method: Frame.evaluate`] returns `undefined`. DevTools Protocol also supports transferring some additional values that
are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
[`method: Frame.evaluate`] returns `undefined`. Playwright also supports transferring some
additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
```js
const result = await frame.evaluate(([x, y]) => {
@ -455,10 +455,10 @@ Optional argument to pass to [`param: expression`]
## async method: Frame.evaluateHandle
- returns: <[JSHandle]>
Returns the return value of [`param: expression`] as in-page object (JSHandle).
Returns the return value of [`param: expression`] as a [JSHandle].
The only difference between [`method: Frame.evaluate`] and [`method: Frame.evaluateHandle`] is that
[method: Frame.evaluateHandle`] returns in-page object (JSHandle).
[method: Frame.evaluateHandle`] returns [JSHandle].
If the function, passed to the [`method: Frame.evaluateHandle`], returns a [Promise], then
[`method: Frame.evaluateHandle`] would wait for the promise to resolve and return its value.

View File

@ -37,7 +37,7 @@ The `jsHandle.dispose` method stops referencing the element handle.
## async method: JSHandle.evaluate
- returns: <[Serializable]>
Returns the return value of [`param: expression`]
Returns the return value of [`param: expression`].
This method passes this handle as the first argument to [`param: expression`].
@ -71,12 +71,11 @@ Optional argument to pass to [`param: expression`]
## async method: JSHandle.evaluateHandle
- returns: <[JSHandle]>
Returns the return value of [`param: expression`] as in-page object (JSHandle).
Returns the return value of [`param: expression`] as a [JSHandle].
This method passes this handle as the first argument to [`param: expression`].
The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle` returns
in-page object (JSHandle).
The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle` returns [JSHandle].
If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would wait
for the promise to resolve and return its value.

View File

@ -808,8 +808,8 @@ If the function passed to the [`method: Page.evaluate`] returns a [Promise], the
for the promise to resolve and return its value.
If the function passed to the [`method: Page.evaluate`] returns a non-[Serializable] value, then
[`method: Page.evaluate`] resolves to `undefined`. DevTools Protocol also supports transferring some additional values
that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
[`method: Page.evaluate`] resolves to `undefined`. Playwright also supports transferring some
additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
Passing argument to [`param: expression`]:
@ -882,10 +882,9 @@ Optional argument to pass to [`param: expression`]
## async method: Page.evaluateHandle
- returns: <[JSHandle]>
Returns the value of the [`param: expression`] invocation as in-page object (JSHandle).
Returns the value of the [`param: expression`] invocation as a [JSHandle].
The only difference between [`method: Page.evaluate`] and [`method: Page.evaluateHandle`] is that [`method: Page.evaluateHandle`] returns in-page
object (JSHandle).
The only difference between [`method: Page.evaluate`] and [`method: Page.evaluateHandle`] is that [`method: Page.evaluateHandle`] returns [JSHandle].
If the function passed to the [`method: Page.evaluateHandle`] returns a [Promise], then [`method: Page.evaluateHandle`] would wait for the
promise to resolve and return its value.

View File

@ -35,14 +35,13 @@ Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs
## async method: Worker.evaluate
- returns: <[Serializable]>
Returns the return value of [`param: expression`]
Returns the return value of [`param: expression`].
If the function passed to the `worker.evaluate` returns a [Promise], then `worker.evaluate` would wait for the promise
If the function passed to the [`method: Worker.evaluate`] returns a [Promise], then [`method: Worker.evaluate`] would wait for the promise
to resolve and return its value.
If the function passed to the `worker.evaluate` returns a non-[Serializable] value, then `worker.evaluate` returns
`undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`:
`-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
If the function passed to the [`method: Worker.evaluate`] returns a non-[Serializable] value, then [`method: Worker.evaluate`] returns `undefined`. Playwright also supports transferring some
additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
### param: Worker.evaluate.expression = %%-evaluate-expression-%%
@ -54,12 +53,13 @@ Optional argument to pass to [`param: expression`]
## async method: Worker.evaluateHandle
- returns: <[JSHandle]>
Returns the return value of [`param: expression`] as in-page object (JSHandle).
Returns the return value of [`param: expression`] as a [JSHandle].
The only difference between `worker.evaluate` and `worker.evaluateHandle` is that `worker.evaluateHandle` returns
in-page object (JSHandle).
The only difference between [`method: Worker.evaluate`] and
[`method: Worker.evaluateHandle`] is that [`method: Worker.evaluateHandle`]
returns [JSHandle].
If the function passed to the `worker.evaluateHandle` returns a [Promise], then `worker.evaluateHandle` would wait for
If the function passed to the [`method: Worker.evaluateHandle`] returns a [Promise], then [`method: Worker.evaluateHandle`] would wait for
the promise to resolve and return its value.
### param: Worker.evaluateHandle.expression = %%-evaluate-expression-%%

View File

@ -14,3 +14,5 @@
### param: Page.waitForFunction.expression = %%-js-evaluate-pagefunction-%%
### param: Worker.evaluate.expression = %%-js-worker-evaluate-workerfunction-%%
### param: Worker.evaluateHandle.expression = %%-js-worker-evaluate-workerfunction-%%
### param: ElectronApplication.evaluate.expression = %%-js-electron-evaluate-workerfunction-%%
### param: ElectronApplication.evaluateHandle.expression = %%-js-electron-evaluate-workerfunction-%%

View File

@ -157,32 +157,39 @@ Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `nul
## evaluate-expression
- `expression` <[string]>
JavaScript expression to be evaluated in the browser context. If it looks like a function declaration,
it is interpreted as a function. Otherwise, evaluated as an expression.
JavaScript expression to be evaluated in the browser context. If it looks like
a function declaration, it is interpreted as a function. Otherwise, evaluated
as an expression.
## js-evaluate-pagefunction
* langs: js
- `pageFunction` <[function]|[string]>
Function to be evaluated in the page context
Function to be evaluated in the page context.
## js-evalonselector-pagefunction
* langs: js
- `pageFunction` <[function]\([Element]\)>
Function to be evaluated in the page context
Function to be evaluated in the page context.
## js-evalonselectorall-pagefunction
* langs: js
- `pageFunction` <[function]\([Array]<[Element]>\)>
Function to be evaluated in the page context
Function to be evaluated in the page context.
## js-worker-evaluate-workerfunction
* langs: js
- `pageFunction` <[function]|[string]>
Function to be evaluated in the worker context
Function to be evaluated in the worker context.
## js-electron-evaluate-workerfunction
* langs: js
- `pageFunction` <[function]|[Electron]>
Function to be evaluated in the worker context.
## python-context-option-viewport
* langs: python

View File

@ -132,7 +132,7 @@ composite selectors. To use this:
### Evaluate Source Maps
PWDEBUG also enables source maps for [`page.evaluate` executions](./core-concepts.md#evaluation).
PWDEBUG also enables source maps for [`method: Page.evaluate`] [executions](./core-concepts.md#evaluation).
This improves the debugging experience for JavaScript executions in the page context.
<a href="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png"><img src="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png" width="500" alt="Highlight selectors"></img></a>

View File

@ -311,7 +311,7 @@ function test_electron_types {
npm install electron@9.0
npm install -D typescript@3.8
npm install -D @types/node@10.17
echo "import { Page, electron, ElectronApplication, ElectronLauncher } from 'playwright-electron';" > "test.ts"
echo "import { Page, electron, ElectronApplication, Electron } from 'playwright-electron';" > "test.ts"
echo "Running tsc"
npx tsc "test.ts"

View File

@ -18,9 +18,7 @@ const playwright = require('playwright-electron');
const path = require('path');
(async () => {
const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
const electronPath = path.join(__dirname, 'node_modules', '.bin', electronName);
const application = await playwright.electron.launch(electronPath, {
const application = await playwright.electron.launch({
args: [path.join(__dirname, 'electron-app.js')],
});
const appPath = await application.evaluate(async ({ app }) => app.getAppPath());

View File

@ -61,7 +61,6 @@ app.whenReady().then(createWindow);
```js
const { electron } = require('playwright-electron');
const assert = require('assert');
const electronPath = require('electron');
const path = require('path')
describe('Sanity checks', function () {
@ -69,8 +68,7 @@ describe('Sanity checks', function () {
beforeEach(async () => {
// Before each test start Electron application.
this.app = await electron.launch(electronPath, {
path: electronPath,
this.app = await electron.launch({
args: [path.join(__dirname, '..')] // loads index.js
});
});

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { ElectronLauncher } from './types/electron';
import { Electron } from './types/types';
export * from './types/types';
export * from './types/electron';
export const electron: ElectronLauncher;
export const electron: Electron;

View File

@ -22,6 +22,7 @@ export { BrowserType } from './browserType';
export { ConsoleMessage } from './consoleMessage';
export { Dialog } from './dialog';
export { Download } from './download';
export { Electron, ElectronApplication } from './electron';
export { ElementHandle } from './elementHandle';
export { FileChooser } from './fileChooser';
export { Logger } from './types';

View File

@ -14,28 +14,27 @@
* limitations under the License.
*/
import * as structs from '../../types/structs';
import * as api from '../../types/types';
import * as channels from '../protocol/channels';
import { TimeoutSettings } from '../utils/timeoutSettings';
import { BrowserContext } from './browserContext';
import { ChannelOwner } from './channelOwner';
import { Page } from './page';
import { serializeArgument, parseResult, JSHandle } from './jsHandle';
import { TimeoutSettings } from '../utils/timeoutSettings';
import { Waiter } from './waiter';
import { Events } from './events';
import { WaitForEventOptions, Env, Logger } from './types';
import { envObjectToArray } from './clientHelper';
import * as electronApi from '../../types/electron';
import * as structs from '../../types/structs';
import type { ChromiumBrowserContext } from './chromiumBrowserContext';
import { envObjectToArray } from './clientHelper';
import { Events } from './events';
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
import { Page } from './page';
import { Env, WaitForEventOptions } from './types';
import { Waiter } from './waiter';
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'> & {
env?: Env,
logger?: Logger,
};
type ElectronAppType = typeof import('electron');
export class Electron extends ChannelOwner<channels.ElectronChannel, channels.ElectronInitializer> implements electronApi.ElectronLauncher {
export class Electron extends ChannelOwner<channels.ElectronChannel, channels.ElectronInitializer> implements api.Electron {
static from(electron: channels.ElectronChannel): Electron {
return (electron as any)._object;
}
@ -44,21 +43,18 @@ export class Electron extends ChannelOwner<channels.ElectronChannel, channels.El
super(parent, type, guid, initializer);
}
async launch(executablePath: string, options: ElectronOptions = {}): Promise<ElectronApplication> {
const logger = options.logger;
options = { ...options, logger: undefined };
async launch(options: ElectronOptions = {}): Promise<ElectronApplication> {
return this._wrapApiCall('electron.launch', async () => {
const params: channels.ElectronLaunchParams = {
...options,
env: options.env ? envObjectToArray(options.env) : undefined,
executablePath,
env: envObjectToArray(options.env ? options.env : process.env),
};
return ElectronApplication.from((await this._channel.launch(params)).electronApplication);
}, logger);
});
}
}
export class ElectronApplication extends ChannelOwner<channels.ElectronApplicationChannel, channels.ElectronApplicationInitializer> implements electronApi.ElectronApplication {
export class ElectronApplication extends ChannelOwner<channels.ElectronApplicationChannel, channels.ElectronApplicationInitializer> implements api.ElectronApplication {
private _context?: BrowserContext;
private _windows = new Set<Page>();
private _timeoutSettings = new TimeoutSettings();
@ -80,12 +76,12 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
this._channel.on('close', () => this.emit(Events.ElectronApplication.Close));
}
windows(): electronApi.ElectronPage[] {
windows(): Page[] {
// TODO: add ElectronPage class inherting from Page.
return [...this._windows] as any as electronApi.ElectronPage[];
return [...this._windows];
}
async firstWindow(): Promise<electronApi.ElectronPage> {
async firstWindow(): Promise<Page> {
return this._wrapApiCall('electronApplication.firstWindow', async () => {
if (this._windows.size)
return this._windows.values().next().value;
@ -93,13 +89,6 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
});
}
async newBrowserWindow(options: any): Promise<electronApi.ElectronPage> {
return this._wrapApiCall('electronApplication.newBrowserWindow', async () => {
const result = await this._channel.newBrowserWindow({ arg: serializeArgument(options) });
return Page.from(result.page) as any as electronApi.ElectronPage;
});
}
context(): ChromiumBrowserContext {
return this._context! as ChromiumBrowserContext;
}

View File

@ -28,7 +28,7 @@ export class ElectronDispatcher extends Dispatcher<Electron, channels.ElectronIn
}
async launch(params: channels.ElectronLaunchParams): Promise<channels.ElectronLaunchResult> {
const electronApplication = await this._object.launch(params.executablePath, params);
const electronApplication = await this._object.launch(params);
return { electronApplication: new ElectronApplicationDispatcher(this._scope, electronApplication) };
}
}
@ -49,11 +49,6 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
});
}
async newBrowserWindow(params: channels.ElectronApplicationNewBrowserWindowParams): Promise<channels.ElectronApplicationNewBrowserWindowResult> {
const page = await this._object.newBrowserWindow(parseArgument(params.arg));
return { page: lookupDispatcher<PageDispatcher>(page) };
}
async evaluateExpression(params: channels.ElectronApplicationEvaluateExpressionParams): Promise<channels.ElectronApplicationEvaluateExpressionResult> {
const handle = this._object._nodeElectronHandle!;
return { value: serializeResult(await handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) };

View File

@ -2452,22 +2452,17 @@ export interface ElectronChannel extends Channel {
launch(params: ElectronLaunchParams, metadata?: Metadata): Promise<ElectronLaunchResult>;
}
export type ElectronLaunchParams = {
executablePath: string,
executablePath?: string,
args?: string[],
cwd?: string,
env?: NameValue[],
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
timeout?: number,
};
export type ElectronLaunchOptions = {
executablePath?: string,
args?: string[],
cwd?: string,
env?: NameValue[],
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
timeout?: number,
};
export type ElectronLaunchResult = {
@ -2480,7 +2475,6 @@ export interface ElectronApplicationChannel extends Channel {
on(event: 'context', callback: (params: ElectronApplicationContextEvent) => void): this;
on(event: 'close', callback: (params: ElectronApplicationCloseEvent) => void): this;
on(event: 'window', callback: (params: ElectronApplicationWindowEvent) => void): this;
newBrowserWindow(params: ElectronApplicationNewBrowserWindowParams, metadata?: Metadata): Promise<ElectronApplicationNewBrowserWindowResult>;
evaluateExpression(params: ElectronApplicationEvaluateExpressionParams, metadata?: Metadata): Promise<ElectronApplicationEvaluateExpressionResult>;
evaluateExpressionHandle(params: ElectronApplicationEvaluateExpressionHandleParams, metadata?: Metadata): Promise<ElectronApplicationEvaluateExpressionHandleResult>;
close(params?: ElectronApplicationCloseParams, metadata?: Metadata): Promise<ElectronApplicationCloseResult>;
@ -2493,15 +2487,6 @@ export type ElectronApplicationWindowEvent = {
page: PageChannel,
browserWindow: JSHandleChannel,
};
export type ElectronApplicationNewBrowserWindowParams = {
arg: SerializedArgument,
};
export type ElectronApplicationNewBrowserWindowOptions = {
};
export type ElectronApplicationNewBrowserWindowResult = {
page: PageChannel,
};
export type ElectronApplicationEvaluateExpressionParams = {
expression: string,
isFunction: boolean,

View File

@ -2080,7 +2080,6 @@ CDPSession:
params: json?
Electron:
type: interface
@ -2088,7 +2087,7 @@ Electron:
launch:
parameters:
executablePath: string
executablePath: string?
args:
type: array?
items: string
@ -2096,26 +2095,16 @@ Electron:
env:
type: array?
items: NameValue
handleSIGINT: boolean?
handleSIGTERM: boolean?
handleSIGHUP: boolean?
timeout: number?
returns:
electronApplication: ElectronApplication
ElectronApplication:
type: interface
commands:
newBrowserWindow:
parameters:
arg: SerializedArgument
returns:
page: Page
evaluateExpression:
parameters:
expression: string

View File

@ -911,18 +911,12 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
});
scheme.CDPSessionDetachParams = tOptional(tObject({}));
scheme.ElectronLaunchParams = tObject({
executablePath: tString,
executablePath: tOptional(tString),
args: tOptional(tArray(tString)),
cwd: tOptional(tString),
env: tOptional(tArray(tType('NameValue'))),
handleSIGINT: tOptional(tBoolean),
handleSIGTERM: tOptional(tBoolean),
handleSIGHUP: tOptional(tBoolean),
timeout: tOptional(tNumber),
});
scheme.ElectronApplicationNewBrowserWindowParams = tObject({
arg: tType('SerializedArgument'),
});
scheme.ElectronApplicationEvaluateExpressionParams = tObject({
expression: tString,
isFunction: tBoolean,

View File

@ -14827,7 +14827,7 @@ other objects in their object group.
export type RemoteObjectId = string;
/**
* Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`,
`-Infinity`, and bigint literals.
`-Infinity`.
*/
export type UnserializableValue = string;
/**

View File

@ -35,12 +35,10 @@ import * as readline from 'readline';
import { RecentLogsCollector } from '../../utils/debugLogger';
export type ElectronLaunchOptionsBase = {
executablePath?: string,
args?: string[],
cwd?: string,
env?: types.EnvArray,
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
timeout?: number,
};
@ -95,21 +93,6 @@ export class ElectronApplication extends EventEmitter {
this.emit(ElectronApplication.Events.Window, page);
}
async newBrowserWindow(options: any): Promise<Page> {
const windowId = await this._nodeElectronHandle!.evaluate(async ({ BrowserWindow }, options) => {
const win = new BrowserWindow(options);
win.loadURL('about:blank');
return win.id;
}, options);
for (const page of this._windows) {
if (page._browserWindowId === windowId)
return page;
}
return await this._waitForEvent(ElectronApplication.Events.Window, (page: ElectronPage) => page._browserWindowId === windowId);
}
context(): BrowserContext {
return this._browserContext;
}
@ -145,12 +128,9 @@ export class Electron {
this._playwrightOptions = playwrightOptions;
}
async launch(executablePath: string, options: ElectronLaunchOptionsBase = {}): Promise<ElectronApplication> {
async launch(options: ElectronLaunchOptionsBase = {}): Promise<ElectronApplication> {
const {
args = [],
handleSIGINT = true,
handleSIGTERM = true,
handleSIGHUP = true,
} = options;
const controller = new ProgressController();
controller.setLogName('browser');
@ -166,12 +146,9 @@ export class Electron {
const browserLogsCollector = new RecentLogsCollector();
const { launchedProcess, gracefullyClose, kill } = await launchProcess({
executablePath,
executablePath: options.executablePath || require('electron/index.js'),
args: electronArguments,
env: options.env ? envArrayToObject(options.env) : process.env,
handleSIGINT,
handleSIGTERM,
handleSIGHUP,
log: (message: string) => {
progress.log(message);
browserLogsCollector.log(message);

View File

@ -65,6 +65,8 @@ function apiForBrowser(browserName) {
const api = require('../lib/client/api');
const otherBrowsers = ['chromium', 'webkit', 'firefox'].filter(name => name.toLowerCase() !== browserName.toLowerCase());
const filteredKeys = Object.keys(api).filter(apiName => {
if (apiName.toLowerCase().startsWith('electron'))
return browserName === 'chromium';
return !otherBrowsers.some(otherName => apiName.toLowerCase().startsWith(otherName));
});
const filteredAPI = {};

View File

@ -14,122 +14,82 @@
* limitations under the License.
*/
import path from 'path';
import { folio } from './electron.fixture';
const { it, expect, describe } = folio;
import path from 'path';
const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
describe('electron app', (suite, { browserName }) => {
suite.skip(browserName !== 'chromium');
}, () => {
it('should fire close event', async ({ playwright }) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
const application = await playwright._electron.launch(electronPath, {
const electronApp = await playwright._electron.launch({
args: [path.join(__dirname, 'testApp.js')],
});
const events = [];
application.on('close', () => events.push('application'));
application.context().on('close', () => events.push('context'));
await application.close();
electronApp.on('close', () => events.push('application'));
electronApp.context().on('close', () => events.push('context'));
await electronApp.close();
expect(events.join('|')).toBe('context|application');
// Give it some time to fire more events - there should not be any.
await new Promise(f => setTimeout(f, 1000));
expect(events.join('|')).toBe('context|application');
});
it('should script application', async ({ application }) => {
const appPath = await application.evaluate(async ({ app }) => app.getAppPath());
it('should script application', async ({ electronApp }) => {
const appPath = await electronApp.evaluate(async ({ app }) => app.getAppPath());
expect(appPath).toContain('electron');
});
it('should create window', async ({ application }) => {
const [ page ] = await Promise.all([
application.waitForEvent('window'),
application.evaluate(({ BrowserWindow }) => {
const window = new BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World 1</title>');
})
]);
await page.waitForLoadState('domcontentloaded');
expect(await page.title()).toBe('Hello World 1');
it('should return windows', async ({ electronApp, newWindow }) => {
const window = await newWindow();
expect(electronApp.windows()).toEqual([window]);
});
it('should create window 2', async ({ application }) => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<title>Hello World 2</title>');
expect(await page.title()).toBe('Hello World 2');
it('should evaluate handle', async ({ electronApp }) => {
const appHandle = await electronApp.evaluateHandle(({ app }) => app);
expect(await electronApp.evaluate(({ app }, appHandle) => app === appHandle, appHandle)).toBeTruthy();
});
it('should create multiple windows', async ({ application }) => {
const createPage = async ordinal => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await Promise.all([
page.waitForNavigation(),
page.browserWindow.evaluate((window, ordinal) => window.loadURL(`data:text/html,<title>Hello World ${ordinal}</title>`), ordinal)
]);
return page;
};
const page1 = await createPage(1);
await createPage(2);
await createPage(3);
await page1.close();
await createPage(4);
const titles = [];
for (const window of application.windows())
titles.push(await window.title());
expect(titles).toEqual(['Hello World 2', 'Hello World 3', 'Hello World 4']);
});
it('should route network', async ({ application }) => {
await application.context().route('**/empty.html', (route, request) => {
it('should route network', async ({ electronApp, newWindow }) => {
await electronApp.context().route('**/empty.html', (route, request) => {
route.fulfill({
status: 200,
contentType: 'text/html',
body: '<title>Hello World</title>',
});
});
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('https://localhost:1000/empty.html');
expect(await page.title()).toBe('Hello World');
const window = await newWindow();
await window.goto('https://localhost:1000/empty.html');
expect(await window.title()).toBe('Hello World');
});
it('should support init script', async ({ application }) => {
await application.context().addInitScript('window.magic = 42;');
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<script>window.copy = magic</script>');
expect(await page.evaluate(() => window['copy'])).toBe(42);
it('should support init script', async ({ electronApp, newWindow }) => {
await electronApp.context().addInitScript('window.magic = 42;');
const window = await newWindow();
await window.goto('data:text/html,<script>window.copy = magic</script>');
expect(await window.evaluate(() => window['copy'])).toBe(42);
});
it('should expose function', async ({ application }) => {
await application.context().exposeFunction('add', (a, b) => a + b);
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<script>window["result"] = add(20, 22);</script>');
expect(await page.evaluate(() => window['result'])).toBe(42);
it('should expose function', async ({ electronApp, newWindow }) => {
await electronApp.context().exposeFunction('add', (a, b) => a + b);
const window = await newWindow();
await window.goto('data:text/html,<script>window["result"] = add(20, 22);</script>');
expect(await window.evaluate(() => window['result'])).toBe(42);
});
it('should wait for first window', async ({ application }) => {
application.evaluate(({ BrowserWindow }) => {
it('should wait for first window', async ({ electronApp }) => {
await electronApp.evaluate(({ BrowserWindow }) => {
const window = new BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World!</title>');
});
const window = await application.firstWindow();
const window = await electronApp.firstWindow();
expect(await window.title()).toBe('Hello World!');
});
it('should have a clipboard instance', async ({ application }) => {
it('should have a clipboard instance', async ({ electronApp }) => {
const clipboardContentToWrite = 'Hello from Playwright';
await application.evaluate(async ({clipboard}, text) => clipboard.writeText(text), clipboardContentToWrite);
const clipboardContentRead = await application.evaluate(async ({clipboard}) => clipboard.readText());
await expect(clipboardContentRead).toEqual(clipboardContentToWrite);
});
it('should be able to send CDP messages', async ({application, window}) => {
const context = await application.context();
const client = await context.newCDPSession(window);
await client.send('Runtime.enable');
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
expect(evalResponse.result.value).toBe(3);
await electronApp.evaluate(async ({clipboard}, text) => clipboard.writeText(text), clipboardContentToWrite);
const clipboardContentRead = await electronApp.evaluate(async ({clipboard}) => clipboard.readText());
expect(clipboardContentRead).toEqual(clipboardContentToWrite);
});
});

View File

@ -15,30 +15,44 @@
*/
import { folio as base } from '../fixtures';
import type { ElectronApplication, ElectronPage } from '../../types/electron';
import path from 'path';
const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
import { ElectronApplication, Page } from '../..';
type TestState = {
application: ElectronApplication;
window: ElectronPage;
electronApp: ElectronApplication;
window: Page;
newWindow: () => Promise<Page>;
};
const fixtures = base.extend<TestState>();
fixtures.application.init(async ({ playwright }, run) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
const application = await playwright._electron.launch(electronPath, {
fixtures.electronApp.init(async ({ playwright }, run) => {
const application = await playwright._electron.launch({
args: [path.join(__dirname, 'testApp.js')],
});
await run(application);
await application.close();
});
fixtures.window.init(async ({ application }, run) => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await run(page);
await page.close();
fixtures.newWindow.init(async ({ electronApp }, run) => {
const windows = [];
const newWindow = async () => {
const [ window ] = await Promise.all([
electronApp.waitForEvent('window'),
electronApp.evaluate(electron => {
const window = new electron.BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World 1</title>');
})
]);
windows.push(window);
return window;
};
await run(newWindow);
for (const window of windows)
await window.close();
});
fixtures.window.init(async ({ newWindow }, run) => {
await run(await newWindow());
});
export const folio = fixtures.build();

View File

@ -1,3 +1,3 @@
const { app } = require('electron');
const { app, BrowserWindow } = require('electron');
app.on('window-all-closed', e => e.preventDefault());
app.on('window-all-closed', e => e.preventDefault());

View File

@ -20,7 +20,7 @@ import fs from 'fs';
import path from 'path';
import util from 'util';
import os from 'os';
import type { Browser, BrowserContext, BrowserType, Page } from '../index';
import type { Browser, BrowserContext, BrowserType, Electron, Page } from '../index';
import { Connection } from '../lib/client/connection';
import { Transport } from '../lib/protocol/transport';
import { installCoverageHooks } from './coverage';
@ -28,7 +28,6 @@ import { folio as httpFolio } from './http.fixtures';
import { folio as playwrightFolio } from './playwright.fixtures';
import { PlaywrightClient } from '../lib/remote/playwrightClient';
import type { Android } from '../types/android';
import type { ElectronLauncher } from '../types/electron';
export { expect, config } from 'folio';
const removeFolderAsync = util.promisify(require('rimraf'));
@ -134,7 +133,7 @@ fixtures.playwright.override(async ({ browserName, testWorkerIndex, platform, mo
stdio: 'pipe'
});
spawnedProcess.stderr.pipe(process.stderr);
await new Promise(f => {
await new Promise<void>(f => {
spawnedProcess.stdout.on('data', data => {
if (data.toString().includes('Listening on'))
f();
@ -195,5 +194,5 @@ export const afterAll = folio.afterAll;
declare module '../index' {
const _android: Android;
const _electron: ElectronLauncher;
const _electron: Electron;
}

57
types/electron.d.ts vendored
View File

@ -1,57 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Logger, Page, JSHandle, ChromiumBrowserContext } from './types';
import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron';
export type ElectronLaunchOptions = {
args?: string[],
cwd?: string,
env?: {[key: string]: string|number|boolean},
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
timeout?: number,
logger?: Logger,
};
export interface ElectronLauncher {
launch(executablePath: string, options?: ElectronLaunchOptions): Promise<ElectronApplication>;
}
export interface ElectronApplication {
on(event: 'window', listener: (page : ElectronPage) => void): this;
addListener(event: 'window', listener: (page : ElectronPage) => void): this;
waitForEvent(event: 'window', optionsOrPredicate?: { predicate?: (page : ElectronPage) => boolean, timeout?: number }): Promise<ElectronPage>;
on(event: 'close', listener: (exitCode? : number) => void): this;
addListener(event: 'close', listener: (exitCode? : number) => void): this;
waitForEvent(event: 'close', optionsOrPredicate?: { predicate?: (exitCode? : number) => boolean, timeout?: number }): Promise<number|undefined>;
context(): ChromiumBrowserContext;
windows(): ElectronPage[];
firstWindow(): Promise<ElectronPage>;
newBrowserWindow(options?: BrowserWindowConstructorOptions): Promise<ElectronPage>;
close(): Promise<void>;
evaluate: HandleToElectron['evaluate'];
evaluateHandle: HandleToElectron['evaluateHandle'];
}
export interface ElectronPage extends Page {
browserWindow: JSHandle<BrowserWindow>;
}
type HandleToElectron = JSHandle<typeof import('electron')>;

2
types/protocol.d.ts vendored
View File

@ -14827,7 +14827,7 @@ other objects in their object group.
export type RemoteObjectId = string;
/**
* Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`,
`-Infinity`, and bigint literals.
`-Infinity`.
*/
export type UnserializableValue = string;
/**

346
types/types.d.ts vendored
View File

@ -85,8 +85,8 @@ export interface Page {
* [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatepagefunction-arg) returns a
* non-[Serializable] value, then
* [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatepagefunction-arg) resolves
* to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`:
* `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
* to `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: `-0`,
* `NaN`, `Infinity`, `-Infinity`.
*
* Passing argument to `pageFunction`:
*
@ -116,21 +116,21 @@ export interface Page {
*
* Shortcut for main frame's
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg).
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<R>;
/**
* Returns the value of the `pageFunction` invocation as in-page object (JSHandle).
* Returns the value of the `pageFunction` invocation as a [JSHandle].
*
* The only difference between
* [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatepagefunction-arg) and
* [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg)
* is that
* [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg)
* returns in-page object (JSHandle).
* returns [JSHandle].
*
* If the function passed to the
* [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg)
@ -159,7 +159,7 @@ export interface Page {
* await resultHandle.dispose();
* ```
*
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
@ -204,7 +204,7 @@ export interface Page {
* Shortcut for main frame's
* [frame.$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevalselector-pagefunction-arg).
* @param selector A selector to query for. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], Arg, R>, arg: Arg): Promise<R>;
@ -227,7 +227,7 @@ export interface Page {
* ```
*
* @param selector A selector to query for. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
$$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], Arg, R>, arg: Arg): Promise<R>;
@ -266,7 +266,7 @@ export interface Page {
*
* Shortcut for main frame's
* [frame.waitForFunction(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-frame#framewaitforfunctionpagefunction-arg-options).
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
* @param options
*/
@ -3178,7 +3178,7 @@ export interface Page {
*/
export interface Frame {
/**
* Returns the return value of `pageFunction`
* Returns the return value of `pageFunction`.
*
* If the function passed to the
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg) returns
@ -3190,8 +3190,8 @@ export interface Frame {
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg) returns
* a non-[Serializable] value, then
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg) returns
* `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`:
* `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
* `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: `-0`,
* `NaN`, `Infinity`, `-Infinity`.
*
* ```js
* const result = await frame.evaluate(([x, y]) => {
@ -3215,19 +3215,19 @@ export interface Frame {
* await bodyHandle.dispose();
* ```
*
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<R>;
/**
* Returns the return value of `pageFunction` as in-page object (JSHandle).
* Returns the return value of `pageFunction` as a [JSHandle].
*
* The only difference between
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg) and
* [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatehandlepagefunction-arg)
* is that [method: Frame.evaluateHandle`] returns in-page object (JSHandle).
* is that [method: Frame.evaluateHandle`] returns [JSHandle].
*
* If the function, passed to the
* [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatehandlepagefunction-arg),
@ -3256,7 +3256,7 @@ export interface Frame {
* await resultHandle.dispose();
* ```
*
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
@ -3283,7 +3283,7 @@ export interface Frame {
$$(selector: string): Promise<ElementHandle<SVGElement | HTMLElement>[]>;
/**
* Returns the return value of `pageFunction`
* Returns the return value of `pageFunction`.
*
* The method finds an element matching the specified selector within the frame and passes it as a first argument to
* `pageFunction`. See [Working with selectors](https://playwright.dev/docs/selectors) for more details. If no elements match the selector, the
@ -3302,7 +3302,7 @@ export interface Frame {
* ```
*
* @param selector A selector to query for. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], Arg, R>, arg: Arg): Promise<R>;
@ -3311,7 +3311,7 @@ export interface Frame {
$eval<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E, void, R>, arg?: any): Promise<R>;
/**
* Returns the return value of `pageFunction`
* Returns the return value of `pageFunction`.
*
* The method finds all elements matching the specified selector within the frame and passes an array of matched elements
* as a first argument to `pageFunction`. See [Working with selectors](https://playwright.dev/docs/selectors) for more details.
@ -3327,7 +3327,7 @@ export interface Frame {
* ```
*
* @param selector A selector to query for. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
$$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], Arg, R>, arg: Arg): Promise<R>;
@ -3362,7 +3362,7 @@ export interface Frame {
* await frame.waitForFunction(selector => !!document.querySelector(selector), selector);
* ```
*
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
* @param options
*/
@ -5102,29 +5102,42 @@ export interface BrowserContext {
*/
export interface Worker {
/**
* Returns the return value of `pageFunction`
* Returns the return value of `pageFunction`.
*
* If the function passed to the `worker.evaluate` returns a [Promise], then `worker.evaluate` would wait for the promise
* to resolve and return its value.
* If the function passed to the
* [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatepagefunction-arg)
* returns a [Promise], then
* [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatepagefunction-arg)
* would wait for the promise to resolve and return its value.
*
* If the function passed to the `worker.evaluate` returns a non-[Serializable] value, then `worker.evaluate` returns
* `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`:
* `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
* @param pageFunction Function to be evaluated in the worker context
* If the function passed to the
* [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatepagefunction-arg)
* returns a non-[Serializable] value, then
* [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatepagefunction-arg)
* returns `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`:
* `-0`, `NaN`, `Infinity`, `-Infinity`.
* @param pageFunction Function to be evaluated in the worker context.
* @param arg Optional argument to pass to `pageFunction`
*/
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<R>;
/**
* Returns the return value of `pageFunction` as in-page object (JSHandle).
* Returns the return value of `pageFunction` as a [JSHandle].
*
* The only difference between `worker.evaluate` and `worker.evaluateHandle` is that `worker.evaluateHandle` returns
* in-page object (JSHandle).
* The only difference between
* [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatepagefunction-arg) and
* [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatehandlepagefunction-arg)
* is that
* [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatehandlepagefunction-arg)
* returns [JSHandle].
*
* If the function passed to the `worker.evaluateHandle` returns a [Promise], then `worker.evaluateHandle` would wait for
* the promise to resolve and return its value.
* @param pageFunction Function to be evaluated in the worker context
* If the function passed to the
* [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatehandlepagefunction-arg)
* returns a [Promise], then
* [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatehandlepagefunction-arg)
* would wait for the promise to resolve and return its value.
* @param pageFunction Function to be evaluated in the worker context.
* @param arg Optional argument to pass to `pageFunction`
*/
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
@ -5178,7 +5191,7 @@ export interface Worker {
*/
export interface JSHandle<T = any> {
/**
* Returns the return value of `pageFunction`
* Returns the return value of `pageFunction`.
*
* This method passes this handle as the first argument to `pageFunction`.
*
@ -5192,19 +5205,19 @@ export interface JSHandle<T = any> {
* expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10 retweets');
* ```
*
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
evaluate<R, Arg, O extends T = T>(pageFunction: PageFunctionOn<O, Arg, R>, arg: Arg): Promise<R>;
evaluate<R, O extends T = T>(pageFunction: PageFunctionOn<O, void, R>, arg?: any): Promise<R>;
/**
* Returns the return value of `pageFunction` as in-page object (JSHandle).
* Returns the return value of `pageFunction` as a [JSHandle].
*
* This method passes this handle as the first argument to `pageFunction`.
*
* The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle` returns
* in-page object (JSHandle).
* [JSHandle].
*
* If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would wait
* for the promise to resolve and return its value.
@ -5212,7 +5225,7 @@ export interface JSHandle<T = any> {
* See
* [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg)
* for more details.
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
evaluateHandle<R, Arg, O extends T = T>(pageFunction: PageFunctionOn<O, Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
@ -5300,7 +5313,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
$$(selector: string): Promise<ElementHandle<SVGElement | HTMLElement>[]>;
/**
* Returns the return value of `pageFunction`
* Returns the return value of `pageFunction`.
*
* The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a first
* argument to `pageFunction`. See [Working with selectors](https://playwright.dev/docs/selectors) for more details. If no elements match the
@ -5319,7 +5332,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* ```
*
* @param selector A selector to query for. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], Arg, R>, arg: Arg): Promise<R>;
@ -5328,7 +5341,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
$eval<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E, void, R>, arg?: any): Promise<R>;
/**
* Returns the return value of `pageFunction`
* Returns the return value of `pageFunction`.
*
* The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array of
* matched elements as a first argument to `pageFunction`. See [Working with selectors](https://playwright.dev/docs/selectors) for more details.
@ -5352,7 +5365,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* ```
*
* @param selector A selector to query for. See [working with selectors](https://playwright.dev/docs/selectors) for more details.
* @param pageFunction Function to be evaluated in the page context
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`
*/
$$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], Arg, R>, arg: Arg): Promise<R>;
@ -6952,6 +6965,179 @@ type AccessibilityNode = {
export const selectors: Selectors;
export const devices: Devices & DeviceDescriptor[];
/**
* Electron application representation. You can use
* [electron.launch([options])](https://playwright.dev/docs/api/class-electron#electronlaunchoptions) to obtain the
* application instance. This instance you can control main electron process as well as work with Electron windows:
*
* ```js
* const { _electron: electron } = require('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 (electron) => {
* // This runs in the main Electron process, |electron| parameter
* // here is always the result of the require('electron') in the main
* // app script.
* return electron.getAppPath();
* });
*
* // 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');
* })();
* ```
*
*/
export interface ElectronApplication {
/**
* Returns the return value of `pageFunction`.
*
* If the function passed to the
* [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatepagefunction-arg)
* returns a [Promise], then
* [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatepagefunction-arg)
* would wait for the promise to resolve and return its value.
*
* If the function passed to the
* [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatepagefunction-arg)
* returns a non-[Serializable] value, then
* [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatepagefunction-arg)
* returns `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`:
* `-0`, `NaN`, `Infinity`, `-Infinity`.
* @param pageFunction Function to be evaluated in the worker context.
* @param arg Optional argument to pass to `pageFunction`.
*/
evaluate<R, Arg>(pageFunction: PageFunctionOn<typeof import('electron'), Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunctionOn<typeof import('electron'), void, R>, arg?: any): Promise<R>;
/**
* Returns the return value of `pageFunction` as a [JSHandle].
*
* The only difference between
* [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatepagefunction-arg)
* and
* [electronApplication.evaluateHandle(pageFunction, arg)](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatehandlepagefunction-arg)
* is that
* [electronApplication.evaluateHandle(pageFunction, arg)](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatehandlepagefunction-arg)
* returns [JSHandle].
*
* If the function passed to the
* [electronApplication.evaluateHandle(pageFunction, arg)](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatehandlepagefunction-arg)
* returns a [Promise], then
* [electronApplication.evaluateHandle(pageFunction, arg)](https://playwright.dev/docs/api/class-electronapplication#electronapplicationevaluatehandlepagefunction-arg)
* would wait for the promise to resolve and return its value.
* @param pageFunction Function to be evaluated in the worker context.
* @param arg
*/
evaluateHandle<R, Arg>(pageFunction: PageFunctionOn<typeof import('electron'), Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
evaluateHandle<R>(pageFunction: PageFunctionOn<typeof import('electron'), void, R>, arg?: any): Promise<SmartHandle<R>>;
/**
* This event is issued when the application closes.
*/
on(event: 'close', listener: () => void): this;
/**
* This event is issued for every window that is created **and loaded** in Electron. It contains a [Page] that can be used
* for Playwright automation.
*/
on(event: 'window', listener: (page: Page) => void): this;
/**
* This event is issued when the application closes.
*/
once(event: 'close', listener: () => void): this;
/**
* This event is issued for every window that is created **and loaded** in Electron. It contains a [Page] that can be used
* for Playwright automation.
*/
once(event: 'window', listener: (page: Page) => void): this;
/**
* This event is issued when the application closes.
*/
addListener(event: 'close', listener: () => void): this;
/**
* This event is issued for every window that is created **and loaded** in Electron. It contains a [Page] that can be used
* for Playwright automation.
*/
addListener(event: 'window', listener: (page: Page) => void): this;
/**
* This event is issued when the application closes.
*/
removeListener(event: 'close', listener: () => void): this;
/**
* This event is issued for every window that is created **and loaded** in Electron. It contains a [Page] that can be used
* for Playwright automation.
*/
removeListener(event: 'window', listener: (page: Page) => void): this;
/**
* This event is issued when the application closes.
*/
off(event: 'close', listener: () => void): this;
/**
* This event is issued for every window that is created **and loaded** in Electron. It contains a [Page] that can be used
* for Playwright automation.
*/
off(event: 'window', listener: (page: Page) => void): this;
/**
* Closes Electron application.
*/
close(): Promise<void>;
/**
* This method returns browser context that can be used for setting up context-wide routing, etc.
*/
context(): BrowserContext;
/**
* Convenience method that waits for the first application window to be opened. Typically your script will start with:
*
* ```js
* const electronApp = await electron.launch({
* args: ['main.js']
* });
* const window = await electronApp.firstWindow();
* // ...
* ```
*
*/
firstWindow(): Promise<Page>;
/**
* This event is issued when the application closes.
*/
waitForEvent(event: 'close', optionsOrPredicate?: { predicate?: () => boolean, timeout?: number } | (() => boolean)): Promise<void>;
/**
* This event is issued for every window that is created **and loaded** in Electron. It contains a [Page] that can be used
* for Playwright automation.
*/
waitForEvent(event: 'window', optionsOrPredicate?: { predicate?: (page: Page) => boolean, timeout?: number } | ((page: Page) => boolean)): Promise<Page>;
/**
* Convenience method that returns all the opened windows.
*/
windows(): Array<Page>;}
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
export {};
@ -7979,6 +8165,80 @@ export interface Download {
url(): string;
}
/**
* Playwright has **experimental** support for Electron automation. You can access electron namespace via:
*
* ```js
* const { _electron } = require('playwright');
* ```
*
* An example of the Electron automation script would be:
*
* ```js
* const { _electron: electron } = require('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 (electron) => {
* // This runs in the main Electron process, |electron| parameter
* // here is always the result of the require('electron') in the main
* // app script.
* return electron.getAppPath();
* });
*
* // 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');
* })();
* ```
*
* Note that since you don't need Playwright to install web browsers when testing Electron, you can omit browser download
* via setting the following environment variable when installing Playwright:
*
* ```sh js
* $ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright
* ```
*
*/
export interface Electron {
/**
* Launches electron application specified with the `executablePath`.
* @param options
*/
launch(options?: {
/**
* Additional arguments to pass to the application when launching. You typically pass the main script name here.
*/
args?: Array<string>;
/**
* Current working directory to launch application from.
*/
cwd?: string;
/**
* Specifies environment variables that will be visible to Electron. Defaults to `process.env`.
*/
env?: { [key: string]: string; };
/**
* Launches given Electron application. If not specified, launches the default Electron executable installed in this
* package, located at `node_modules/.bin/electron`.
*/
executablePath?: string;
}): Promise<ElectronApplication>;
}
/**
* [FileChooser] objects are dispatched by the page in the
* [page.on('filechooser')](https://playwright.dev/docs/api/class-page#pageonfilechooser) event.

View File

@ -44,8 +44,12 @@ class ApiParser {
md.visitAll(api, node => {
if (node.type === 'h1')
this.parseClass(node);
});
md.visitAll(api, node => {
if (node.type === 'h2')
this.parseMember(node);
});
md.visitAll(api, node => {
if (node.type === 'h3')
this.parseArgument(node);
});
@ -131,6 +135,8 @@ class ApiParser {
arg.name = name;
const existingArg = method.argsArray.find(m => m.name === arg.name);
if (existingArg) {
if (!arg.langs || !arg.langs.only)
throw new Error('Override does not have lang: ' + spec.text);
for (const lang of arg.langs.only) {
existingArg.langs.overrides = existingArg.langs.overrides || {};
existingArg.langs.overrides[lang] = arg;

View File

@ -222,5 +222,13 @@ type AccessibilityNode = {
export const selectors: Selectors;
export const devices: Devices & DeviceDescriptor[];
export interface ElectronApplication {
evaluate<R, Arg>(pageFunction: PageFunctionOn<typeof import('electron'), Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunctionOn<typeof import('electron'), void, R>, arg?: any): Promise<R>;
evaluateHandle<R, Arg>(pageFunction: PageFunctionOn<typeof import('electron'), Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
evaluateHandle<R>(pageFunction: PageFunctionOn<typeof import('electron'), void, R>, arg?: any): Promise<SmartHandle<R>>;
}
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
export {};