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 # class: Dialog
[Dialog] objects are dispatched by page via the [`event: Page.dialog`] event. [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 - alias-js: $eval
- returns: <[Serializable]> - 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 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 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 - alias-js: $$eval
- returns: <[Serializable]> - 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 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 matched elements as a first argument to [`param: expression`]. See

View File

@ -300,7 +300,7 @@ Optional event-specific initialization properties.
- alias-js: $eval - alias-js: $eval
- returns: <[Serializable]> - 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 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 [`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 - alias-js: $$eval
- returns: <[Serializable]> - 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 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 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 ## async method: Frame.evaluate
- returns: <[Serializable]> - 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 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. resolve and return its value.
If the function passed to the [`method: Frame.evaluate`] returns a non-[Serializable] value, then 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 [`method: Frame.evaluate`] returns `undefined`. Playwright also supports transferring some
are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals. additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
```js ```js
const result = await frame.evaluate(([x, y]) => { const result = await frame.evaluate(([x, y]) => {
@ -455,10 +455,10 @@ Optional argument to pass to [`param: expression`]
## async method: Frame.evaluateHandle ## async method: Frame.evaluateHandle
- returns: <[JSHandle]> - 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 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 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. [`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 ## async method: JSHandle.evaluate
- returns: <[Serializable]> - 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`]. 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 ## async method: JSHandle.evaluateHandle
- returns: <[JSHandle]> - 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`]. 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 The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle` returns [JSHandle].
in-page object (JSHandle).
If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would wait 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. 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. for the promise to resolve and return its value.
If the function passed to the [`method: Page.evaluate`] returns a non-[Serializable] value, then 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 [`method: Page.evaluate`] resolves to `undefined`. Playwright also supports transferring some
that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals. additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
Passing argument to [`param: expression`]: Passing argument to [`param: expression`]:
@ -882,10 +882,9 @@ Optional argument to pass to [`param: expression`]
## async method: Page.evaluateHandle ## async method: Page.evaluateHandle
- returns: <[JSHandle]> - 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 The only difference between [`method: Page.evaluate`] and [`method: Page.evaluateHandle`] is that [`method: Page.evaluateHandle`] returns [JSHandle].
object (JSHandle).
If the function passed to the [`method: Page.evaluateHandle`] returns a [Promise], then [`method: Page.evaluateHandle`] would wait for the 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. 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 ## async method: Worker.evaluate
- returns: <[Serializable]> - 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. to resolve and return its value.
If the function passed to the `worker.evaluate` returns a non-[Serializable] value, then `worker.evaluate` returns 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
`undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
`-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
### param: Worker.evaluate.expression = %%-evaluate-expression-%% ### param: Worker.evaluate.expression = %%-evaluate-expression-%%
@ -54,12 +53,13 @@ Optional argument to pass to [`param: expression`]
## async method: Worker.evaluateHandle ## async method: Worker.evaluateHandle
- returns: <[JSHandle]> - 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 The only difference between [`method: Worker.evaluate`] and
in-page object (JSHandle). [`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. the promise to resolve and return its value.
### param: Worker.evaluateHandle.expression = %%-evaluate-expression-%% ### param: Worker.evaluateHandle.expression = %%-evaluate-expression-%%

View File

@ -14,3 +14,5 @@
### param: Page.waitForFunction.expression = %%-js-evaluate-pagefunction-%% ### param: Page.waitForFunction.expression = %%-js-evaluate-pagefunction-%%
### param: Worker.evaluate.expression = %%-js-worker-evaluate-workerfunction-%% ### param: Worker.evaluate.expression = %%-js-worker-evaluate-workerfunction-%%
### param: Worker.evaluateHandle.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 ## evaluate-expression
- `expression` <[string]> - `expression` <[string]>
JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, JavaScript expression to be evaluated in the browser context. If it looks like
it is interpreted as a function. Otherwise, evaluated as an expression. a function declaration, it is interpreted as a function. Otherwise, evaluated
as an expression.
## js-evaluate-pagefunction ## js-evaluate-pagefunction
* langs: js * langs: js
- `pageFunction` <[function]|[string]> - `pageFunction` <[function]|[string]>
Function to be evaluated in the page context Function to be evaluated in the page context.
## js-evalonselector-pagefunction ## js-evalonselector-pagefunction
* langs: js * langs: js
- `pageFunction` <[function]\([Element]\)> - `pageFunction` <[function]\([Element]\)>
Function to be evaluated in the page context Function to be evaluated in the page context.
## js-evalonselectorall-pagefunction ## js-evalonselectorall-pagefunction
* langs: js * langs: js
- `pageFunction` <[function]\([Array]<[Element]>\)> - `pageFunction` <[function]\([Array]<[Element]>\)>
Function to be evaluated in the page context Function to be evaluated in the page context.
## js-worker-evaluate-workerfunction ## js-worker-evaluate-workerfunction
* langs: js * langs: js
- `pageFunction` <[function]|[string]> - `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 ## python-context-option-viewport
* langs: python * langs: python

View File

@ -132,7 +132,7 @@ composite selectors. To use this:
### Evaluate Source Maps ### 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. 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> <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 electron@9.0
npm install -D typescript@3.8 npm install -D typescript@3.8
npm install -D @types/node@10.17 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" echo "Running tsc"
npx tsc "test.ts" npx tsc "test.ts"

View File

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

View File

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

View File

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

View File

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

View File

@ -14,28 +14,27 @@
* limitations under the License. * limitations under the License.
*/ */
import * as structs from '../../types/structs';
import * as api from '../../types/types';
import * as channels from '../protocol/channels'; import * as channels from '../protocol/channels';
import { TimeoutSettings } from '../utils/timeoutSettings';
import { BrowserContext } from './browserContext'; import { BrowserContext } from './browserContext';
import { ChannelOwner } from './channelOwner'; 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 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'> & { type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'> & {
env?: Env, env?: Env,
logger?: Logger,
}; };
type ElectronAppType = typeof import('electron'); 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 { static from(electron: channels.ElectronChannel): Electron {
return (electron as any)._object; return (electron as any)._object;
} }
@ -44,21 +43,18 @@ export class Electron extends ChannelOwner<channels.ElectronChannel, channels.El
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
} }
async launch(executablePath: string, options: ElectronOptions = {}): Promise<ElectronApplication> { async launch(options: ElectronOptions = {}): Promise<ElectronApplication> {
const logger = options.logger;
options = { ...options, logger: undefined };
return this._wrapApiCall('electron.launch', async () => { return this._wrapApiCall('electron.launch', async () => {
const params: channels.ElectronLaunchParams = { const params: channels.ElectronLaunchParams = {
...options, ...options,
env: options.env ? envObjectToArray(options.env) : undefined, env: envObjectToArray(options.env ? options.env : process.env),
executablePath,
}; };
return ElectronApplication.from((await this._channel.launch(params)).electronApplication); 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 _context?: BrowserContext;
private _windows = new Set<Page>(); private _windows = new Set<Page>();
private _timeoutSettings = new TimeoutSettings(); private _timeoutSettings = new TimeoutSettings();
@ -80,12 +76,12 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
this._channel.on('close', () => this.emit(Events.ElectronApplication.Close)); this._channel.on('close', () => this.emit(Events.ElectronApplication.Close));
} }
windows(): electronApi.ElectronPage[] { windows(): Page[] {
// TODO: add ElectronPage class inherting from 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 () => { return this._wrapApiCall('electronApplication.firstWindow', async () => {
if (this._windows.size) if (this._windows.size)
return this._windows.values().next().value; 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 { context(): ChromiumBrowserContext {
return this._context! as 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> { 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) }; 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> { async evaluateExpression(params: channels.ElectronApplicationEvaluateExpressionParams): Promise<channels.ElectronApplicationEvaluateExpressionResult> {
const handle = this._object._nodeElectronHandle!; const handle = this._object._nodeElectronHandle!;
return { value: serializeResult(await handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) }; 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>; launch(params: ElectronLaunchParams, metadata?: Metadata): Promise<ElectronLaunchResult>;
} }
export type ElectronLaunchParams = { export type ElectronLaunchParams = {
executablePath: string, executablePath?: string,
args?: string[], args?: string[],
cwd?: string, cwd?: string,
env?: NameValue[], env?: NameValue[],
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
timeout?: number, timeout?: number,
}; };
export type ElectronLaunchOptions = { export type ElectronLaunchOptions = {
executablePath?: string,
args?: string[], args?: string[],
cwd?: string, cwd?: string,
env?: NameValue[], env?: NameValue[],
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
timeout?: number, timeout?: number,
}; };
export type ElectronLaunchResult = { export type ElectronLaunchResult = {
@ -2480,7 +2475,6 @@ export interface ElectronApplicationChannel extends Channel {
on(event: 'context', callback: (params: ElectronApplicationContextEvent) => void): this; on(event: 'context', callback: (params: ElectronApplicationContextEvent) => void): this;
on(event: 'close', callback: (params: ElectronApplicationCloseEvent) => void): this; on(event: 'close', callback: (params: ElectronApplicationCloseEvent) => void): this;
on(event: 'window', callback: (params: ElectronApplicationWindowEvent) => void): this; on(event: 'window', callback: (params: ElectronApplicationWindowEvent) => void): this;
newBrowserWindow(params: ElectronApplicationNewBrowserWindowParams, metadata?: Metadata): Promise<ElectronApplicationNewBrowserWindowResult>;
evaluateExpression(params: ElectronApplicationEvaluateExpressionParams, metadata?: Metadata): Promise<ElectronApplicationEvaluateExpressionResult>; evaluateExpression(params: ElectronApplicationEvaluateExpressionParams, metadata?: Metadata): Promise<ElectronApplicationEvaluateExpressionResult>;
evaluateExpressionHandle(params: ElectronApplicationEvaluateExpressionHandleParams, metadata?: Metadata): Promise<ElectronApplicationEvaluateExpressionHandleResult>; evaluateExpressionHandle(params: ElectronApplicationEvaluateExpressionHandleParams, metadata?: Metadata): Promise<ElectronApplicationEvaluateExpressionHandleResult>;
close(params?: ElectronApplicationCloseParams, metadata?: Metadata): Promise<ElectronApplicationCloseResult>; close(params?: ElectronApplicationCloseParams, metadata?: Metadata): Promise<ElectronApplicationCloseResult>;
@ -2493,15 +2487,6 @@ export type ElectronApplicationWindowEvent = {
page: PageChannel, page: PageChannel,
browserWindow: JSHandleChannel, browserWindow: JSHandleChannel,
}; };
export type ElectronApplicationNewBrowserWindowParams = {
arg: SerializedArgument,
};
export type ElectronApplicationNewBrowserWindowOptions = {
};
export type ElectronApplicationNewBrowserWindowResult = {
page: PageChannel,
};
export type ElectronApplicationEvaluateExpressionParams = { export type ElectronApplicationEvaluateExpressionParams = {
expression: string, expression: string,
isFunction: boolean, isFunction: boolean,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,30 +15,44 @@
*/ */
import { folio as base } from '../fixtures'; import { folio as base } from '../fixtures';
import type { ElectronApplication, ElectronPage } from '../../types/electron';
import path from 'path'; import path from 'path';
import { ElectronApplication, Page } from '../..';
const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
type TestState = { type TestState = {
application: ElectronApplication; electronApp: ElectronApplication;
window: ElectronPage; window: Page;
newWindow: () => Promise<Page>;
}; };
const fixtures = base.extend<TestState>(); const fixtures = base.extend<TestState>();
fixtures.application.init(async ({ playwright }, run) => { fixtures.electronApp.init(async ({ playwright }, run) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName); const application = await playwright._electron.launch({
const application = await playwright._electron.launch(electronPath, {
args: [path.join(__dirname, 'testApp.js')], args: [path.join(__dirname, 'testApp.js')],
}); });
await run(application); await run(application);
await application.close(); await application.close();
}); });
fixtures.window.init(async ({ application }, run) => { fixtures.newWindow.init(async ({ electronApp }, run) => {
const page = await application.newBrowserWindow({ width: 800, height: 600 }); const windows = [];
await run(page); const newWindow = async () => {
await page.close(); 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(); 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 path from 'path';
import util from 'util'; import util from 'util';
import os from 'os'; 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 { Connection } from '../lib/client/connection';
import { Transport } from '../lib/protocol/transport'; import { Transport } from '../lib/protocol/transport';
import { installCoverageHooks } from './coverage'; import { installCoverageHooks } from './coverage';
@ -28,7 +28,6 @@ import { folio as httpFolio } from './http.fixtures';
import { folio as playwrightFolio } from './playwright.fixtures'; import { folio as playwrightFolio } from './playwright.fixtures';
import { PlaywrightClient } from '../lib/remote/playwrightClient'; import { PlaywrightClient } from '../lib/remote/playwrightClient';
import type { Android } from '../types/android'; import type { Android } from '../types/android';
import type { ElectronLauncher } from '../types/electron';
export { expect, config } from 'folio'; export { expect, config } from 'folio';
const removeFolderAsync = util.promisify(require('rimraf')); const removeFolderAsync = util.promisify(require('rimraf'));
@ -134,7 +133,7 @@ fixtures.playwright.override(async ({ browserName, testWorkerIndex, platform, mo
stdio: 'pipe' stdio: 'pipe'
}); });
spawnedProcess.stderr.pipe(process.stderr); spawnedProcess.stderr.pipe(process.stderr);
await new Promise(f => { await new Promise<void>(f => {
spawnedProcess.stdout.on('data', data => { spawnedProcess.stdout.on('data', data => {
if (data.toString().includes('Listening on')) if (data.toString().includes('Listening on'))
f(); f();
@ -195,5 +194,5 @@ export const afterAll = folio.afterAll;
declare module '../index' { declare module '../index' {
const _android: Android; 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; export type RemoteObjectId = string;
/** /**
* Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`, * Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`,
`-Infinity`, and bigint literals. `-Infinity`.
*/ */
export type UnserializableValue = string; 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 * [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatepagefunction-arg) returns a
* non-[Serializable] value, then * non-[Serializable] value, then
* [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatepagefunction-arg) resolves * [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`: * to `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: `-0`,
* `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals. * `NaN`, `Infinity`, `-Infinity`.
* *
* Passing argument to `pageFunction`: * Passing argument to `pageFunction`:
* *
@ -116,21 +116,21 @@ export interface Page {
* *
* Shortcut for main frame's * Shortcut for main frame's
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg). * [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` * @param arg Optional argument to pass to `pageFunction`
*/ */
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>; evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): 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 * The only difference between
* [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatepagefunction-arg) and * [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) * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg)
* is that * is that
* [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg) * [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 * If the function passed to the
* [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg) * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg)
@ -159,7 +159,7 @@ export interface Page {
* await resultHandle.dispose(); * 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` * @param arg Optional argument to pass to `pageFunction`
*/ */
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>; evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
@ -204,7 +204,7 @@ export interface Page {
* Shortcut for main frame's * Shortcut for main frame's
* [frame.$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevalselector-pagefunction-arg). * [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 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` * @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>; $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 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` * @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>; $$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 * Shortcut for main frame's
* [frame.waitForFunction(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-frame#framewaitforfunctionpagefunction-arg-options). * [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 arg Optional argument to pass to `pageFunction`
* @param options * @param options
*/ */
@ -3178,7 +3178,7 @@ export interface Page {
*/ */
export interface Frame { export interface Frame {
/** /**
* Returns the return value of `pageFunction` * Returns the return value of `pageFunction`.
* *
* If the function passed to the * If the function passed to the
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg) returns * [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 * [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg) returns
* a non-[Serializable] value, then * a non-[Serializable] value, then
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg) returns * [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`: * `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: `-0`,
* `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals. * `NaN`, `Infinity`, `-Infinity`.
* *
* ```js * ```js
* const result = await frame.evaluate(([x, y]) => { * const result = await frame.evaluate(([x, y]) => {
@ -3215,19 +3215,19 @@ export interface Frame {
* await bodyHandle.dispose(); * 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` * @param arg Optional argument to pass to `pageFunction`
*/ */
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>; evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): 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 * The only difference between
* [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatepagefunction-arg) and * [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) * [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 * If the function, passed to the
* [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatehandlepagefunction-arg), * [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frameevaluatehandlepagefunction-arg),
@ -3256,7 +3256,7 @@ export interface Frame {
* await resultHandle.dispose(); * 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` * @param arg Optional argument to pass to `pageFunction`
*/ */
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>; 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>[]>; $$(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 * 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 * `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 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` * @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>; $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>; $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 * 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. * 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 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` * @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>; $$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); * 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 arg Optional argument to pass to `pageFunction`
* @param options * @param options
*/ */
@ -5102,29 +5102,42 @@ export interface BrowserContext {
*/ */
export interface Worker { 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 * If the function passed to the
* to resolve and return its value. * [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 * If the function passed to the
* `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: * [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatepagefunction-arg)
* `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals. * returns a non-[Serializable] value, then
* @param pageFunction Function to be evaluated in the worker context * [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` * @param arg Optional argument to pass to `pageFunction`
*/ */
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>; evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): 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 * The only difference between
* in-page object (JSHandle). * [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 * If the function passed to the
* the promise to resolve and return its value. * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#workerevaluatehandlepagefunction-arg)
* @param pageFunction Function to be evaluated in the worker context * 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` * @param arg Optional argument to pass to `pageFunction`
*/ */
evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>; evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
@ -5178,7 +5191,7 @@ export interface Worker {
*/ */
export interface JSHandle<T = any> { 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`. * 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'); * 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` * @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, 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>; 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`. * 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 * 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 * 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. * for the promise to resolve and return its value.
@ -5212,7 +5225,7 @@ export interface JSHandle<T = any> {
* See * See
* [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg) * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#pageevaluatehandlepagefunction-arg)
* for more details. * 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` * @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>>; 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>[]>; $$(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 * 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 * 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 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` * @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>; $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>; $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 * 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. * 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 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` * @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>; $$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 selectors: Selectors;
export const devices: Devices & DeviceDescriptor[]; 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 // This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
export {}; export {};
@ -7979,6 +8165,80 @@ export interface Download {
url(): string; 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 * [FileChooser] objects are dispatched by the page in the
* [page.on('filechooser')](https://playwright.dev/docs/api/class-page#pageonfilechooser) event. * [page.on('filechooser')](https://playwright.dev/docs/api/class-page#pageonfilechooser) event.

View File

@ -44,8 +44,12 @@ class ApiParser {
md.visitAll(api, node => { md.visitAll(api, node => {
if (node.type === 'h1') if (node.type === 'h1')
this.parseClass(node); this.parseClass(node);
});
md.visitAll(api, node => {
if (node.type === 'h2') if (node.type === 'h2')
this.parseMember(node); this.parseMember(node);
});
md.visitAll(api, node => {
if (node.type === 'h3') if (node.type === 'h3')
this.parseArgument(node); this.parseArgument(node);
}); });
@ -131,6 +135,8 @@ class ApiParser {
arg.name = name; arg.name = name;
const existingArg = method.argsArray.find(m => m.name === arg.name); const existingArg = method.argsArray.find(m => m.name === arg.name);
if (existingArg) { 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) { for (const lang of arg.langs.only) {
existingArg.langs.overrides = existingArg.langs.overrides || {}; existingArg.langs.overrides = existingArg.langs.overrides || {};
existingArg.langs.overrides[lang] = arg; existingArg.langs.overrides[lang] = arg;

View File

@ -222,5 +222,13 @@ type AccessibilityNode = {
export const selectors: Selectors; export const selectors: Selectors;
export const devices: Devices & DeviceDescriptor[]; 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 // This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
export {}; export {};