From b1d963c24c6770ed96d4f0d232ec32d43e638043 Mon Sep 17 00:00:00 2001 From: Playwright Service <89237858+playwrightmachine@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:06:52 -0700 Subject: [PATCH 01/15] feat(webkit): roll to r2002 (#30505) Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- packages/playwright-core/browsers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index d63b4289d6..19256911a8 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -33,7 +33,7 @@ }, { "name": "webkit", - "revision": "2001", + "revision": "2002", "installByDefault": true, "revisionOverrides": { "mac10.14": "1446", From 8fc7723f2209d114811817922105d6e45d91e6ed Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 24 Apr 2024 09:25:43 -0700 Subject: [PATCH 02/15] fix(types): allow any return type from event handlers (#30492) Closes #29353. --- .eslintignore | 2 - docs/src/api/class-page.md | 4 +- packages/playwright-core/types/types.d.ts | 556 +++++++++++----------- tests/page/page-dialog.spec.ts | 4 +- utils/generate_types/index.js | 2 +- utils/generate_types/test/test.ts | 4 + 6 files changed, 281 insertions(+), 291 deletions(-) diff --git a/.eslintignore b/.eslintignore index 152cbbe228..60b8fd360f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,8 +7,6 @@ test/assets/modernizr.js /packages/playwright-core/types/* /packages/playwright-ct-core/src/generated/* /index.d.ts -utils/generate_types/overrides.d.ts -utils/generate_types/test/test.ts node_modules/ browser_patches/*/checkout/ browser_patches/chromium/output/ diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 072f024e17..3934270f07 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -290,9 +290,7 @@ Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` o **Usage** ```js -page.on('dialog', dialog => { - dialog.accept(); -}); +page.on('dialog', dialog => dialog.accept()); ``` ```java diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index fe42f0ae39..23eee3fe17 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -898,7 +898,7 @@ export interface Page { /** * Emitted when the page closes. */ - on(event: 'close', listener: (page: Page) => void): this; + on(event: 'close', listener: (page: Page) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -918,7 +918,7 @@ export interface Page { * ``` * */ - on(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + on(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page @@ -938,7 +938,7 @@ export interface Page { * ``` * */ - on(event: 'crash', listener: (page: Page) => void): this; + on(event: 'crash', listener: (page: Page) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -950,28 +950,26 @@ export interface Page { * **Usage** * * ```js - * page.on('dialog', dialog => { - * dialog.accept(); - * }); + * page.on('dialog', dialog => dialog.accept()); * ``` * * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - on(event: 'dialog', listener: (dialog: Dialog) => void): this; + on(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Emitted when the JavaScript * [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched. */ - on(event: 'domcontentloaded', listener: (page: Page) => void): this; + on(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the * passed {@link Download} instance. */ - on(event: 'download', listener: (download: Download) => void): this; + on(event: 'download', listener: (download: Download) => any): this; /** * Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can @@ -986,27 +984,27 @@ export interface Page { * ``` * */ - on(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + on(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Emitted when a frame is attached. */ - on(event: 'frameattached', listener: (frame: Frame) => void): this; + on(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is detached. */ - on(event: 'framedetached', listener: (frame: Frame) => void): this; + on(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is navigated to a new url. */ - on(event: 'framenavigated', listener: (frame: Frame) => void): this; + on(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched. */ - on(event: 'load', listener: (page: Page) => void): this; + on(event: 'load', listener: (page: Page) => any): this; /** * Emitted when an uncaught exception happens within the page. @@ -1022,7 +1020,7 @@ export interface Page { * ``` * */ - on(event: 'pageerror', listener: (error: Error) => void): this; + on(event: 'pageerror', listener: (error: Error) => any): this; /** * Emitted when the page opens a new tab or window. This event is emitted in addition to the @@ -1050,14 +1048,14 @@ export interface Page { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - on(event: 'popup', listener: (page: Page) => void): this; + on(event: 'popup', listener: (page: Page) => any): this; /** * Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, * see [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route) or * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route). */ - on(event: 'request', listener: (request: Request) => void): this; + on(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. @@ -1075,130 +1073,130 @@ export interface Page { * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. */ - on(event: 'requestfailed', listener: (request: Request) => void): this; + on(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the * sequence of events is `request`, `response` and `requestfinished`. */ - on(event: 'requestfinished', listener: (request: Request) => void): this; + on(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. */ - on(event: 'response', listener: (response: Response) => void): this; + on(event: 'response', listener: (response: Response) => any): this; /** * Emitted when {@link WebSocket} request is sent. */ - on(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + on(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned * by the page. */ - on(event: 'worker', listener: (worker: Worker) => void): this; + on(event: 'worker', listener: (worker: Worker) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: (page: Page) => void): this; + once(event: 'close', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + once(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'crash', listener: (page: Page) => void): this; + once(event: 'crash', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'dialog', listener: (dialog: Dialog) => void): this; + once(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'domcontentloaded', listener: (page: Page) => void): this; + once(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'download', listener: (download: Download) => void): this; + once(event: 'download', listener: (download: Download) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + once(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'frameattached', listener: (frame: Frame) => void): this; + once(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'framedetached', listener: (frame: Frame) => void): this; + once(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'framenavigated', listener: (frame: Frame) => void): this; + once(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'load', listener: (page: Page) => void): this; + once(event: 'load', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'pageerror', listener: (error: Error) => void): this; + once(event: 'pageerror', listener: (error: Error) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'popup', listener: (page: Page) => void): this; + once(event: 'popup', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'request', listener: (request: Request) => void): this; + once(event: 'request', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'requestfailed', listener: (request: Request) => void): this; + once(event: 'requestfailed', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'requestfinished', listener: (request: Request) => void): this; + once(event: 'requestfinished', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'response', listener: (response: Response) => void): this; + once(event: 'response', listener: (response: Response) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + once(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'worker', listener: (worker: Worker) => void): this; + once(event: 'worker', listener: (worker: Worker) => any): this; /** * Emitted when the page closes. */ - addListener(event: 'close', listener: (page: Page) => void): this; + addListener(event: 'close', listener: (page: Page) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -1218,7 +1216,7 @@ export interface Page { * ``` * */ - addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page @@ -1238,7 +1236,7 @@ export interface Page { * ``` * */ - addListener(event: 'crash', listener: (page: Page) => void): this; + addListener(event: 'crash', listener: (page: Page) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -1250,28 +1248,26 @@ export interface Page { * **Usage** * * ```js - * page.on('dialog', dialog => { - * dialog.accept(); - * }); + * page.on('dialog', dialog => dialog.accept()); * ``` * * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - addListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + addListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Emitted when the JavaScript * [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched. */ - addListener(event: 'domcontentloaded', listener: (page: Page) => void): this; + addListener(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the * passed {@link Download} instance. */ - addListener(event: 'download', listener: (download: Download) => void): this; + addListener(event: 'download', listener: (download: Download) => any): this; /** * Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can @@ -1286,27 +1282,27 @@ export interface Page { * ``` * */ - addListener(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + addListener(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Emitted when a frame is attached. */ - addListener(event: 'frameattached', listener: (frame: Frame) => void): this; + addListener(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is detached. */ - addListener(event: 'framedetached', listener: (frame: Frame) => void): this; + addListener(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is navigated to a new url. */ - addListener(event: 'framenavigated', listener: (frame: Frame) => void): this; + addListener(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched. */ - addListener(event: 'load', listener: (page: Page) => void): this; + addListener(event: 'load', listener: (page: Page) => any): this; /** * Emitted when an uncaught exception happens within the page. @@ -1322,7 +1318,7 @@ export interface Page { * ``` * */ - addListener(event: 'pageerror', listener: (error: Error) => void): this; + addListener(event: 'pageerror', listener: (error: Error) => any): this; /** * Emitted when the page opens a new tab or window. This event is emitted in addition to the @@ -1350,14 +1346,14 @@ export interface Page { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - addListener(event: 'popup', listener: (page: Page) => void): this; + addListener(event: 'popup', listener: (page: Page) => any): this; /** * Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, * see [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route) or * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route). */ - addListener(event: 'request', listener: (request: Request) => void): this; + addListener(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. @@ -1375,225 +1371,225 @@ export interface Page { * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. */ - addListener(event: 'requestfailed', listener: (request: Request) => void): this; + addListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the * sequence of events is `request`, `response` and `requestfinished`. */ - addListener(event: 'requestfinished', listener: (request: Request) => void): this; + addListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. */ - addListener(event: 'response', listener: (response: Response) => void): this; + addListener(event: 'response', listener: (response: Response) => any): this; /** * Emitted when {@link WebSocket} request is sent. */ - addListener(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + addListener(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned * by the page. */ - addListener(event: 'worker', listener: (worker: Worker) => void): this; + addListener(event: 'worker', listener: (worker: Worker) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: (page: Page) => void): this; + removeListener(event: 'close', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'crash', listener: (page: Page) => void): this; + removeListener(event: 'crash', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + removeListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'domcontentloaded', listener: (page: Page) => void): this; + removeListener(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'download', listener: (download: Download) => void): this; + removeListener(event: 'download', listener: (download: Download) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + removeListener(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'frameattached', listener: (frame: Frame) => void): this; + removeListener(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'framedetached', listener: (frame: Frame) => void): this; + removeListener(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'framenavigated', listener: (frame: Frame) => void): this; + removeListener(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'load', listener: (page: Page) => void): this; + removeListener(event: 'load', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'pageerror', listener: (error: Error) => void): this; + removeListener(event: 'pageerror', listener: (error: Error) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'popup', listener: (page: Page) => void): this; + removeListener(event: 'popup', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'request', listener: (request: Request) => void): this; + removeListener(event: 'request', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'requestfailed', listener: (request: Request) => void): this; + removeListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'requestfinished', listener: (request: Request) => void): this; + removeListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'response', listener: (response: Response) => void): this; + removeListener(event: 'response', listener: (response: Response) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + removeListener(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'worker', listener: (worker: Worker) => void): this; + removeListener(event: 'worker', listener: (worker: Worker) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: (page: Page) => void): this; + off(event: 'close', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + off(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'crash', listener: (page: Page) => void): this; + off(event: 'crash', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'dialog', listener: (dialog: Dialog) => void): this; + off(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'domcontentloaded', listener: (page: Page) => void): this; + off(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'download', listener: (download: Download) => void): this; + off(event: 'download', listener: (download: Download) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + off(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'frameattached', listener: (frame: Frame) => void): this; + off(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'framedetached', listener: (frame: Frame) => void): this; + off(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'framenavigated', listener: (frame: Frame) => void): this; + off(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'load', listener: (page: Page) => void): this; + off(event: 'load', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'pageerror', listener: (error: Error) => void): this; + off(event: 'pageerror', listener: (error: Error) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'popup', listener: (page: Page) => void): this; + off(event: 'popup', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'request', listener: (request: Request) => void): this; + off(event: 'request', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'requestfailed', listener: (request: Request) => void): this; + off(event: 'requestfailed', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'requestfinished', listener: (request: Request) => void): this; + off(event: 'requestfinished', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'response', listener: (response: Response) => void): this; + off(event: 'response', listener: (response: Response) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + off(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'worker', listener: (worker: Worker) => void): this; + off(event: 'worker', listener: (worker: Worker) => any): this; /** * Emitted when the page closes. */ - prependListener(event: 'close', listener: (page: Page) => void): this; + prependListener(event: 'close', listener: (page: Page) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -1613,7 +1609,7 @@ export interface Page { * ``` * */ - prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page @@ -1633,7 +1629,7 @@ export interface Page { * ``` * */ - prependListener(event: 'crash', listener: (page: Page) => void): this; + prependListener(event: 'crash', listener: (page: Page) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -1645,28 +1641,26 @@ export interface Page { * **Usage** * * ```js - * page.on('dialog', dialog => { - * dialog.accept(); - * }); + * page.on('dialog', dialog => dialog.accept()); * ``` * * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - prependListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + prependListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Emitted when the JavaScript * [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched. */ - prependListener(event: 'domcontentloaded', listener: (page: Page) => void): this; + prependListener(event: 'domcontentloaded', listener: (page: Page) => any): this; /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the * passed {@link Download} instance. */ - prependListener(event: 'download', listener: (download: Download) => void): this; + prependListener(event: 'download', listener: (download: Download) => any): this; /** * Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can @@ -1681,27 +1675,27 @@ export interface Page { * ``` * */ - prependListener(event: 'filechooser', listener: (fileChooser: FileChooser) => void): this; + prependListener(event: 'filechooser', listener: (fileChooser: FileChooser) => any): this; /** * Emitted when a frame is attached. */ - prependListener(event: 'frameattached', listener: (frame: Frame) => void): this; + prependListener(event: 'frameattached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is detached. */ - prependListener(event: 'framedetached', listener: (frame: Frame) => void): this; + prependListener(event: 'framedetached', listener: (frame: Frame) => any): this; /** * Emitted when a frame is navigated to a new url. */ - prependListener(event: 'framenavigated', listener: (frame: Frame) => void): this; + prependListener(event: 'framenavigated', listener: (frame: Frame) => any): this; /** * Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched. */ - prependListener(event: 'load', listener: (page: Page) => void): this; + prependListener(event: 'load', listener: (page: Page) => any): this; /** * Emitted when an uncaught exception happens within the page. @@ -1717,7 +1711,7 @@ export interface Page { * ``` * */ - prependListener(event: 'pageerror', listener: (error: Error) => void): this; + prependListener(event: 'pageerror', listener: (error: Error) => any): this; /** * Emitted when the page opens a new tab or window. This event is emitted in addition to the @@ -1745,14 +1739,14 @@ export interface Page { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - prependListener(event: 'popup', listener: (page: Page) => void): this; + prependListener(event: 'popup', listener: (page: Page) => any): this; /** * Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, * see [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route) or * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route). */ - prependListener(event: 'request', listener: (request: Request) => void): this; + prependListener(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. @@ -1770,30 +1764,30 @@ export interface Page { * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. */ - prependListener(event: 'requestfailed', listener: (request: Request) => void): this; + prependListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the * sequence of events is `request`, `response` and `requestfinished`. */ - prependListener(event: 'requestfinished', listener: (request: Request) => void): this; + prependListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. */ - prependListener(event: 'response', listener: (response: Response) => void): this; + prependListener(event: 'response', listener: (response: Response) => any): this; /** * Emitted when {@link WebSocket} request is sent. */ - prependListener(event: 'websocket', listener: (webSocket: WebSocket) => void): this; + prependListener(event: 'websocket', listener: (webSocket: WebSocket) => any): this; /** * Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned * by the page. */ - prependListener(event: 'worker', listener: (worker: Worker) => void): this; + prependListener(event: 'worker', listener: (worker: Worker) => any): this; /** * **NOTE** This method is experimental and its behavior may change in the upcoming releases. @@ -4443,9 +4437,7 @@ export interface Page { * **Usage** * * ```js - * page.on('dialog', dialog => { - * dialog.accept(); - * }); + * page.on('dialog', dialog => dialog.accept()); * ``` * * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or @@ -7716,7 +7708,7 @@ export interface BrowserContext { * ``` * */ - on(event: 'backgroundpage', listener: (page: Page) => void): this; + on(event: 'backgroundpage', listener: (page: Page) => any): this; /** * Emitted when Browser context gets closed. This might happen because of one of the following: @@ -7724,7 +7716,7 @@ export interface BrowserContext { * - Browser application is closed or crashed. * - The [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close) method was called. */ - on(event: 'close', listener: (browserContext: BrowserContext) => void): this; + on(event: 'close', listener: (browserContext: BrowserContext) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -7745,7 +7737,7 @@ export interface BrowserContext { * ``` * */ - on(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + on(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -7766,7 +7758,7 @@ export interface BrowserContext { * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - on(event: 'dialog', listener: (dialog: Dialog) => void): this; + on(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event @@ -7794,7 +7786,7 @@ export interface BrowserContext { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - on(event: 'page', listener: (page: Page) => void): this; + on(event: 'page', listener: (page: Page) => any): this; /** * Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To @@ -7805,7 +7797,7 @@ export interface BrowserContext { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * or [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route). */ - on(event: 'request', listener: (request: Request) => void): this; + on(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, @@ -7817,7 +7809,7 @@ export interface BrowserContext { * event and not with * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-failed). */ - on(event: 'requestfailed', listener: (request: Request) => void): this; + on(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the @@ -7825,82 +7817,82 @@ export interface BrowserContext { * particular page, use * [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#page-event-request-finished). */ - on(event: 'requestfinished', listener: (request: Request) => void): this; + on(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use * [page.on('response')](https://playwright.dev/docs/api/class-page#page-event-response). */ - on(event: 'response', listener: (response: Response) => void): this; + on(event: 'response', listener: (response: Response) => any): this; /** * **NOTE** Service workers are only supported on Chromium-based browsers. * * Emitted when new service worker is created in the context. */ - on(event: 'serviceworker', listener: (worker: Worker) => void): this; + on(event: 'serviceworker', listener: (worker: Worker) => any): this; /** * Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular * page, use [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error) instead. */ - on(event: 'weberror', listener: (webError: WebError) => void): this; + on(event: 'weberror', listener: (webError: WebError) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'backgroundpage', listener: (page: Page) => void): this; + once(event: 'backgroundpage', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: (browserContext: BrowserContext) => void): this; + once(event: 'close', listener: (browserContext: BrowserContext) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + once(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'dialog', listener: (dialog: Dialog) => void): this; + once(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'page', listener: (page: Page) => void): this; + once(event: 'page', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'request', listener: (request: Request) => void): this; + once(event: 'request', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'requestfailed', listener: (request: Request) => void): this; + once(event: 'requestfailed', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'requestfinished', listener: (request: Request) => void): this; + once(event: 'requestfinished', listener: (request: Request) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'response', listener: (response: Response) => void): this; + once(event: 'response', listener: (response: Response) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'serviceworker', listener: (worker: Worker) => void): this; + once(event: 'serviceworker', listener: (worker: Worker) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'weberror', listener: (webError: WebError) => void): this; + once(event: 'weberror', listener: (webError: WebError) => any): this; /** * **NOTE** Only works with Chromium browser's persistent context. @@ -7912,7 +7904,7 @@ export interface BrowserContext { * ``` * */ - addListener(event: 'backgroundpage', listener: (page: Page) => void): this; + addListener(event: 'backgroundpage', listener: (page: Page) => any): this; /** * Emitted when Browser context gets closed. This might happen because of one of the following: @@ -7920,7 +7912,7 @@ export interface BrowserContext { * - Browser application is closed or crashed. * - The [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close) method was called. */ - addListener(event: 'close', listener: (browserContext: BrowserContext) => void): this; + addListener(event: 'close', listener: (browserContext: BrowserContext) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -7941,7 +7933,7 @@ export interface BrowserContext { * ``` * */ - addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -7962,7 +7954,7 @@ export interface BrowserContext { * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - addListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + addListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event @@ -7990,7 +7982,7 @@ export interface BrowserContext { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - addListener(event: 'page', listener: (page: Page) => void): this; + addListener(event: 'page', listener: (page: Page) => any): this; /** * Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To @@ -8001,7 +7993,7 @@ export interface BrowserContext { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * or [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route). */ - addListener(event: 'request', listener: (request: Request) => void): this; + addListener(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, @@ -8013,7 +8005,7 @@ export interface BrowserContext { * event and not with * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-failed). */ - addListener(event: 'requestfailed', listener: (request: Request) => void): this; + addListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the @@ -8021,137 +8013,137 @@ export interface BrowserContext { * particular page, use * [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#page-event-request-finished). */ - addListener(event: 'requestfinished', listener: (request: Request) => void): this; + addListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use * [page.on('response')](https://playwright.dev/docs/api/class-page#page-event-response). */ - addListener(event: 'response', listener: (response: Response) => void): this; + addListener(event: 'response', listener: (response: Response) => any): this; /** * **NOTE** Service workers are only supported on Chromium-based browsers. * * Emitted when new service worker is created in the context. */ - addListener(event: 'serviceworker', listener: (worker: Worker) => void): this; + addListener(event: 'serviceworker', listener: (worker: Worker) => any): this; /** * Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular * page, use [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error) instead. */ - addListener(event: 'weberror', listener: (webError: WebError) => void): this; + addListener(event: 'weberror', listener: (webError: WebError) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'backgroundpage', listener: (page: Page) => void): this; + removeListener(event: 'backgroundpage', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: (browserContext: BrowserContext) => void): this; + removeListener(event: 'close', listener: (browserContext: BrowserContext) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + removeListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'page', listener: (page: Page) => void): this; + removeListener(event: 'page', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'request', listener: (request: Request) => void): this; + removeListener(event: 'request', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'requestfailed', listener: (request: Request) => void): this; + removeListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'requestfinished', listener: (request: Request) => void): this; + removeListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'response', listener: (response: Response) => void): this; + removeListener(event: 'response', listener: (response: Response) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'serviceworker', listener: (worker: Worker) => void): this; + removeListener(event: 'serviceworker', listener: (worker: Worker) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'weberror', listener: (webError: WebError) => void): this; + removeListener(event: 'weberror', listener: (webError: WebError) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'backgroundpage', listener: (page: Page) => void): this; + off(event: 'backgroundpage', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: (browserContext: BrowserContext) => void): this; + off(event: 'close', listener: (browserContext: BrowserContext) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + off(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'dialog', listener: (dialog: Dialog) => void): this; + off(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'page', listener: (page: Page) => void): this; + off(event: 'page', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'request', listener: (request: Request) => void): this; + off(event: 'request', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'requestfailed', listener: (request: Request) => void): this; + off(event: 'requestfailed', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'requestfinished', listener: (request: Request) => void): this; + off(event: 'requestfinished', listener: (request: Request) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'response', listener: (response: Response) => void): this; + off(event: 'response', listener: (response: Response) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'serviceworker', listener: (worker: Worker) => void): this; + off(event: 'serviceworker', listener: (worker: Worker) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'weberror', listener: (webError: WebError) => void): this; + off(event: 'weberror', listener: (webError: WebError) => any): this; /** * **NOTE** Only works with Chromium browser's persistent context. @@ -8163,7 +8155,7 @@ export interface BrowserContext { * ``` * */ - prependListener(event: 'backgroundpage', listener: (page: Page) => void): this; + prependListener(event: 'backgroundpage', listener: (page: Page) => any): this; /** * Emitted when Browser context gets closed. This might happen because of one of the following: @@ -8171,7 +8163,7 @@ export interface BrowserContext { * - Browser application is closed or crashed. * - The [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close) method was called. */ - prependListener(event: 'close', listener: (browserContext: BrowserContext) => void): this; + prependListener(event: 'close', listener: (browserContext: BrowserContext) => any): this; /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. @@ -8192,7 +8184,7 @@ export interface BrowserContext { * ``` * */ - prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** @@ -8213,7 +8205,7 @@ export interface BrowserContext { * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. */ - prependListener(event: 'dialog', listener: (dialog: Dialog) => void): this; + prependListener(event: 'dialog', listener: (dialog: Dialog) => any): this; /** * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event @@ -8241,7 +8233,7 @@ export interface BrowserContext { * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). */ - prependListener(event: 'page', listener: (page: Page) => void): this; + prependListener(event: 'page', listener: (page: Page) => any): this; /** * Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To @@ -8252,7 +8244,7 @@ export interface BrowserContext { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * or [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route). */ - prependListener(event: 'request', listener: (request: Request) => void): this; + prependListener(event: 'request', listener: (request: Request) => any): this; /** * Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, @@ -8264,7 +8256,7 @@ export interface BrowserContext { * event and not with * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-failed). */ - prependListener(event: 'requestfailed', listener: (request: Request) => void): this; + prependListener(event: 'requestfailed', listener: (request: Request) => any): this; /** * Emitted when a request finishes successfully after downloading the response body. For a successful response, the @@ -8272,27 +8264,27 @@ export interface BrowserContext { * particular page, use * [page.on('requestfinished')](https://playwright.dev/docs/api/class-page#page-event-request-finished). */ - prependListener(event: 'requestfinished', listener: (request: Request) => void): this; + prependListener(event: 'requestfinished', listener: (request: Request) => any): this; /** * Emitted when [response] status and headers are received for a request. For a successful response, the sequence of * events is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use * [page.on('response')](https://playwright.dev/docs/api/class-page#page-event-response). */ - prependListener(event: 'response', listener: (response: Response) => void): this; + prependListener(event: 'response', listener: (response: Response) => any): this; /** * **NOTE** Service workers are only supported on Chromium-based browsers. * * Emitted when new service worker is created in the context. */ - prependListener(event: 'serviceworker', listener: (worker: Worker) => void): this; + prependListener(event: 'serviceworker', listener: (worker: Worker) => any): this; /** * Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular * page, use [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error) instead. */ - prependListener(event: 'weberror', listener: (webError: WebError) => void): this; + prependListener(event: 'weberror', listener: (webError: WebError) => any): this; /** * Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies @@ -9054,34 +9046,34 @@ export interface Worker { * Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is * terminated. */ - on(event: 'close', listener: (worker: Worker) => void): this; + on(event: 'close', listener: (worker: Worker) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: (worker: Worker) => void): this; + once(event: 'close', listener: (worker: Worker) => any): this; /** * Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is * terminated. */ - addListener(event: 'close', listener: (worker: Worker) => void): this; + addListener(event: 'close', listener: (worker: Worker) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: (worker: Worker) => void): this; + removeListener(event: 'close', listener: (worker: Worker) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: (worker: Worker) => void): this; + off(event: 'close', listener: (worker: Worker) => any): this; /** * Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is * terminated. */ - prependListener(event: 'close', listener: (worker: Worker) => void): this; + prependListener(event: 'close', listener: (worker: Worker) => any): this; url(): string; } @@ -14060,7 +14052,7 @@ export interface ElectronApplication { /** * This event is issued when the application process has been terminated. */ - on(event: 'close', listener: () => void): this; + on(event: 'close', listener: () => any): this; /** * Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or @@ -14081,33 +14073,33 @@ export interface ElectronApplication { * ``` * */ - on(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + on(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * This event is issued for every window that is created **and loaded** in Electron. It contains a {@link Page} that * can be used for Playwright automation. */ - on(event: 'window', listener: (page: Page) => void): this; + on(event: 'window', listener: (page: Page) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: () => void): this; + once(event: 'close', listener: () => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + once(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'window', listener: (page: Page) => void): this; + once(event: 'window', listener: (page: Page) => any): this; /** * This event is issued when the application process has been terminated. */ - addListener(event: 'close', listener: () => void): this; + addListener(event: 'close', listener: () => any): this; /** * Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or @@ -14128,48 +14120,48 @@ export interface ElectronApplication { * ``` * */ - addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * This event is issued for every window that is created **and loaded** in Electron. It contains a {@link Page} that * can be used for Playwright automation. */ - addListener(event: 'window', listener: (page: Page) => void): this; + addListener(event: 'window', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: () => void): this; + removeListener(event: 'close', listener: () => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'window', listener: (page: Page) => void): this; + removeListener(event: 'window', listener: (page: Page) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: () => void): this; + off(event: 'close', listener: () => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + off(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'window', listener: (page: Page) => void): this; + off(event: 'window', listener: (page: Page) => any): this; /** * This event is issued when the application process has been terminated. */ - prependListener(event: 'close', listener: () => void): this; + prependListener(event: 'close', listener: () => any): this; /** * Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or @@ -14190,13 +14182,13 @@ export interface ElectronApplication { * ``` * */ - prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** * This event is issued for every window that is created **and loaded** in Electron. It contains a {@link Page} that * can be used for Playwright automation. */ - prependListener(event: 'window', listener: (page: Page) => void): this; + prependListener(event: 'window', listener: (page: Page) => any): this; /** * Returns the BrowserWindow object that corresponds to the given Playwright page. @@ -14599,62 +14591,62 @@ export interface AndroidDevice { /** * Emitted when the device connection gets closed. */ - on(event: 'close', listener: (androidDevice: AndroidDevice) => void): this; + on(event: 'close', listener: (androidDevice: AndroidDevice) => any): this; /** * Emitted when a new WebView instance is detected. */ - on(event: 'webview', listener: (androidWebView: AndroidWebView) => void): this; + on(event: 'webview', listener: (androidWebView: AndroidWebView) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: (androidDevice: AndroidDevice) => void): this; + once(event: 'close', listener: (androidDevice: AndroidDevice) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'webview', listener: (androidWebView: AndroidWebView) => void): this; + once(event: 'webview', listener: (androidWebView: AndroidWebView) => any): this; /** * Emitted when the device connection gets closed. */ - addListener(event: 'close', listener: (androidDevice: AndroidDevice) => void): this; + addListener(event: 'close', listener: (androidDevice: AndroidDevice) => any): this; /** * Emitted when a new WebView instance is detected. */ - addListener(event: 'webview', listener: (androidWebView: AndroidWebView) => void): this; + addListener(event: 'webview', listener: (androidWebView: AndroidWebView) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: (androidDevice: AndroidDevice) => void): this; + removeListener(event: 'close', listener: (androidDevice: AndroidDevice) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'webview', listener: (androidWebView: AndroidWebView) => void): this; + removeListener(event: 'webview', listener: (androidWebView: AndroidWebView) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: (androidDevice: AndroidDevice) => void): this; + off(event: 'close', listener: (androidDevice: AndroidDevice) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'webview', listener: (androidWebView: AndroidWebView) => void): this; + off(event: 'webview', listener: (androidWebView: AndroidWebView) => any): this; /** * Emitted when the device connection gets closed. */ - prependListener(event: 'close', listener: (androidDevice: AndroidDevice) => void): this; + prependListener(event: 'close', listener: (androidDevice: AndroidDevice) => any): this; /** * Emitted when a new WebView instance is detected. */ - prependListener(event: 'webview', listener: (androidWebView: AndroidWebView) => void): this; + prependListener(event: 'webview', listener: (androidWebView: AndroidWebView) => any): this; /** * Disconnects from the device. @@ -15406,62 +15398,62 @@ export interface AndroidSocket { /** * Emitted when the socket is closed. */ - on(event: 'close', listener: () => void): this; + on(event: 'close', listener: () => any): this; /** * Emitted when data is available to read from the socket. */ - on(event: 'data', listener: (buffer: Buffer) => void): this; + on(event: 'data', listener: (buffer: Buffer) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: () => void): this; + once(event: 'close', listener: () => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'data', listener: (buffer: Buffer) => void): this; + once(event: 'data', listener: (buffer: Buffer) => any): this; /** * Emitted when the socket is closed. */ - addListener(event: 'close', listener: () => void): this; + addListener(event: 'close', listener: () => any): this; /** * Emitted when data is available to read from the socket. */ - addListener(event: 'data', listener: (buffer: Buffer) => void): this; + addListener(event: 'data', listener: (buffer: Buffer) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: () => void): this; + removeListener(event: 'close', listener: () => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'data', listener: (buffer: Buffer) => void): this; + removeListener(event: 'data', listener: (buffer: Buffer) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: () => void): this; + off(event: 'close', listener: () => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'data', listener: (buffer: Buffer) => void): this; + off(event: 'data', listener: (buffer: Buffer) => any): this; /** * Emitted when the socket is closed. */ - prependListener(event: 'close', listener: () => void): this; + prependListener(event: 'close', listener: () => any): this; /** * Emitted when data is available to read from the socket. */ - prependListener(event: 'data', listener: (buffer: Buffer) => void): this; + prependListener(event: 'data', listener: (buffer: Buffer) => any): this; /** * Closes the socket. @@ -15485,32 +15477,32 @@ export interface AndroidWebView { /** * Emitted when the WebView is closed. */ - on(event: 'close', listener: () => void): this; + on(event: 'close', listener: () => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: () => void): this; + once(event: 'close', listener: () => any): this; /** * Emitted when the WebView is closed. */ - addListener(event: 'close', listener: () => void): this; + addListener(event: 'close', listener: () => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: () => void): this; + removeListener(event: 'close', listener: () => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: () => void): this; + off(event: 'close', listener: () => any): this; /** * Emitted when the WebView is closed. */ - prependListener(event: 'close', listener: () => void): this; + prependListener(event: 'close', listener: () => any): this; /** * Connects to the WebView and returns a regular Playwright {@link Page} to interact with. @@ -16514,12 +16506,12 @@ export interface Browser extends EventEmitter { * - Browser application is closed or crashed. * - The [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close) method was called. */ - on(event: 'disconnected', listener: (browser: Browser) => void): this; + on(event: 'disconnected', listener: (browser: Browser) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'disconnected', listener: (browser: Browser) => void): this; + once(event: 'disconnected', listener: (browser: Browser) => any): this; /** * Emitted when Browser gets disconnected from the browser application. This might happen because of one of the @@ -16527,17 +16519,17 @@ export interface Browser extends EventEmitter { * - Browser application is closed or crashed. * - The [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close) method was called. */ - addListener(event: 'disconnected', listener: (browser: Browser) => void): this; + addListener(event: 'disconnected', listener: (browser: Browser) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'disconnected', listener: (browser: Browser) => void): this; + removeListener(event: 'disconnected', listener: (browser: Browser) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'disconnected', listener: (browser: Browser) => void): this; + off(event: 'disconnected', listener: (browser: Browser) => any): this; /** * Emitted when Browser gets disconnected from the browser application. This might happen because of one of the @@ -16545,7 +16537,7 @@ export interface Browser extends EventEmitter { * - Browser application is closed or crashed. * - The [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close) method was called. */ - prependListener(event: 'disconnected', listener: (browser: Browser) => void): this; + prependListener(event: 'disconnected', listener: (browser: Browser) => any): this; /** * Get the browser type (chromium, firefox or webkit) that the browser belongs to. @@ -17089,32 +17081,32 @@ export interface BrowserServer { /** * Emitted when the browser server closes. */ - on(event: 'close', listener: () => void): this; + on(event: 'close', listener: () => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: () => void): this; + once(event: 'close', listener: () => any): this; /** * Emitted when the browser server closes. */ - addListener(event: 'close', listener: () => void): this; + addListener(event: 'close', listener: () => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: () => void): this; + removeListener(event: 'close', listener: () => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: () => void): this; + off(event: 'close', listener: () => any): this; /** * Emitted when the browser server closes. */ - prependListener(event: 'close', listener: () => void): this; + prependListener(event: 'close', listener: () => any): this; /** * Closes the browser gracefully and makes sure the process is terminated. @@ -19671,7 +19663,7 @@ export interface WebSocket { /** * Fired when the websocket closes. */ - on(event: 'close', listener: (webSocket: WebSocket) => void): this; + on(event: 'close', listener: (webSocket: WebSocket) => any): this; /** * Fired when the websocket receives a frame. @@ -19681,7 +19673,7 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Fired when the websocket sends a frame. @@ -19691,17 +19683,17 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Fired when the websocket has an error. */ - on(event: 'socketerror', listener: (string: string) => void): this; + on(event: 'socketerror', listener: (string: string) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'close', listener: (webSocket: WebSocket) => void): this; + once(event: 'close', listener: (webSocket: WebSocket) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. @@ -19711,7 +19703,7 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. @@ -19721,17 +19713,17 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'socketerror', listener: (string: string) => void): this; + once(event: 'socketerror', listener: (string: string) => any): this; /** * Fired when the websocket closes. */ - addListener(event: 'close', listener: (webSocket: WebSocket) => void): this; + addListener(event: 'close', listener: (webSocket: WebSocket) => any): this; /** * Fired when the websocket receives a frame. @@ -19741,7 +19733,7 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Fired when the websocket sends a frame. @@ -19751,17 +19743,17 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Fired when the websocket has an error. */ - addListener(event: 'socketerror', listener: (string: string) => void): this; + addListener(event: 'socketerror', listener: (string: string) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'close', listener: (webSocket: WebSocket) => void): this; + removeListener(event: 'close', listener: (webSocket: WebSocket) => any): this; /** * Removes an event listener added by `on` or `addListener`. @@ -19771,7 +19763,7 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. @@ -19781,17 +19773,17 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'socketerror', listener: (string: string) => void): this; + removeListener(event: 'socketerror', listener: (string: string) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'close', listener: (webSocket: WebSocket) => void): this; + off(event: 'close', listener: (webSocket: WebSocket) => any): this; /** * Removes an event listener added by `on` or `addListener`. @@ -19801,7 +19793,7 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. @@ -19811,17 +19803,17 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'socketerror', listener: (string: string) => void): this; + off(event: 'socketerror', listener: (string: string) => any): this; /** * Fired when the websocket closes. */ - prependListener(event: 'close', listener: (webSocket: WebSocket) => void): this; + prependListener(event: 'close', listener: (webSocket: WebSocket) => any): this; /** * Fired when the websocket receives a frame. @@ -19831,7 +19823,7 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Fired when the websocket sends a frame. @@ -19841,12 +19833,12 @@ export interface WebSocket { * frame payload */ payload: string|Buffer; - }) => void): this; + }) => any): this; /** * Fired when the websocket has an error. */ - prependListener(event: 'socketerror', listener: (string: string) => void): this; + prependListener(event: 'socketerror', listener: (string: string) => any): this; /** * Indicates that the web socket has been closed. diff --git a/tests/page/page-dialog.spec.ts b/tests/page/page-dialog.spec.ts index ca7c0d45ae..fdaed252b8 100644 --- a/tests/page/page-dialog.spec.ts +++ b/tests/page/page-dialog.spec.ts @@ -43,9 +43,7 @@ it('should allow accepting prompts @smoke', async ({ page, isElectron }) => { it('should dismiss the prompt', async ({ page, isElectron }) => { it.skip(isElectron, 'prompt() is not a thing in electron'); - page.on('dialog', dialog => { - void dialog.dismiss(); - }); + page.on('dialog', dialog => dialog.dismiss()); const result = await page.evaluate(() => prompt('question?')); expect(result).toBe(null); }); diff --git a/utils/generate_types/index.js b/utils/generate_types/index.js index 24966b1704..e6b549c173 100644 --- a/utils/generate_types/index.js +++ b/utils/generate_types/index.js @@ -283,7 +283,7 @@ class TypesGenerator { parts.push(this.writeComment(comment, indent)); else parts.push(this.writeComment(commentForMethod[method], indent)); - parts.push(` ${method}(event: '${eventName}', listener: (${params}) => void): this;\n`); + parts.push(` ${method}(event: '${eventName}', listener: (${params}) => any): this;\n`); } } diff --git a/utils/generate_types/test/test.ts b/utils/generate_types/test/test.ts index e13c2ac43c..e4b090d8d2 100644 --- a/utils/generate_types/test/test.ts +++ b/utils/generate_types/test/test.ts @@ -921,6 +921,10 @@ playwright.chromium.launch().then(async browser => { .removeListener('close', listener) .off('close', listener); } + { + const page: playwright.Page = {} as any; + page.on('dialog', dialog => dialog.accept()); + } }); // waitForResponse callback predicate From 7a9fd27146b34042051a399ab02a1cb8f1a4b948 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 24 Apr 2024 12:27:47 -0700 Subject: [PATCH 03/15] chore: never watch node_modules (#30514) --- packages/playwright/src/fsWatcher.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/playwright/src/fsWatcher.ts b/packages/playwright/src/fsWatcher.ts index 749c11ab0b..c0d4e4d04e 100644 --- a/packages/playwright/src/fsWatcher.ts +++ b/packages/playwright/src/fsWatcher.ts @@ -16,6 +16,7 @@ import { chokidar } from './utilsBundle'; import type { FSWatcher } from 'chokidar'; +import path from 'path'; export type FSEvent = { event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', file: string }; @@ -51,7 +52,8 @@ export class Watcher { if (!this._watchedFiles.length) return; - this._fsWatcher = chokidar.watch(watchedFiles, { ignoreInitial: true, ignored: this._ignoredFolders }).on('all', async (event, file) => { + const ignored = [...this._ignoredFolders, (name: string) => name.includes(path.sep + 'node_modules' + path.sep)]; + this._fsWatcher = chokidar.watch(watchedFiles, { ignoreInitial: true, ignored }).on('all', async (event, file) => { if (this._throttleTimer) clearTimeout(this._throttleTimer); if (this._mode === 'flat' && event !== 'add' && event !== 'change') From e91d372544b4509623250e53d63e86ee830c5dbf Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 24 Apr 2024 12:32:28 -0700 Subject: [PATCH 04/15] fix(ct): allow importing components from node_modules (#30493) Fixes https://github.com/microsoft/playwright/issues/29854 --- packages/playwright-ct-core/src/viteUtils.ts | 2 +- .../playwright.ct-build.spec.ts | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/playwright-ct-core/src/viteUtils.ts b/packages/playwright-ct-core/src/viteUtils.ts index 505301c48e..a9f7020999 100644 --- a/packages/playwright-ct-core/src/viteUtils.ts +++ b/packages/playwright-ct-core/src/viteUtils.ts @@ -183,7 +183,7 @@ export function transformIndexFile(id: string, content: string, templateDir: str lines.push(registerSource); for (const value of importInfos.values()) { - const importPath = resolveHook(value.filename, value.importSource); + const importPath = resolveHook(value.filename, value.importSource) || value.importSource; lines.push(`const ${value.id} = () => import('${importPath?.replaceAll(path.sep, '/')}').then((mod) => mod.${value.remoteName || 'default'});`); } diff --git a/tests/playwright-test/playwright.ct-build.spec.ts b/tests/playwright-test/playwright.ct-build.spec.ts index 0f8b1bdff6..a2a021b642 100644 --- a/tests/playwright-test/playwright.ct-build.spec.ts +++ b/tests/playwright-test/playwright.ct-build.spec.ts @@ -665,3 +665,33 @@ test('should pass undefined value as param', async ({ runInlineTest }) => { expect(result.exitCode).toBe(0); expect(result.passed).toBe(1); }); + +test('should resolve components imported from node_modules', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'package.json': `{ "name": "test-project" }`, + 'playwright.config.ts': playwrightCtConfigText, + 'playwright/index.html': ``, + 'playwright/index.js': ``, + + 'node_modules/@mui/material/index.js': ` + const TextField = () => 'input'; + module.exports = { TextField }; + `, + 'node_modules/@mui/material/package.json': JSON.stringify({ + name: '@mui/material', + main: './index.js', + }), + + 'src/component.spec.tsx': ` + import { test } from '@playwright/experimental-ct-react'; + import { TextField } from '@mui/material'; + + test("passes", async ({ mount }) => { + await mount(); + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); +}); From 9914b35c96fcac1315d380e525de9e628dd29032 Mon Sep 17 00:00:00 2001 From: Playwright Service <89237858+playwrightmachine@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:43:26 -0700 Subject: [PATCH 05/15] feat(firefox-beta): roll to r1449 (#30510) Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- packages/playwright-core/browsers.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 19256911a8..ec1ac84c6a 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -27,9 +27,9 @@ }, { "name": "firefox-beta", - "revision": "1447", + "revision": "1449", "installByDefault": false, - "browserVersion": "125.0b3" + "browserVersion": "126.0b1" }, { "name": "webkit", From 4e086e6df84df157a939689e85dc24249aeceea7 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 24 Apr 2024 13:10:17 -0700 Subject: [PATCH 06/15] docs: update `type` property description (#30516) --- docs/src/test-reporter-api/class-suite.md | 2 +- docs/src/test-reporter-api/class-testcase.md | 2 +- packages/playwright/types/testReporter.d.ts | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/src/test-reporter-api/class-suite.md b/docs/src/test-reporter-api/class-suite.md index f9661dc3fd..1d458c842b 100644 --- a/docs/src/test-reporter-api/class-suite.md +++ b/docs/src/test-reporter-api/class-suite.md @@ -30,7 +30,7 @@ Returns the list of all test cases in this suite and its descendants, as opposit * since: v1.44 - type: <[Array]<[TestCase]|[Suite]>> -Test cases and suites defined directly in this suite. The elements are returned in their declaration order. You can discriminate between different entry types using [`property: TestCase.type`] and [`property: Suite.type`]. +Test cases and suites defined directly in this suite. The elements are returned in their declaration order. You can differentiate between various entry types by using [`property: TestCase.type`] and [`property: Suite.type`]. ## property: Suite.location * since: v1.10 diff --git a/docs/src/test-reporter-api/class-testcase.md b/docs/src/test-reporter-api/class-testcase.md index a601b284cd..82ab6ca521 100644 --- a/docs/src/test-reporter-api/class-testcase.md +++ b/docs/src/test-reporter-api/class-testcase.md @@ -112,4 +112,4 @@ Returns a list of titles from the root down to this test. * since: v1.44 - returns: <[TestCaseType]<"test">> -Returns type of the test. +Returns "test". Useful for detecting test cases in [`method: Suite.entries`]. diff --git a/packages/playwright/types/testReporter.d.ts b/packages/playwright/types/testReporter.d.ts index 2b61ffaeea..694ba65961 100644 --- a/packages/playwright/types/testReporter.d.ts +++ b/packages/playwright/types/testReporter.d.ts @@ -578,7 +578,7 @@ export interface Suite { /** * Test cases and suites defined directly in this suite. The elements are returned in their declaration order. You can - * discriminate between different entry types using + * differentiate between various entry types by using * [testCase.type](https://playwright.dev/docs/api/class-testcase#test-case-type) and * [suite.type](https://playwright.dev/docs/api/class-suite#suite-type). */ @@ -770,7 +770,8 @@ export interface TestCase { title: string; /** - * Returns type of the test. + * Returns "test". Useful for detecting test cases in + * [suite.entries()](https://playwright.dev/docs/api/class-suite#suite-entries). */ type: "test"; } From d5ade91a922da931f450a443bf53ac136726ca21 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Wed, 24 Apr 2024 22:23:28 +0200 Subject: [PATCH 07/15] devops: change flakiness-dashboard auth to be secret-less (#30488) --- .gitignore | 1 + utils/flakiness-dashboard/README.md | 4 +- utils/flakiness-dashboard/host.json | 2 +- utils/flakiness-dashboard/package-lock.json | 845 +++++++++++++++++- utils/flakiness-dashboard/package.json | 7 +- .../flakiness-dashboard/processing/host.json | 13 - utils/flakiness-dashboard/processing/utils.js | 12 +- 7 files changed, 831 insertions(+), 53 deletions(-) delete mode 100644 utils/flakiness-dashboard/processing/host.json diff --git a/.gitignore b/.gitignore index 8902876690..69d85e4975 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ node_modules/ *.swp *.pyc .vscode +.mono .idea yarn.lock /packages/playwright-core/src/generated diff --git a/utils/flakiness-dashboard/README.md b/utils/flakiness-dashboard/README.md index 0c3b84f527..7ea2c92a7f 100644 --- a/utils/flakiness-dashboard/README.md +++ b/utils/flakiness-dashboard/README.md @@ -14,11 +14,11 @@ Azure Functions Core Tools is not available on macOS M1 yet, so we use GitHub Co curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list' - apt-get update && apt-get install azure-functions-core-tools-4 + apt-get update && apt-get install azure-functions-core-tools-4 sudo ``` - Install Azure CLI: ```bash - curl -sL https://aka.ms/InstallAzureCLIDeb | bash + curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash ``` - Login to Azure: ```bash diff --git a/utils/flakiness-dashboard/host.json b/utils/flakiness-dashboard/host.json index 5c997fdf49..6432c26df3 100644 --- a/utils/flakiness-dashboard/host.json +++ b/utils/flakiness-dashboard/host.json @@ -16,6 +16,6 @@ }, "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[2.*, 3.0.0)" + "version": "[4.*, 5.0.0)" } } diff --git a/utils/flakiness-dashboard/package-lock.json b/utils/flakiness-dashboard/package-lock.json index 483798e826..43efb92951 100644 --- a/utils/flakiness-dashboard/package-lock.json +++ b/utils/flakiness-dashboard/package-lock.json @@ -6,7 +6,8 @@ "": { "name": "flakiness-dashboard", "dependencies": { - "@azure/storage-blob": "^12.16.0" + "@azure/identity": "^4.1.0", + "@azure/storage-blob": "^12.17.0" } }, "node_modules/@azure/abort-controller": { @@ -26,15 +27,66 @@ "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" }, "node_modules/@azure/core-auth": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", - "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz", + "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client/node_modules/@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@azure/core-http": { @@ -86,6 +138,46 @@ "node": ">=8.0.0" } }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.15.2.tgz", + "integrity": "sha512-BmWfpjc/QXc2ipHOh6LbUzp3ONCaa6xzIssTU0DwH9bbYNXJlGUL6tujx5TrbVd/QQknmS+vlQJGrCq2oL1gZA==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline/node_modules/@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@azure/core-tracing": { "version": "1.0.0-preview.13", "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", @@ -99,15 +191,61 @@ } }, "node_modules/@azure/core-util": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.0.tgz", - "integrity": "sha512-ANP0Er7R2KHHHjwmKzPF9wbd0gXvOX7yRRHeYL1eNd/OaNrMLyfZH/FQasHRVAf6rMXX+EAUpvYwLMFDHDI5Gw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.0.tgz", + "integrity": "sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.1.0.tgz", + "integrity": "sha512-BhYkF8Xr2gXjyDxocm0pc9RI5J5a1jw8iW0dw6Bx95OGdYbuMyFZrrwNw4eYSqQ2BB6FZOqpJP3vjsAqRcvDhw==", "dependencies": { "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.5.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^3.11.1", + "@azure/msal-node": "^2.6.6", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@azure/logger": { @@ -121,10 +259,42 @@ "node": ">=14.0.0" } }, + "node_modules/@azure/msal-browser": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.13.0.tgz", + "integrity": "sha512-fD906nmJei3yE7la6DZTdUtXKvpwzJURkfsiz9747Icv4pit77cegSm6prJTKLQ1fw4iiZzrrWwxnhMLrTf5gQ==", + "dependencies": { + "@azure/msal-common": "14.9.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "14.9.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.9.0.tgz", + "integrity": "sha512-yzBPRlWPnTBeixxLNI3BBIgF5/bHpbhoRVuuDBnYjCyWRavaPUsKAHUDYLqpGkBLDciA6TCc6GOxN4/S3WiSxg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.7.0.tgz", + "integrity": "sha512-wXD8LkUvHICeSWZydqg6o8Yvv+grlBEcmLGu+QEI4FcwFendbTEZrlSygnAXXSOCVaGAirWLchca35qrgpO6Jw==", + "dependencies": { + "@azure/msal-common": "14.9.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@azure/storage-blob": { - "version": "12.16.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.16.0.tgz", - "integrity": "sha512-jz33rUSUGUB65FgYrTRgRDjG6hdPHwfvHe+g/UrwVG8MsyLqSxg9TaW7Yuhjxu1v1OZ5xam2NU6+IpCN0xJO8Q==", + "version": "12.17.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz", + "integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-http": "^3.0.0", @@ -182,11 +352,27 @@ "@types/node": "*" } }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -198,6 +384,30 @@ "node": ">= 0.8" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -206,6 +416,14 @@ "node": ">=0.4.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -227,6 +445,160 @@ "node": ">= 6" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -246,6 +618,11 @@ "node": ">= 0.6" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -265,6 +642,22 @@ } } }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -273,20 +666,62 @@ "node": ">= 0.6.0" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tunnel": { "version": "0.0.6", @@ -337,6 +772,11 @@ "engines": { "node": ">=4.0" } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } }, "dependencies": { @@ -354,12 +794,55 @@ "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" }, "@azure/core-auth": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", - "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz", + "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==", "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "requires": { + "tslib": "^2.6.2" + } + } + } + }, + "@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "requires": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "requires": { + "tslib": "^2.6.2" + } + } } }, "@azure/core-http": { @@ -402,6 +885,39 @@ "@azure/core-asynciterator-polyfill": "^1.0.0" } }, + "@azure/core-rest-pipeline": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.15.2.tgz", + "integrity": "sha512-BmWfpjc/QXc2ipHOh6LbUzp3ONCaa6xzIssTU0DwH9bbYNXJlGUL6tujx5TrbVd/QQknmS+vlQJGrCq2oL1gZA==", + "requires": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "requires": { + "tslib": "^2.6.2" + } + }, + "@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "requires": { + "tslib": "^2.6.2" + } + } + } + }, "@azure/core-tracing": { "version": "1.0.0-preview.13", "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", @@ -412,12 +928,53 @@ } }, "@azure/core-util": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.0.tgz", - "integrity": "sha512-ANP0Er7R2KHHHjwmKzPF9wbd0gXvOX7yRRHeYL1eNd/OaNrMLyfZH/FQasHRVAf6rMXX+EAUpvYwLMFDHDI5Gw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.0.tgz", + "integrity": "sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw==", + "requires": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "requires": { + "tslib": "^2.6.2" + } + } + } + }, + "@azure/identity": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.1.0.tgz", + "integrity": "sha512-BhYkF8Xr2gXjyDxocm0pc9RI5J5a1jw8iW0dw6Bx95OGdYbuMyFZrrwNw4eYSqQ2BB6FZOqpJP3vjsAqRcvDhw==", "requires": { "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.5.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^3.11.1", + "@azure/msal-node": "^2.6.6", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", "tslib": "^2.2.0" + }, + "dependencies": { + "@azure/core-tracing": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", + "requires": { + "tslib": "^2.6.2" + } + } } }, "@azure/logger": { @@ -428,10 +985,33 @@ "tslib": "^2.2.0" } }, + "@azure/msal-browser": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.13.0.tgz", + "integrity": "sha512-fD906nmJei3yE7la6DZTdUtXKvpwzJURkfsiz9747Icv4pit77cegSm6prJTKLQ1fw4iiZzrrWwxnhMLrTf5gQ==", + "requires": { + "@azure/msal-common": "14.9.0" + } + }, + "@azure/msal-common": { + "version": "14.9.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.9.0.tgz", + "integrity": "sha512-yzBPRlWPnTBeixxLNI3BBIgF5/bHpbhoRVuuDBnYjCyWRavaPUsKAHUDYLqpGkBLDciA6TCc6GOxN4/S3WiSxg==" + }, + "@azure/msal-node": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.7.0.tgz", + "integrity": "sha512-wXD8LkUvHICeSWZydqg6o8Yvv+grlBEcmLGu+QEI4FcwFendbTEZrlSygnAXXSOCVaGAirWLchca35qrgpO6Jw==", + "requires": { + "@azure/msal-common": "14.9.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + } + }, "@azure/storage-blob": { - "version": "12.16.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.16.0.tgz", - "integrity": "sha512-jz33rUSUGUB65FgYrTRgRDjG6hdPHwfvHe+g/UrwVG8MsyLqSxg9TaW7Yuhjxu1v1OZ5xam2NU6+IpCN0xJO8Q==", + "version": "12.17.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz", + "integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==", "requires": { "@azure/abort-controller": "^1.0.0", "@azure/core-http": "^3.0.0", @@ -482,11 +1062,24 @@ "@types/node": "*" } }, + "agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "requires": { + "debug": "^4.3.4" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -495,11 +1088,32 @@ "delayed-stream": "~1.0.0" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -515,6 +1129,137 @@ "mime-types": "^2.1.12" } }, + "http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "dependencies": { + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + } + } + }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -528,6 +1273,11 @@ "mime-db": "1.52.0" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -536,25 +1286,53 @@ "whatwg-url": "^5.0.0" } }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==" + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "tunnel": { "version": "0.0.6", @@ -593,6 +1371,11 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/utils/flakiness-dashboard/package.json b/utils/flakiness-dashboard/package.json index 3e13565339..d8844674b9 100644 --- a/utils/flakiness-dashboard/package.json +++ b/utils/flakiness-dashboard/package.json @@ -2,11 +2,10 @@ "name": "flakiness-dashboard", "version": "", "description": "", - "scripts": { - "test": "echo \"No tests yet...\"" - }, + "main": "processing/index.js", "author": "", "dependencies": { - "@azure/storage-blob": "^12.16.0" + "@azure/identity": "^4.1.0", + "@azure/storage-blob": "^12.17.0" } } diff --git a/utils/flakiness-dashboard/processing/host.json b/utils/flakiness-dashboard/processing/host.json deleted file mode 100644 index 7a24f01b78..0000000000 --- a/utils/flakiness-dashboard/processing/host.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "2.0", - "extensions": { - "queues": { - "batchSize": 1, - "newBatchThreshold": 0 - } - }, - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[1.*, 2.0.0)" - } -} diff --git a/utils/flakiness-dashboard/processing/utils.js b/utils/flakiness-dashboard/processing/utils.js index 5bc6888a8f..a0f166f7e5 100644 --- a/utils/flakiness-dashboard/processing/utils.js +++ b/utils/flakiness-dashboard/processing/utils.js @@ -13,14 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -const { BlobServiceClient } = require("@azure/storage-blob"); +// @ts-check +const { DefaultAzureCredential } = require('@azure/identity'); +const { BlobServiceClient } = require('@azure/storage-blob'); +const defaultAzureCredential = new DefaultAzureCredential(); const zlib = require('zlib'); const util = require('util'); const gzipAsync = util.promisify(zlib.gzip); const gunzipAsync = util.promisify(zlib.gunzip); -const blobServiceClient = BlobServiceClient.fromConnectionString(process.env.AzureWebJobsStorage); +const AZURE_STORAGE_ACCOUNT = 'folioflakinessdashboard'; + +const blobServiceClient = new BlobServiceClient( + `https://${AZURE_STORAGE_ACCOUNT}.blob.core.windows.net`, + defaultAzureCredential +); function flattenSpecs(suite, result = [], titlePaths = []) { if (suite.suites) { From c9e4c49c2f81055539a80444fb1680c6c8db7e1f Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Wed, 24 Apr 2024 22:26:31 +0200 Subject: [PATCH 08/15] devops: remove redundant files (#30521) --- .github/actions/download-artifact/action.yml | 2 +- .../action.yml | 26 ------------------- .../playwright-chromium/package.json | 7 ----- .../playwright-core/package.json | 7 ----- .../playwright-firefox/package.json | 7 ----- .../playwright-test/package.json | 7 ----- .../playwright-webkit/package.json | 7 ----- .../playwright/package.json | 7 ----- 8 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 .github/actions/download-blob-report-from-azure/action.yml delete mode 100644 .github/dummy-package-files-for-dependents-analytics/playwright-chromium/package.json delete mode 100644 .github/dummy-package-files-for-dependents-analytics/playwright-core/package.json delete mode 100644 .github/dummy-package-files-for-dependents-analytics/playwright-firefox/package.json delete mode 100644 .github/dummy-package-files-for-dependents-analytics/playwright-test/package.json delete mode 100644 .github/dummy-package-files-for-dependents-analytics/playwright-webkit/package.json delete mode 100644 .github/dummy-package-files-for-dependents-analytics/playwright/package.json diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml index 3ab38a4c17..d5dd4bea89 100644 --- a/.github/actions/download-artifact/action.yml +++ b/.github/actions/download-artifact/action.yml @@ -34,7 +34,7 @@ runs: artifact_id: artifact.id, archive_format: 'zip' }); - console.log('downloaded artifact', result); + console.log(`Downloaded ${artifact.name}.zip (${result.data.byteLength} bytes)`); fs.writeFileSync(`${{ inputs.path }}/artifacts/${artifact.name}.zip`, Buffer.from(result.data)); } - name: Unzip artifacts diff --git a/.github/actions/download-blob-report-from-azure/action.yml b/.github/actions/download-blob-report-from-azure/action.yml deleted file mode 100644 index 848b98cfbc..0000000000 --- a/.github/actions/download-blob-report-from-azure/action.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: 'Download blob report from Azure' -description: 'Download blob report from Azure blob storage' -inputs: - blob_prefix: - description: 'Name of the Azure blob storage directory containing blob report' - required: true - output_dir: - description: 'Output directory where downloaded blobs will be stored' - required: true - default: 'blob-report' - connection_string: - description: 'Azure connection string' - required: true -runs: - using: "composite" - steps: - - name: Download Blob Reports from Azure Blob Storage - shell: bash - run: | - OUTPUT_DIR='${{ inputs.output_dir }}' - mkdir -p $OUTPUT_DIR - LIST=$(az storage blob list -c '$web' --prefix ${{ inputs.blob_prefix }} --connection-string "${{ inputs.connection_string }}") - for name in $(echo $LIST | jq --raw-output '.[].name | select(test("report-.*\\.zip$"))'); - do - az storage blob download -c '$web' --name $name -f $OUTPUT_DIR/$(basename $name) --connection-string "${{ inputs.connection_string }}" - done diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-chromium/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-chromium/package.json deleted file mode 100644 index ec0de7ab12..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-chromium/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright-chromium", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-core/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-core/package.json deleted file mode 100644 index 7a76fc0b9f..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-core/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright-core", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-firefox/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-firefox/package.json deleted file mode 100644 index da7b955fc8..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-firefox/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright-firefox", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-test/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-test/package.json deleted file mode 100644 index e3cef4a193..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-test/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@playwright/test", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright-webkit/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright-webkit/package.json deleted file mode 100644 index 6d0f7491c8..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright-webkit/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright-webkit", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/.github/dummy-package-files-for-dependents-analytics/playwright/package.json b/.github/dummy-package-files-for-dependents-analytics/playwright/package.json deleted file mode 100644 index 4515e5a1e9..0000000000 --- a/.github/dummy-package-files-for-dependents-analytics/playwright/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "playwright", - "version": "1.0.0", - "description": "A high-level API to automate web browsers", - "repository": "github:Microsoft/playwright", - "license": "Apache-2.0" -} \ No newline at end of file From 1b7e097b68cd76eb1a57a947305de8c59f36d85f Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Wed, 24 Apr 2024 22:40:38 +0200 Subject: [PATCH 09/15] docs(python): add docs for new_context fixture (#30522) https://github.com/microsoft/playwright-internal/issues/202 --- docs/src/test-runners-python.md | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/docs/src/test-runners-python.md b/docs/src/test-runners-python.md index 0e3ffde0dd..0ad4043066 100644 --- a/docs/src/test-runners-python.md +++ b/docs/src/test-runners-python.md @@ -55,6 +55,7 @@ def test_my_app_is_working(fixture_name): - `context`: New [browser context](./browser-contexts) for a test. - `page`: New [browser page](./pages) for a test. +- `new_context`: Allows creating different [browser contexts](./browser-contexts) for a test. Useful for multi-user scenarios. Accepts the same parameters as [`method: Browser.newContext`]. **Session scope**: These fixtures are created when requested in a test function and destroyed when all tests end. @@ -211,30 +212,6 @@ def browser_context_args(browser_context_args, playwright): Or via the CLI `--device="iPhone 11 Pro"` -### Persistent context - -```py title="conftest.py" -import pytest -from playwright.sync_api import BrowserType -from typing import Dict - -@pytest.fixture(scope="session") -def context( - browser_type: BrowserType, - browser_type_launch_args: Dict, - browser_context_args: Dict -): - context = browser_type.launch_persistent_context("./foobar", **{ - **browser_type_launch_args, - **browser_context_args, - "locale": "de-DE", - }) - yield context - context.close() -``` - -When using that all pages inside your test are created from the persistent context. - ### Using with `unittest.TestCase` See the following example for using it with `unittest.TestCase`. This has a limitation, From 6fecf5201674d456dc2a079305bcce303c2cfbea Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 24 Apr 2024 14:05:39 -0700 Subject: [PATCH 10/15] test: adjust headers expectations for firefox-beta (#30520) There is a new experimental feature enabled in firefox-beta that sends a "Priority" header. --- tests/page/page-network-request.spec.ts | 26 ++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/page/page-network-request.spec.ts b/tests/page/page-network-request.spec.ts index 624e36abfd..d3679ed14a 100644 --- a/tests/page/page-network-request.spec.ts +++ b/tests/page/page-network-request.spec.ts @@ -19,6 +19,14 @@ import { test as it, expect } from './pageTest'; import { attachFrame } from '../config/utils'; import fs from 'fs'; +function adjustServerHeaders(headers: Object, browserName: string, channel: string) { + if (browserName === 'firefox' && channel === 'firefox-beta') { + // This is a new experimental feature, only enabled in Firefox Beta for now. + delete headers['priority']; + } + return headers; +} + it('should work for main frame navigation request', async ({ page, server }) => { const requests = []; page.on('request', request => requests.push(request)); @@ -82,7 +90,7 @@ it('should return headers', async ({ page, server, browserName }) => { expect(response.request().headers()['user-agent']).toContain('WebKit'); }); -it('should get the same headers as the server', async ({ page, server, browserName, platform, isElectron, browserMajorVersion }) => { +it('should get the same headers as the server', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => { it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99'); it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language'); let serverRequest; @@ -92,10 +100,10 @@ it('should get the same headers as the server', async ({ page, server, browserNa }); const response = await page.goto(server.PREFIX + '/empty.html'); const headers = await response.request().allHeaders(); - expect(headers).toEqual(serverRequest.headers); + expect(headers).toEqual(adjustServerHeaders(serverRequest.headers, browserName, channel)); }); -it('should not return allHeaders() until they are available', async ({ page, server, browserName, platform, isElectron, browserMajorVersion }) => { +it('should not return allHeaders() until they are available', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => { it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99'); it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language'); @@ -114,13 +122,13 @@ it('should not return allHeaders() until they are available', async ({ page, ser await page.goto(server.PREFIX + '/empty.html'); const requestHeaders = await requestHeadersPromise; - expect(requestHeaders).toEqual(serverRequest.headers); + expect(requestHeaders).toEqual(adjustServerHeaders(serverRequest.headers, browserName, channel)); const responseHeaders = await responseHeadersPromise; expect(responseHeaders['foo']).toBe('bar'); }); -it('should get the same headers as the server CORS', async ({ page, server, browserName, platform, isElectron, browserMajorVersion }) => { +it('should get the same headers as the server CORS', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => { it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99'); it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language'); @@ -139,7 +147,7 @@ it('should get the same headers as the server CORS', async ({ page, server, brow expect(text).toBe('done'); const response = await responsePromise; const headers = await response.request().allHeaders(); - expect(headers).toEqual(serverRequest.headers); + expect(headers).toEqual(adjustServerHeaders(serverRequest.headers, browserName, channel)); }); it('should not get preflight CORS requests when intercepting', async ({ page, server, browserName, isAndroid }) => { @@ -353,7 +361,7 @@ it('should return navigation bit when navigating to image', async ({ page, serve expect(requests[0].isNavigationRequest()).toBe(true); }); -it('should report raw headers', async ({ page, server, browserName, platform, isElectron, browserMajorVersion }) => { +it('should report raw headers', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => { it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99'); let expectedHeaders: { name: string, value: string }[]; @@ -376,6 +384,10 @@ it('should report raw headers', async ({ page, server, browserName, platform, is return { name, value: values[0] }; }); } + if (browserName === 'firefox' && channel === 'firefox-beta') { + // This is a new experimental feature, only enabled in Firefox Beta for now. + expectedHeaders = expectedHeaders.filter(({ name }) => name.toLowerCase() !== 'priority'); + } res.end(); }); await page.goto(server.EMPTY_PAGE); From f1f3929a675f4fb6467595721e88692619dbbe52 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 24 Apr 2024 14:14:05 -0700 Subject: [PATCH 11/15] chore: get rid of ConfigInWorker, use FullConfig instead (#30517) Addressing API review feedback. --- docs/src/test-api/class-configinworker.md | 135 ----------- .../class-fullconfig.md | 6 +- .../class-fullproject.md | 5 +- docs/src/test-api/class-projectinworker.md | 96 -------- docs/src/test-api/class-testconfig.md | 4 +- docs/src/test-api/class-testinfo.md | 4 +- docs/src/test-api/class-testproject.md | 2 +- docs/src/test-api/class-workerinfo.md | 4 +- packages/playwright/src/worker/testInfo.ts | 8 +- packages/playwright/types/test.d.ts | 40 ++-- packages/playwright/types/testReporter.d.ts | 213 +----------------- tests/playwright-test/global-setup.spec.ts | 4 +- utils/generate_types/overrides-test.d.ts | 6 +- .../overrides-testReporter.d.ts | 16 +- 14 files changed, 49 insertions(+), 494 deletions(-) delete mode 100644 docs/src/test-api/class-configinworker.md rename docs/src/{test-reporter-api => test-api}/class-fullconfig.md (88%) rename docs/src/{test-reporter-api => test-api}/class-fullproject.md (85%) delete mode 100644 docs/src/test-api/class-projectinworker.md diff --git a/docs/src/test-api/class-configinworker.md b/docs/src/test-api/class-configinworker.md deleted file mode 100644 index 5832dca885..0000000000 --- a/docs/src/test-api/class-configinworker.md +++ /dev/null @@ -1,135 +0,0 @@ -# class: ConfigInWorker -* since: v1.10 -* langs: js - -Resolved configuration available via [`property: TestInfo.config`] and [`property: WorkerInfo.config`]. - -## property: ConfigInWorker.configFile -* since: v1.20 -- type: ?<[string]> - -Path to the configuration file (if any) used to run the tests. - -## property: ConfigInWorker.forbidOnly -* since: v1.10 -- type: <[boolean]> - -See [`property: TestConfig.forbidOnly`]. - -## property: ConfigInWorker.fullyParallel -* since: v1.20 -- type: <[boolean]> - -See [`property: TestConfig.fullyParallel`]. - -## property: ConfigInWorker.globalSetup -* since: v1.10 -- type: <[null]|[string]> - -See [`property: TestConfig.globalSetup`]. - -## property: ConfigInWorker.globalTeardown -* since: v1.10 -- type: <[null]|[string]> - -See [`property: TestConfig.globalTeardown`]. - -## property: ConfigInWorker.globalTimeout -* since: v1.10 -- type: <[int]> - -See [`property: TestConfig.globalTimeout`]. - -## property: ConfigInWorker.grep -* since: v1.10 -- type: <[RegExp]|[Array]<[RegExp]>> - -See [`property: TestConfig.grep`]. - -## property: ConfigInWorker.grepInvert -* since: v1.10 -- type: <[null]|[RegExp]|[Array]<[RegExp]>> - -See [`property: TestConfig.grepInvert`]. - -## property: ConfigInWorker.maxFailures -* since: v1.10 -- type: <[int]> - -See [`property: TestConfig.maxFailures`]. - -## property: ConfigInWorker.metadata -* since: v1.10 -- type: <[Metadata]> - -See [`property: TestConfig.metadata`]. - -## property: ConfigInWorker.preserveOutput -* since: v1.10 -- type: <[PreserveOutput]<"always"|"never"|"failures-only">> - -See [`property: TestConfig.preserveOutput`]. - -## property: ConfigInWorker.projects -* since: v1.10 -- type: <[Array]<[ProjectInWorker]>> - -List of resolved projects. - -## property: ConfigInWorker.quiet -* since: v1.10 -- type: <[boolean]> - -See [`property: TestConfig.quiet`]. - -## property: ConfigInWorker.reporter -* since: v1.10 -- type: <[string]|[Array]<[Object]>|[BuiltInReporter]<"list"|"dot"|"line"|"github"|"json"|"junit"|"null"|"html">> - - `0` <[string]> Reporter name or module or file path - - `1` <[Object]> An object with reporter options if any - -See [`property: TestConfig.reporter`]. - -## property: ConfigInWorker.reportSlowTests -* since: v1.10 -- type: <[null]|[Object]> - - `max` <[int]> The maximum number of slow test files to report. Defaults to `5`. - - `threshold` <[float]> Test duration in milliseconds that is considered slow. Defaults to 15 seconds. - -See [`property: TestConfig.reportSlowTests`]. - -## property: ConfigInWorker.rootDir -* since: v1.20 -- type: <[string]> - -## property: ConfigInWorker.shard -* since: v1.10 -- type: <[null]|[Object]> - - `total` <[int]> The total number of shards. - - `current` <[int]> The index of the shard to execute, one-based. - -See [`property: TestConfig.shard`]. - -## property: ConfigInWorker.updateSnapshots -* since: v1.10 -- type: <[UpdateSnapshots]<"all"|"none"|"missing">> - -See [`property: TestConfig.updateSnapshots`]. - -## property: ConfigInWorker.version -* since: v1.20 -- type: <[string]> - -Playwright version. - -## property: ConfigInWorker.webServer -* since: v1.10 -- type: <[null]|[Object]> - -See [`property: TestConfig.webServer`]. - -## property: ConfigInWorker.workers -* since: v1.10 -- type: <[int]> - -See [`property: TestConfig.workers`]. diff --git a/docs/src/test-reporter-api/class-fullconfig.md b/docs/src/test-api/class-fullconfig.md similarity index 88% rename from docs/src/test-reporter-api/class-fullconfig.md rename to docs/src/test-api/class-fullconfig.md index ce4318560f..e6437ab314 100644 --- a/docs/src/test-reporter-api/class-fullconfig.md +++ b/docs/src/test-api/class-fullconfig.md @@ -2,13 +2,13 @@ * since: v1.10 * langs: js -Resolved configuration passed to [`method: Reporter.onBegin`]. +Resolved configuration which is accessible via [`property: TestInfo.config`] and is passed to the test reporters. To see the format of Playwright configuration file, please see [TestConfig] instead. ## property: FullConfig.configFile * since: v1.20 - type: ?<[string]> -Path to the configuration file (if any) used to run the tests. +Path to the configuration file used to run the tests. The value is an empty string if no config file was used. ## property: FullConfig.forbidOnly * since: v1.10 @@ -102,6 +102,8 @@ See [`property: TestConfig.reportSlowTests`]. * since: v1.20 - type: <[string]> +Base directory for all relative paths used in the reporters. + ## property: FullConfig.shard * since: v1.10 - type: <[null]|[Object]> diff --git a/docs/src/test-reporter-api/class-fullproject.md b/docs/src/test-api/class-fullproject.md similarity index 85% rename from docs/src/test-reporter-api/class-fullproject.md rename to docs/src/test-api/class-fullproject.md index 9a744115df..682f9bb672 100644 --- a/docs/src/test-reporter-api/class-fullproject.md +++ b/docs/src/test-api/class-fullproject.md @@ -2,10 +2,7 @@ * since: v1.10 * langs: js -Runtime representation of the test project configuration that is passed -to [Reporter]. It exposes some of the resolved fields declared in -[TestProject]. You can get [FullProject] instance from [`property: FullConfig.projects`] -or [`method: Suite.project`]. +Runtime representation of the test project configuration. It is accessible in the tests via [`property: TestInfo.project`] and [`property: WorkerInfo.project`] and is passed to the test reporters. To see the format of the project in the Playwright configuration file please see [TestProject] instead. ## property: FullProject.dependencies * since: v1.31 diff --git a/docs/src/test-api/class-projectinworker.md b/docs/src/test-api/class-projectinworker.md deleted file mode 100644 index c8ede102ba..0000000000 --- a/docs/src/test-api/class-projectinworker.md +++ /dev/null @@ -1,96 +0,0 @@ -# class: ProjectInWorker -* since: v1.10 -* langs: js - -Runtime representation of the test project configuration that can be accessed -in the tests via [`property: TestInfo.project`] and [`property: WorkerInfo.project`]. - -## property: ProjectInWorker.dependencies -* since: v1.31 -- type: <[Array]<[string]>> - -See [`property: TestProject.dependencies`]. - -## property: ProjectInWorker.grep -* since: v1.10 -- type: <[RegExp]|[Array]<[RegExp]>> - -See [`property: TestProject.grep`]. - -## property: ProjectInWorker.grepInvert -* since: v1.10 -- type: <[null]|[RegExp]|[Array]<[RegExp]>> - -See [`property: TestProject.grepInvert`]. - -## property: ProjectInWorker.metadata -* since: v1.10 -- type: <[Metadata]> - -See [`property: TestProject.metadata`]. - -## property: ProjectInWorker.name -* since: v1.10 -- type: <[string]> - -See [`property: TestProject.name`]. - -## property: ProjectInWorker.snapshotDir -* since: v1.10 -- type: <[string]> - -See [`property: TestProject.snapshotDir`]. - -## property: ProjectInWorker.outputDir -* since: v1.10 -- type: <[string]> - -See [`property: TestProject.outputDir`]. - -## property: ProjectInWorker.repeatEach -* since: v1.10 -- type: <[int]> - -See [`property: TestProject.repeatEach`]. - -## property: ProjectInWorker.retries -* since: v1.10 -- type: <[int]> - -See [`property: TestProject.retries`]. - -## property: ProjectInWorker.teardown -* since: v1.34 -- type: ?<[string]> - -See [`property: TestProject.teardown`]. - -## property: ProjectInWorker.testDir -* since: v1.10 -- type: <[string]> - -See [`property: TestProject.testDir`]. - -## property: ProjectInWorker.testIgnore -* since: v1.10 -- type: <[string]|[RegExp]|[Array]<[string]|[RegExp]>> - -See [`property: TestProject.testIgnore`]. - -## property: ProjectInWorker.testMatch -* since: v1.10 -- type: <[string]|[RegExp]|[Array]<[string]|[RegExp]>> - -See [`property: TestProject.testMatch`]. - -## property: ProjectInWorker.timeout -* since: v1.10 -- type: <[int]> - -See [`property: TestProject.timeout`]. - -## property: ProjectInWorker.use -* since: v1.10 -- type: <[Fixtures]> - -See [`property: TestProject.use`]. diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index a89236cbf0..e19a11f815 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -2,7 +2,7 @@ * since: v1.10 * langs: js -Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or `testDir`. These options are described in the [TestConfig] object in the [configuration file](../test-configuration.md). +Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or `testDir`. These options are described in the [TestConfig] object in the [configuration file](../test-configuration.md). This type describes format of the configuration file, to access resolved configuration parameters at run time use [FullConfig]. Playwright Test supports running multiple test projects at the same time. Project-specific options should be put to [`property: TestConfig.projects`], but top-level [TestConfig] can also define base options shared between all projects. @@ -112,7 +112,7 @@ export default defineConfig({ * since: v1.10 - type: ?<[string]> -Path to the global setup file. This file will be required and run before all the tests. It must export a single function that takes a [`TestConfig`] argument. +Path to the global setup file. This file will be required and run before all the tests. It must export a single function that takes a [FullConfig] argument. Learn more about [global setup and teardown](../test-global-setup-teardown.md). diff --git a/docs/src/test-api/class-testinfo.md b/docs/src/test-api/class-testinfo.md index 096fbfcc10..cbc125b3bf 100644 --- a/docs/src/test-api/class-testinfo.md +++ b/docs/src/test-api/class-testinfo.md @@ -106,7 +106,7 @@ Column number where the currently running test is declared. ## property: TestInfo.config * since: v1.10 -- type: <[ConfigInWorker]> +- type: <[FullConfig]> Processed configuration from the [configuration file](../test-configuration.md). @@ -279,7 +279,7 @@ Also available as `process.env.TEST_PARALLEL_INDEX`. Learn more about [paralleli ## property: TestInfo.project * since: v1.10 -- type: <[ProjectInWorker]> +- type: <[FullProject]> Processed project configuration from the [configuration file](../test-configuration.md). diff --git a/docs/src/test-api/class-testproject.md b/docs/src/test-api/class-testproject.md index 2ed68de275..7477a3a88d 100644 --- a/docs/src/test-api/class-testproject.md +++ b/docs/src/test-api/class-testproject.md @@ -2,7 +2,7 @@ * since: v1.10 * langs: js -Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple configurations. For example, consider running tests against multiple browsers. +Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple configurations. For example, consider running tests against multiple browsers. This type describes format of a project in the configuration file, to access resolved configuration parameters at run time use [FullProject]. `TestProject` encapsulates configuration specific to a single project. Projects are configured in [`property: TestConfig.projects`] specified in the [configuration file](../test-configuration.md). Note that all properties of [TestProject] are available in the top-level [TestConfig], in which case they are shared between all projects. diff --git a/docs/src/test-api/class-workerinfo.md b/docs/src/test-api/class-workerinfo.md index e7ae892190..3ae9a3bd21 100644 --- a/docs/src/test-api/class-workerinfo.md +++ b/docs/src/test-api/class-workerinfo.md @@ -6,7 +6,7 @@ ## property: WorkerInfo.config * since: v1.10 -- type: <[ConfigInWorker]> +- type: <[FullConfig]> Processed configuration from the [configuration file](../test-configuration.md). @@ -22,7 +22,7 @@ Also available as `process.env.TEST_PARALLEL_INDEX`. Learn more about [paralleli ## property: WorkerInfo.project * since: v1.10 -- type: <[ProjectInWorker]> +- type: <[FullProject]> Processed project configuration from the [configuration file](../test-configuration.md). diff --git a/packages/playwright/src/worker/testInfo.ts b/packages/playwright/src/worker/testInfo.ts index 5204ade21e..6e55f68dac 100644 --- a/packages/playwright/src/worker/testInfo.ts +++ b/packages/playwright/src/worker/testInfo.ts @@ -17,13 +17,13 @@ import fs from 'fs'; import path from 'path'; import { captureRawStack, monotonicTime, zones, sanitizeForFilePath, stringifyStackFrames } from 'playwright-core/lib/utils'; -import type { TestInfoError, TestInfo, TestStatus, ProjectInWorker, ConfigInWorker } from '../../types/test'; +import type { TestInfoError, TestInfo, TestStatus, FullProject } from '../../types/test'; import type { AttachmentPayload, StepBeginPayload, StepEndPayload, WorkerInitParams } from '../common/ipc'; import type { TestCase } from '../common/test'; import { TimeoutManager, TimeoutManagerError, kMaxDeadline } from './timeoutManager'; import type { RunnableDescription } from './timeoutManager'; import type { Annotation, FullConfigInternal, FullProjectInternal } from '../common/config'; -import type { Location } from '../../types/testReporter'; +import type { FullConfig, Location } from '../../types/testReporter'; import { debugTest, filteredStackTrace, formatLocation, getContainedPath, normalizeAndSaveAttachment, serializeError, trimLongString, windowsFilesystemFriendlyLength } from '../util'; import { TestTracing } from './testTracing'; import type { Attachment } from './testTracing'; @@ -81,8 +81,8 @@ export class TestInfoImpl implements TestInfo { readonly retry: number; readonly workerIndex: number; readonly parallelIndex: number; - readonly project: ProjectInWorker; - readonly config: ConfigInWorker; + readonly project: FullProject; + readonly config: FullConfig; readonly title: string; readonly titlePath: string[]; readonly file: string; diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 01470c2bba..1a31c166b9 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -35,7 +35,9 @@ type UseOptions = Partial & Partial; /** * Playwright Test supports running multiple test projects at the same time. This is useful for running tests in - * multiple configurations. For example, consider running tests against multiple browsers. + * multiple configurations. For example, consider running tests against multiple browsers. This type describes format + * of a project in the configuration file, to access resolved configuration parameters at run time use {@link + * FullProject}. * * `TestProject` encapsulates configuration specific to a single project. Projects are configured in * [testConfig.projects](https://playwright.dev/docs/api/class-testconfig#test-config-projects) specified in the @@ -626,11 +628,13 @@ export interface Project extends TestProject { +export interface FullProject { /** * See [testProject.use](https://playwright.dev/docs/api/class-testproject#test-project-use). */ @@ -711,7 +715,8 @@ type LiteralUnion = T | (U & { zz_IGNORE_ME?: never }); /** * Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` * or `testDir`. These options are described in the {@link TestConfig} object in the - * [configuration file](https://playwright.dev/docs/test-configuration). + * [configuration file](https://playwright.dev/docs/test-configuration). This type describes format of the configuration file, to access + * resolved configuration parameters at run time use {@link FullConfig}. * * Playwright Test supports running multiple test projects at the same time. Project-specific options should be put to * [testConfig.projects](https://playwright.dev/docs/api/class-testconfig#test-config-projects), but top-level {@link @@ -1055,7 +1060,7 @@ interface TestConfig { /** * Path to the global setup file. This file will be required and run before all the tests. It must export a single - * function that takes a [`TestConfig`] argument. + * function that takes a {@link FullConfig} argument. * * Learn more about [global setup and teardown](https://playwright.dev/docs/test-global-setup-teardown). * @@ -1667,15 +1672,15 @@ export interface Config extends TestConfig { +export interface FullConfig { /** * List of resolved projects. */ - projects: ProjectInWorker[]; + projects: FullProject[]; /** * See [testConfig.reporter](https://playwright.dev/docs/api/class-testconfig#test-config-reporter). */ @@ -1685,7 +1690,7 @@ export interface ConfigInWorker { */ webServer: TestConfigWebServer | null; /** - * Path to the configuration file (if any) used to run the tests. + * Path to the configuration file used to run the tests. The value is an empty string if no config file was used. */ configFile?: string; @@ -1759,6 +1764,9 @@ export interface ConfigInWorker { threshold: number; }; + /** + * Base directory for all relative paths used in the reporters. + */ rootDir: string; /** @@ -8129,7 +8137,7 @@ export interface TestInfo { /** * Processed configuration from the [configuration file](https://playwright.dev/docs/test-configuration). */ - config: ConfigInWorker; + config: FullConfig; /** * The number of milliseconds the test took to finish. Always zero before the test finishes, either successfully or @@ -8205,7 +8213,7 @@ export interface TestInfo { /** * Processed project configuration from the [configuration file](https://playwright.dev/docs/test-configuration). */ - project: ProjectInWorker; + project: FullProject; /** * Specifies a unique repeat index when running in "repeat each" mode. This mode is enabled by passing `--repeat-each` @@ -8359,7 +8367,7 @@ export interface WorkerInfo { /** * Processed configuration from the [configuration file](https://playwright.dev/docs/test-configuration). */ - config: ConfigInWorker; + config: FullConfig; /** * The index of the worker between `0` and `workers - 1`. It is guaranteed that workers running at the same time have @@ -8374,7 +8382,7 @@ export interface WorkerInfo { /** * Processed project configuration from the [configuration file](https://playwright.dev/docs/test-configuration). */ - project: ProjectInWorker; + project: FullProject; /** * The unique index of the worker process that is running the test. When a worker is restarted, for example after a diff --git a/packages/playwright/types/testReporter.d.ts b/packages/playwright/types/testReporter.d.ts index 694ba65961..06e762006e 100644 --- a/packages/playwright/types/testReporter.d.ts +++ b/packages/playwright/types/testReporter.d.ts @@ -15,217 +15,8 @@ * limitations under the License. */ -import type { TestStatus, Metadata, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription, ConfigInWorker } from './test'; -export type { TestStatus } from './test'; - -type UseOptions = Partial & Partial; - -/** - * Resolved configuration passed to - * [reporter.onBegin(config, suite)](https://playwright.dev/docs/api/class-reporter#reporter-on-begin). - */ -export interface FullConfig { - /** - * List of resolved projects. - */ - projects: FullProject[]; - /** - * See [testConfig.reporter](https://playwright.dev/docs/api/class-testconfig#test-config-reporter). - */ - reporter: ReporterDescription[]; - /** - * See [testConfig.webServer](https://playwright.dev/docs/api/class-testconfig#test-config-web-server). - */ - webServer: ConfigInWorker['webServer']; - /** - * Path to the configuration file (if any) used to run the tests. - */ - configFile?: string; - - /** - * See [testConfig.forbidOnly](https://playwright.dev/docs/api/class-testconfig#test-config-forbid-only). - */ - forbidOnly: boolean; - - /** - * See [testConfig.fullyParallel](https://playwright.dev/docs/api/class-testconfig#test-config-fully-parallel). - */ - fullyParallel: boolean; - - /** - * See [testConfig.globalSetup](https://playwright.dev/docs/api/class-testconfig#test-config-global-setup). - */ - globalSetup: null|string; - - /** - * See [testConfig.globalTeardown](https://playwright.dev/docs/api/class-testconfig#test-config-global-teardown). - */ - globalTeardown: null|string; - - /** - * See [testConfig.globalTimeout](https://playwright.dev/docs/api/class-testconfig#test-config-global-timeout). - */ - globalTimeout: number; - - /** - * See [testConfig.grep](https://playwright.dev/docs/api/class-testconfig#test-config-grep). - */ - grep: RegExp|Array; - - /** - * See [testConfig.grepInvert](https://playwright.dev/docs/api/class-testconfig#test-config-grep-invert). - */ - grepInvert: null|RegExp|Array; - - /** - * See [testConfig.maxFailures](https://playwright.dev/docs/api/class-testconfig#test-config-max-failures). - */ - maxFailures: number; - - /** - * See [testConfig.metadata](https://playwright.dev/docs/api/class-testconfig#test-config-metadata). - */ - metadata: Metadata; - - /** - * See [testConfig.preserveOutput](https://playwright.dev/docs/api/class-testconfig#test-config-preserve-output). - */ - preserveOutput: "always"|"never"|"failures-only"; - - /** - * See [testConfig.quiet](https://playwright.dev/docs/api/class-testconfig#test-config-quiet). - */ - quiet: boolean; - - /** - * See [testConfig.reportSlowTests](https://playwright.dev/docs/api/class-testconfig#test-config-report-slow-tests). - */ - reportSlowTests: null|{ - /** - * The maximum number of slow test files to report. Defaults to `5`. - */ - max: number; - - /** - * Test duration in milliseconds that is considered slow. Defaults to 15 seconds. - */ - threshold: number; - }; - - rootDir: string; - - /** - * See [testConfig.shard](https://playwright.dev/docs/api/class-testconfig#test-config-shard). - */ - shard: null|{ - /** - * The total number of shards. - */ - total: number; - - /** - * The index of the shard to execute, one-based. - */ - current: number; - }; - - /** - * See [testConfig.updateSnapshots](https://playwright.dev/docs/api/class-testconfig#test-config-update-snapshots). - */ - updateSnapshots: "all"|"none"|"missing"; - - /** - * Playwright version. - */ - version: string; - - /** - * See [testConfig.workers](https://playwright.dev/docs/api/class-testconfig#test-config-workers). - */ - workers: number; -} - -/** - * Runtime representation of the test project configuration that is passed to {@link Reporter}. It exposes some of the - * resolved fields declared in {@link TestProject}. You can get {@link FullProject} instance from - * [fullConfig.projects](https://playwright.dev/docs/api/class-fullconfig#full-config-projects) or - * [suite.project()](https://playwright.dev/docs/api/class-suite#suite-project). - */ -export interface FullProject { - /** - * See [testProject.use](https://playwright.dev/docs/api/class-testproject#test-project-use). - */ - use: UseOptions; - /** - * See [testProject.dependencies](https://playwright.dev/docs/api/class-testproject#test-project-dependencies). - */ - dependencies: Array; - - /** - * See [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep). - */ - grep: RegExp|Array; - - /** - * See [testProject.grepInvert](https://playwright.dev/docs/api/class-testproject#test-project-grep-invert). - */ - grepInvert: null|RegExp|Array; - - /** - * See [testProject.metadata](https://playwright.dev/docs/api/class-testproject#test-project-metadata). - */ - metadata: Metadata; - - /** - * See [testProject.name](https://playwright.dev/docs/api/class-testproject#test-project-name). - */ - name: string; - - /** - * See [testProject.outputDir](https://playwright.dev/docs/api/class-testproject#test-project-output-dir). - */ - outputDir: string; - - /** - * See [testProject.repeatEach](https://playwright.dev/docs/api/class-testproject#test-project-repeat-each). - */ - repeatEach: number; - - /** - * See [testProject.retries](https://playwright.dev/docs/api/class-testproject#test-project-retries). - */ - retries: number; - - /** - * See [testProject.snapshotDir](https://playwright.dev/docs/api/class-testproject#test-project-snapshot-dir). - */ - snapshotDir: string; - - /** - * See [testProject.teardown](https://playwright.dev/docs/api/class-testproject#test-project-teardown). - */ - teardown?: string; - - /** - * See [testProject.testDir](https://playwright.dev/docs/api/class-testproject#test-project-test-dir). - */ - testDir: string; - - /** - * See [testProject.testIgnore](https://playwright.dev/docs/api/class-testproject#test-project-test-ignore). - */ - testIgnore: string|RegExp|Array; - - /** - * See [testProject.testMatch](https://playwright.dev/docs/api/class-testproject#test-project-test-match). - */ - testMatch: string|RegExp|Array; - - /** - * See [testProject.timeout](https://playwright.dev/docs/api/class-testproject#test-project-timeout). - */ - timeout: number; -} +import type { TestStatus, Metadata, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription, FullConfig, FullProject } from './test'; +export type { FullConfig, FullProject, TestStatus } from './test'; /** * Result of the full test run. diff --git a/tests/playwright-test/global-setup.spec.ts b/tests/playwright-test/global-setup.spec.ts index bcb9013387..3d28be82cd 100644 --- a/tests/playwright-test/global-setup.spec.ts +++ b/tests/playwright-test/global-setup.spec.ts @@ -280,8 +280,8 @@ const authFiles = { export default config; `, 'auth.ts': ` - import { chromium, ConfigInWorker } from '@playwright/test'; - async function globalSetup(config: ConfigInWorker) { + import { chromium, FullConfig } from '@playwright/test'; + async function globalSetup(config: FullConfig) { const { baseURL, storageState } = config.projects[0].use; const browser = await chromium.launch(); const page = await browser.newPage(); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 9d58db13fe..4907995f3e 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -39,7 +39,7 @@ interface TestProject { export interface Project extends TestProject { } -export interface ProjectInWorker { +export interface FullProject { use: UseOptions; } @@ -57,8 +57,8 @@ export interface Config extends TestConfig { - projects: ProjectInWorker[]; +export interface FullConfig { + projects: FullProject[]; reporter: ReporterDescription[]; webServer: TestConfigWebServer | null; } diff --git a/utils/generate_types/overrides-testReporter.d.ts b/utils/generate_types/overrides-testReporter.d.ts index e5bf37cdc8..51eab7e370 100644 --- a/utils/generate_types/overrides-testReporter.d.ts +++ b/utils/generate_types/overrides-testReporter.d.ts @@ -14,20 +14,8 @@ * limitations under the License. */ -import type { TestStatus, Metadata, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription, ConfigInWorker } from './test'; -export type { TestStatus } from './test'; - -type UseOptions = Partial & Partial; - -export interface FullConfig { - projects: FullProject[]; - reporter: ReporterDescription[]; - webServer: ConfigInWorker['webServer']; -} - -export interface FullProject { - use: UseOptions; -} +import type { TestStatus, Metadata, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription, FullConfig, FullProject } from './test'; +export type { FullConfig, FullProject, TestStatus } from './test'; /** * Result of the full test run. From e2f7acea1e76cbcb1d957e8887dcdde9edd51f14 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 24 Apr 2024 15:18:43 -0700 Subject: [PATCH 12/15] docs: fix capitalization and alphabetic order in TestConfig (#30523) --- docs/src/api/params.md | 16 +-- docs/src/test-api/class-testconfig.md | 28 ++--- packages/playwright/types/test.d.ts | 150 +++++++++++++------------- 3 files changed, 97 insertions(+), 97 deletions(-) diff --git a/docs/src/api/params.md b/docs/src/api/params.md index e105f2fd14..9bc70c1232 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -1731,13 +1731,17 @@ test.describe('suite', () => { The list of supported tokens: -* `{testDir}` - Project's [`property: TestConfig.testDir`]. - * Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with config) -* `{snapshotDir}` - Project's [`property: TestConfig.snapshotDir`]. - * Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`) +* `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated snapshot name. + * Value: `foo/bar/baz` +* `{ext}` - snapshot extension (with dots) + * Value: `.png` * `{platform}` - The value of `process.platform`. * `{projectName}` - Project's file-system-sanitized name, if any. * Value: `''` (empty string). +* `{snapshotDir}` - Project's [`property: TestConfig.snapshotDir`]. + * Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`) +* `{testDir}` - Project's [`property: TestConfig.testDir`]. + * Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with config) * `{testFileDir}` - Directories in relative path from `testDir` to **test file**. * Value: `page` * `{testFileName}` - Test file name with extension. @@ -1746,10 +1750,6 @@ The list of supported tokens: * Value: `page/page-click.spec.ts` * `{testName}` - File-system-sanitized test title, including parent describes but excluding file name. * Value: `suite-test-should-work` -* `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated snapshot name. - * Value: `foo/bar/baz` -* `{ext}` - snapshot extension (with dots) - * Value: `.png` Each token can be preceded with a single character that will be used **only if** this token has non-empty value. diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index e19a11f815..a36b7437d2 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -41,20 +41,20 @@ export default defineConfig({ - type: ?<[Object]> - `timeout` ?<[int]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms. - `toHaveScreenshot` ?<[Object]> Configuration for the [`method: PageAssertions.toHaveScreenshot#1`] method. - - `threshold` ?<[float]> an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - - `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default. - - `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. - `animations` ?<[ScreenshotAnimations]<"allow"|"disabled">> See [`option: animations`] in [`method: Page.screenshot`]. Defaults to `"disabled"`. - `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`. + - `maxDiffPixels` ?<[int]> An acceptable amount of pixels that could be different, unset by default. + - `maxDiffPixelRatio` ?<[float]> An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. - `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: scale`] in [`method: Page.screenshot`]. Defaults to `"css"`. - `stylePath` ?<[string]|[Array]<[string]>> See [`option: style`] in [`method: Page.screenshot`]. + - `threshold` ?<[float]> An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - `toMatchSnapshot` ?<[Object]> Configuration for the [`method: SnapshotAssertions.toMatchSnapshot#1`] method. - - `threshold` ?<[float]> an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - - `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default. - - `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. + - `maxDiffPixels` ?<[int]> An acceptable amount of pixels that could be different, unset by default. + - `maxDiffPixelRatio` ?<[float]> An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. + - `threshold` ?<[float]> An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - `toPass` ?<[Object]> Configuration for the [expect(value).toPass()](../test-assertions.md#expecttopass) method. - - `timeout` ?<[int]> timeout for toPass method in milliseconds. - - `intervals` ?<[Array]<[int]>> probe intervals for toPass method in milliseconds. + - `intervals` ?<[Array]<[int]>> Probe intervals for toPass method in milliseconds. + - `timeout` ?<[int]> Timeout for toPass method in milliseconds. Configuration for the `expect` assertion library. Learn more about [various timeouts](../test-timeouts.md). @@ -458,8 +458,8 @@ export default defineConfig({ ## property: TestConfig.shard * since: v1.10 - type: ?<[null]|[Object]> - - `total` <[int]> The total number of shards. - `current` <[int]> The index of the shard to execute, one-based. + - `total` <[int]> The total number of shards. Shard tests and execute only the selected shard. Specify in the one-based form like `{ total: 5, current: 2 }`. @@ -589,15 +589,15 @@ export default defineConfig({ * since: v1.10 - type: ?<[Object]|[Array]<[Object]>> - `command` <[string]> Shell command to start. For example `npm run start`.. - - `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` or `url` should be specified. - - `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified. + - `cwd` ?<[string]> Current working directory of the spawned process, defaults to the directory of the configuration file. + - `env` ?<[Object]<[string], [string]>> Environment variables to set for the command, `process.env` by default. - `ignoreHTTPSErrors` ?<[boolean]> Whether to ignore HTTPS errors when fetching the `url`. Defaults to `false`. - - `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. + - `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` or `url` should be specified. - `reuseExistingServer` ?<[boolean]> If true, it will re-use an existing server on the `port` or `url` when available. If no server is running on that `port` or `url`, it will run the command to start a new server. If `false`, it will throw if an existing process is listening on the `port` or `url`. This should be commonly set to `!process.env.CI` to allow the local dev server when running tests locally. - `stdout` ?<["pipe"|"ignore"]> If `"pipe"`, it will pipe the stdout of the command to the process stdout. If `"ignore"`, it will ignore the stdout of the command. Default to `"ignore"`. - `stderr` ?<["pipe"|"ignore"]> Whether to pipe the stderr of the command to the process stderr or ignore it. Defaults to `"pipe"`. - - `cwd` ?<[string]> Current working directory of the spawned process, defaults to the directory of the configuration file. - - `env` ?<[Object]<[string], [string]>> Environment variables to set for the command, `process.env` by default. + - `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. + - `url` ?<[string]> The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified. Launch a development web server (or multiple) during the tests. diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 1a31c166b9..993e7addd1 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -434,16 +434,22 @@ interface TestProject { * ``` * * The list of supported tokens: + * - `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the + * `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated + * snapshot name. + * - Value: `foo/bar/baz` + * - `{ext}` - snapshot extension (with dots) + * - Value: `.png` + * - `{platform}` - The value of `process.platform`. + * - `{projectName}` - Project's file-system-sanitized name, if any. + * - Value: `''` (empty string). + * - `{snapshotDir}` - Project's + * [testConfig.snapshotDir](https://playwright.dev/docs/api/class-testconfig#test-config-snapshot-dir). + * - Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`) * - `{testDir}` - Project's * [testConfig.testDir](https://playwright.dev/docs/api/class-testconfig#test-config-test-dir). * - Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with * config) - * - `{snapshotDir}` - Project's - * [testConfig.snapshotDir](https://playwright.dev/docs/api/class-testconfig#test-config-snapshot-dir). - * - Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`) - * - `{platform}` - The value of `process.platform`. - * - `{projectName}` - Project's file-system-sanitized name, if any. - * - Value: `''` (empty string). * - `{testFileDir}` - Directories in relative path from `testDir` to **test file**. * - Value: `page` * - `{testFileName}` - Test file name with extension. @@ -452,12 +458,6 @@ interface TestProject { * - Value: `page/page-click.spec.ts` * - `{testName}` - File-system-sanitized test title, including parent describes but excluding file name. * - Value: `suite-test-should-work` - * - `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the - * `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated - * snapshot name. - * - Value: `foo/bar/baz` - * - `{ext}` - snapshot extension (with dots) - * - Value: `.png` * * Each token can be preceded with a single character that will be used **only if** this token has non-empty value. * @@ -934,24 +934,6 @@ interface TestConfig { * method. */ toHaveScreenshot?: { - /** - * an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and - * `1` (lax). `"pixelmatch"` comparator computes color difference in - * [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - */ - threshold?: number; - - /** - * an acceptable amount of pixels that could be different, unset by default. - */ - maxDiffPixels?: number; - - /** - * an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by - * default. - */ - maxDiffPixelRatio?: number; - /** * See `animations` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). * Defaults to `"disabled"`. @@ -964,6 +946,17 @@ interface TestConfig { */ caret?: "hide"|"initial"; + /** + * An acceptable amount of pixels that could be different, unset by default. + */ + maxDiffPixels?: number; + + /** + * An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by + * default. + */ + maxDiffPixelRatio?: number; + /** * See `scale` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults * to `"css"`. @@ -974,6 +967,13 @@ interface TestConfig { * See `style` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). */ stylePath?: string|Array; + + /** + * An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and + * `1` (lax). `"pixelmatch"` comparator computes color difference in + * [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. + */ + threshold?: number; }; /** @@ -983,22 +983,22 @@ interface TestConfig { */ toMatchSnapshot?: { /** - * an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and - * `1` (lax). `"pixelmatch"` comparator computes color difference in - * [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - */ - threshold?: number; - - /** - * an acceptable amount of pixels that could be different, unset by default. + * An acceptable amount of pixels that could be different, unset by default. */ maxDiffPixels?: number; /** - * an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by + * An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by * default. */ maxDiffPixelRatio?: number; + + /** + * An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and + * `1` (lax). `"pixelmatch"` comparator computes color difference in + * [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. + */ + threshold?: number; }; /** @@ -1006,14 +1006,14 @@ interface TestConfig { */ toPass?: { /** - * timeout for toPass method in milliseconds. - */ - timeout?: number; - - /** - * probe intervals for toPass method in milliseconds. + * Probe intervals for toPass method in milliseconds. */ intervals?: Array; + + /** + * Timeout for toPass method in milliseconds. + */ + timeout?: number; }; }; @@ -1397,15 +1397,15 @@ interface TestConfig { * */ shard?: null|{ - /** - * The total number of shards. - */ - total: number; - /** * The index of the shard to execute, one-based. */ current: number; + + /** + * The total number of shards. + */ + total: number; }; /** @@ -1484,16 +1484,22 @@ interface TestConfig { * ``` * * The list of supported tokens: + * - `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the + * `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated + * snapshot name. + * - Value: `foo/bar/baz` + * - `{ext}` - snapshot extension (with dots) + * - Value: `.png` + * - `{platform}` - The value of `process.platform`. + * - `{projectName}` - Project's file-system-sanitized name, if any. + * - Value: `''` (empty string). + * - `{snapshotDir}` - Project's + * [testConfig.snapshotDir](https://playwright.dev/docs/api/class-testconfig#test-config-snapshot-dir). + * - Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`) * - `{testDir}` - Project's * [testConfig.testDir](https://playwright.dev/docs/api/class-testconfig#test-config-test-dir). * - Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with * config) - * - `{snapshotDir}` - Project's - * [testConfig.snapshotDir](https://playwright.dev/docs/api/class-testconfig#test-config-snapshot-dir). - * - Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`) - * - `{platform}` - The value of `process.platform`. - * - `{projectName}` - Project's file-system-sanitized name, if any. - * - Value: `''` (empty string). * - `{testFileDir}` - Directories in relative path from `testDir` to **test file**. * - Value: `page` * - `{testFileName}` - Test file name with extension. @@ -1502,12 +1508,6 @@ interface TestConfig { * - Value: `page/page-click.spec.ts` * - `{testName}` - File-system-sanitized test title, including parent describes but excluding file name. * - Value: `suite-test-should-work` - * - `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the - * `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated - * snapshot name. - * - Value: `foo/bar/baz` - * - `{ext}` - snapshot extension (with dots) - * - Value: `.png` * * Each token can be preceded with a single character that will be used **only if** this token has non-empty value. * @@ -8401,17 +8401,14 @@ interface TestConfigWebServer { command: string; /** - * The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` - * or `url` should be specified. + * Current working directory of the spawned process, defaults to the directory of the configuration file. */ - port?: number; + cwd?: string; /** - * The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the - * server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is - * checked. Either `port` or `url` should be specified. + * Environment variables to set for the command, `process.env` by default. */ - url?: string; + env?: { [key: string]: string; }; /** * Whether to ignore HTTPS errors when fetching the `url`. Defaults to `false`. @@ -8419,9 +8416,10 @@ interface TestConfigWebServer { ignoreHTTPSErrors?: boolean; /** - * How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. + * The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` + * or `url` should be specified. */ - timeout?: number; + port?: number; /** * If true, it will re-use an existing server on the `port` or `url` when available. If no server is running on that @@ -8443,13 +8441,15 @@ interface TestConfigWebServer { stderr?: "pipe"|"ignore"; /** - * Current working directory of the spawned process, defaults to the directory of the configuration file. + * How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. */ - cwd?: string; + timeout?: number; /** - * Environment variables to set for the command, `process.env` by default. + * The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the + * server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is + * checked. Either `port` or `url` should be specified. */ - env?: { [key: string]: string; }; + url?: string; } From 59689c9c97a140de2c0bf8a615b53a9e5859961b Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 24 Apr 2024 15:19:12 -0700 Subject: [PATCH 13/15] feat(addLocatorHandler): various improvements (#30494) - Automatically waiting for the overlay locator to be hidden, with `allowStayingVisible` opt-out. - `times: 1` option. - `removeLocatorHandler(locator, handler)` method. - Passing `locator` as first argument to `handler`. Fixes #30471. Fixes #30424. Fixes #29779. --- docs/src/api/class-page.md | 94 +++++++++++-- .../playwright-core/src/client/locator.ts | 4 + packages/playwright-core/src/client/page.ts | 29 +++- .../playwright-core/src/protocol/validator.ts | 6 + .../src/server/dispatchers/pageDispatcher.ts | 8 +- packages/playwright-core/src/server/frames.ts | 99 ++++++------- packages/playwright-core/src/server/page.ts | 21 ++- packages/playwright-core/types/types.d.ts | 45 +++++- packages/protocol/src/channels.ts | 14 +- packages/protocol/src/protocol.yml | 6 + tests/assets/input/handle-locator.html | 17 ++- tests/page/page-add-locator-handler.spec.ts | 132 +++++++++++++++++- utils/generate_types/test/test.ts | 2 - 13 files changed, 394 insertions(+), 83 deletions(-) diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 3934270f07..39610ae84d 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -3155,6 +3155,7 @@ This method lets you set up a special function, called a handler, that activates Things to keep in mind: * When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as a part of your normal test flow, instead of using [`method: Page.addLocatorHandler`]. * Playwright checks for the overlay every time before executing or retrying an action that requires an [actionability check](../actionability.md), or before performing an auto-waiting assertion check. When overlay is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't perform any actions, the handler will not be triggered. +* After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with [`option: allowStayingVisible`]. * The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. If your handler takes too long, it might cause timeouts. * You can register multiple handlers. However, only a single handler will be running at a time. Make sure the actions within a handler don't depend on another handler. @@ -3284,13 +3285,13 @@ await page.GotoAsync("https://example.com"); await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync(); ``` -An example with a custom callback on every actionability check. It uses a `` locator that is always visible, so the handler is called before every actionability check: +An example with a custom callback on every actionability check. It uses a `` locator that is always visible, so the handler is called before every actionability check. It is important to specify [`option: allowStayingVisible`], because the handler does not hide the `` element. ```js // Setup the handler. await page.addLocatorHandler(page.locator('body'), async () => { await page.evaluate(() => window.removeObstructionsForTestIfNeeded()); -}); +}, { allowStayingVisible: true }); // Write the test as usual. await page.goto('https://example.com'); @@ -3301,7 +3302,7 @@ await page.getByRole('button', { name: 'Start here' }).click(); // Setup the handler. page.addLocatorHandler(page.locator("body")), () => { page.evaluate("window.removeObstructionsForTestIfNeeded()"); -}); +}, new Page.AddLocatorHandlerOptions.setAllowStayingVisible(true)); // Write the test as usual. page.goto("https://example.com"); @@ -3312,7 +3313,7 @@ page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); # Setup the handler. def handler(): page.evaluate("window.removeObstructionsForTestIfNeeded()") -page.add_locator_handler(page.locator("body"), handler) +page.add_locator_handler(page.locator("body"), handler, allow_staying_visible=True) # Write the test as usual. page.goto("https://example.com") @@ -3323,7 +3324,7 @@ page.get_by_role("button", name="Start here").click() # Setup the handler. def handler(): await page.evaluate("window.removeObstructionsForTestIfNeeded()") -await page.add_locator_handler(page.locator("body"), handler) +await page.add_locator_handler(page.locator("body"), handler, allow_staying_visible=True) # Write the test as usual. await page.goto("https://example.com") @@ -3334,13 +3335,45 @@ await page.get_by_role("button", name="Start here").click() // Setup the handler. await page.AddLocatorHandlerAsync(page.Locator("body"), async () => { await page.EvaluateAsync("window.removeObstructionsForTestIfNeeded()"); -}); +}, new() { AllowStayingVisible = true }); // Write the test as usual. await page.GotoAsync("https://example.com"); await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync(); ``` +Handler takes the original locator as an argument. You can also automatically remove the handler after a number of invocations by setting [`option: times`]: + +```js +await page.addLocatorHandler(page.getByLabel('Close'), async locator => { + await locator.click(); +}, { times: 1 }); +``` + +```java +page.addLocatorHandler(page.getByLabel("Close"), locator => { + locator.click(); +}, new Page.AddLocatorHandlerOptions().setTimes(1)); +``` + +```python sync +def handler(locator): + locator.click() +page.add_locator_handler(page.get_by_label("Close"), handler, times=1) +``` + +```python async +def handler(locator): + await locator.click() +await page.add_locator_handler(page.get_by_label("Close"), handler, times=1) +``` + +```csharp +await page.AddLocatorHandlerAsync(page.GetByText("Sign up to the newsletter"), async locator => { + await locator.ClickAsync(); +}, new() { Times = 1 }); +``` + ### param: Page.addLocatorHandler.locator * since: v1.42 - `locator` <[Locator]> @@ -3350,24 +3383,67 @@ Locator that triggers the handler. ### param: Page.addLocatorHandler.handler * langs: js, python * since: v1.42 -- `handler` <[function]> +- `handler` <[function]\([Locator]\): [Promise]> Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. ### param: Page.addLocatorHandler.handler * langs: csharp * since: v1.42 -- `handler` <[function](): [Promise]> +- `handler` <[function]\([Locator]\)> Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. ### param: Page.addLocatorHandler.handler * langs: java * since: v1.42 -- `handler` <[Runnable]> +- `handler` <[function]\([Locator]\)> Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. +### option: Page.addLocatorHandler.times +* since: v1.44 +- `times` <[int]> + +Specifies the maximum number of times this handler should be called. Unlimited by default. + +### option: Page.addLocatorHandler.allowStayingVisible +* since: v1.44 +- `allowStayingVisible` <[boolean]> + +By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of this behavior, so that overlay can stay visible after the handler has run. + + +## async method: Page.removeLocatorHandler +* since: v1.44 + +:::warning[Experimental] +This method is experimental and its behavior may change in the upcoming releases. +::: + +Removes locator handler added by [`method: Page.addLocatorHandler`]. + +### param: Page.removeLocatorHandler.locator +* since: v1.44 +- `locator` <[Locator]> + +Locator passed to [`method: Page.addLocatorHandler`]. + +### param: Page.removeLocatorHandler.handler +* langs: js, python +* since: v1.44 +- `handler` <[function]\([Locator]\): [Promise]> + +Handler passed to [`method: Page.addLocatorHandler`]. + +### param: Page.addLocatorHandler.handler +* langs: csharp, java +* since: v1.44 +- `handler` <[function]\([Locator]\)> + +Handler passed to [`method: Page.addLocatorHandler`]. + + ## async method: Page.reload * since: v1.8 - returns: <[null]|[Response]> diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts index 8fd2d8e088..b6058e0abb 100644 --- a/packages/playwright-core/src/client/locator.ts +++ b/packages/playwright-core/src/client/locator.ts @@ -80,6 +80,10 @@ export class Locator implements api.Locator { }); } + _equals(locator: Locator) { + return this._frame === locator._frame && this._selector === locator._selector; + } + page() { return this._frame.page(); } diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 8fd1394a6f..254d4fbf6e 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -96,7 +96,7 @@ export class Page extends ChannelOwner implements api.Page _closeWasCalled: boolean = false; private _harRouters: HarRouter[] = []; - private _locatorHandlers = new Map(); + private _locatorHandlers = new Map any, times: number | undefined }>(); static from(page: channels.PageChannel): Page { return (page as any)._object; @@ -362,19 +362,36 @@ export class Page extends ChannelOwner implements api.Page return Response.fromNullable((await this._channel.reload({ ...options, waitUntil })).response); } - async addLocatorHandler(locator: Locator, handler: Function): Promise { + async addLocatorHandler(locator: Locator, handler: (locator: Locator) => any, options: { times?: number, allowStayingVisible?: boolean } = {}): Promise { if (locator._frame !== this._mainFrame) throw new Error(`Locator must belong to the main frame of this page`); - const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector }); - this._locatorHandlers.set(uid, handler); + if (options.times === 0) + return; + const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector, allowStayingVisible: options.allowStayingVisible }); + this._locatorHandlers.set(uid, { locator, handler, times: options.times }); } private async _onLocatorHandlerTriggered(uid: number) { + let remove = false; try { const handler = this._locatorHandlers.get(uid); - await handler?.(); + if (handler && handler.times !== 0) { + if (handler.times !== undefined) + handler.times--; + await handler.handler(handler.locator); + } + remove = handler?.times === 0; } finally { - this._wrapApiCall(() => this._channel.resolveLocatorHandlerNoReply({ uid }), true).catch(() => {}); + this._wrapApiCall(() => this._channel.resolveLocatorHandlerNoReply({ uid, remove }), true).catch(() => {}); + } + } + + async removeLocatorHandler(locator: Locator, handler: (locator: Locator) => any): Promise { + for (const [uid, data] of this._locatorHandlers) { + if (data.locator._equals(locator) && data.handler === handler) { + this._locatorHandlers.delete(uid); + await this._channel.unregisterLocatorHandlerNoReply({ uid }).catch(() => {}); + } } } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 2ec820711a..d060718c50 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1046,14 +1046,20 @@ scheme.PageGoForwardResult = tObject({ }); scheme.PageRegisterLocatorHandlerParams = tObject({ selector: tString, + allowStayingVisible: tOptional(tBoolean), }); scheme.PageRegisterLocatorHandlerResult = tObject({ uid: tNumber, }); scheme.PageResolveLocatorHandlerNoReplyParams = tObject({ uid: tNumber, + remove: tOptional(tBoolean), }); scheme.PageResolveLocatorHandlerNoReplyResult = tOptional(tObject({})); +scheme.PageUnregisterLocatorHandlerNoReplyParams = tObject({ + uid: tNumber, +}); +scheme.PageUnregisterLocatorHandlerNoReplyResult = tOptional(tObject({})); scheme.PageReloadParams = tObject({ timeout: tOptional(tNumber), waitUntil: tOptional(tType('LifecycleEvent')), diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index fb2e1f0468..d873d8d06a 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -138,12 +138,16 @@ export class PageDispatcher extends Dispatcher { - const uid = this._page.registerLocatorHandler(params.selector); + const uid = this._page.registerLocatorHandler(params.selector, params.allowStayingVisible); return { uid }; } async resolveLocatorHandlerNoReply(params: channels.PageResolveLocatorHandlerNoReplyParams, metadata: CallMetadata): Promise { - this._page.resolveLocatorHandler(params.uid); + this._page.resolveLocatorHandler(params.uid, params.remove); + } + + async unregisterLocatorHandlerNoReply(params: channels.PageUnregisterLocatorHandlerNoReplyParams, metadata: CallMetadata): Promise { + this._page.unregisterLocatorHandler(params.uid); } async emulateMedia(params: channels.PageEmulateMediaParams, metadata: CallMetadata): Promise { diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 4a49231b3b..73aa1f4dae 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -773,56 +773,61 @@ export class Frame extends SdkObject { throw new Error(`state: expected one of (attached|detached|visible|hidden)`); return controller.run(async progress => { progress.log(`waiting for ${this._asLocator(selector)}${state === 'attached' ? '' : ' to be ' + state}`); - const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async continuePolling => { - const resolved = await this.selectors.resolveInjectedForSelector(selector, options, scope); - progress.throwIfAborted(); - if (!resolved) { - if (state === 'hidden' || state === 'detached') - return null; - return continuePolling; - } - const result = await resolved.injected.evaluateHandle((injected, { info, root }) => { - const elements = injected.querySelectorAll(info.parsed, root || document); - const element: Element | undefined = elements[0]; - const visible = element ? injected.isVisible(element) : false; - let log = ''; - if (elements.length > 1) { - if (info.strict) - throw injected.strictModeViolationError(info.parsed, elements); - log = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`; - } else if (element) { - log = ` locator resolved to ${visible ? 'visible' : 'hidden'} ${injected.previewNode(element)}`; - } - return { log, element, visible, attached: !!element }; - }, { info: resolved.info, root: resolved.frame === this ? scope : undefined }); - const { log, visible, attached } = await result.evaluate(r => ({ log: r.log, visible: r.visible, attached: r.attached })); - if (log) - progress.log(log); - const success = { attached, detached: !attached, visible, hidden: !visible }[state]; - if (!success) { - result.dispose(); - return continuePolling; - } - if (options.omitReturnValue) { - result.dispose(); - return null; - } - const element = state === 'attached' || state === 'visible' ? await result.evaluateHandle(r => r.element) : null; - result.dispose(); - if (!element) - return null; - if ((options as any).__testHookBeforeAdoptNode) - await (options as any).__testHookBeforeAdoptNode(); - try { - return await element._adoptTo(await resolved.frame._mainContext()); - } catch (e) { - return continuePolling; - } - }); - return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise; + return await this.waitForSelectorInternal(progress, selector, options, scope); }, this._page._timeoutSettings.timeout(options)); } + async waitForSelectorInternal(progress: Progress, selector: string, options: types.WaitForElementOptions, scope?: dom.ElementHandle): Promise | null> { + const { state = 'visible' } = options; + const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async continuePolling => { + const resolved = await this.selectors.resolveInjectedForSelector(selector, options, scope); + progress.throwIfAborted(); + if (!resolved) { + if (state === 'hidden' || state === 'detached') + return null; + return continuePolling; + } + const result = await resolved.injected.evaluateHandle((injected, { info, root }) => { + const elements = injected.querySelectorAll(info.parsed, root || document); + const element: Element | undefined = elements[0]; + const visible = element ? injected.isVisible(element) : false; + let log = ''; + if (elements.length > 1) { + if (info.strict) + throw injected.strictModeViolationError(info.parsed, elements); + log = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`; + } else if (element) { + log = ` locator resolved to ${visible ? 'visible' : 'hidden'} ${injected.previewNode(element)}`; + } + return { log, element, visible, attached: !!element }; + }, { info: resolved.info, root: resolved.frame === this ? scope : undefined }); + const { log, visible, attached } = await result.evaluate(r => ({ log: r.log, visible: r.visible, attached: r.attached })); + if (log) + progress.log(log); + const success = { attached, detached: !attached, visible, hidden: !visible }[state]; + if (!success) { + result.dispose(); + return continuePolling; + } + if (options.omitReturnValue) { + result.dispose(); + return null; + } + const element = state === 'attached' || state === 'visible' ? await result.evaluateHandle(r => r.element) : null; + result.dispose(); + if (!element) + return null; + if ((options as any).__testHookBeforeAdoptNode) + await (options as any).__testHookBeforeAdoptNode(); + try { + return await element._adoptTo(await resolved.frame._mainContext()); + } catch (e) { + return continuePolling; + } + }); + return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise; + } + async dispatchEvent(metadata: CallMetadata, selector: string, type: string, eventInit: Object = {}, options: types.QueryOnSelectorOptions = {}, scope?: dom.ElementHandle): Promise { await this._callOnElementOnceMatches(metadata, selector, (injectedScript, element, data) => { injectedScript.dispatchEvent(element, data.type, data.eventInit); diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 2c3fd8f6dd..4bc8730b26 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -168,7 +168,7 @@ export class Page extends SdkObject { _video: Artifact | null = null; _opener: Page | undefined; private _isServerSideOnly = false; - private _locatorHandlers = new Map }>(); + private _locatorHandlers = new Map }>(); private _lastLocatorHandlerUid = 0; private _locatorHandlerRunningCounter = 0; @@ -432,20 +432,26 @@ export class Page extends SdkObject { }), this._timeoutSettings.navigationTimeout(options)); } - registerLocatorHandler(selector: string) { + registerLocatorHandler(selector: string, allowStayingVisible: boolean | undefined) { const uid = ++this._lastLocatorHandlerUid; - this._locatorHandlers.set(uid, { selector }); + this._locatorHandlers.set(uid, { selector, allowStayingVisible }); return uid; } - resolveLocatorHandler(uid: number) { + resolveLocatorHandler(uid: number, remove: boolean | undefined) { const handler = this._locatorHandlers.get(uid); + if (remove) + this._locatorHandlers.delete(uid); if (handler) { handler.resolved?.resolve(); handler.resolved = undefined; } } + unregisterLocatorHandler(uid: number) { + this._locatorHandlers.delete(uid); + } + async performLocatorHandlersCheckpoint(progress: Progress) { // Do not run locator handlers from inside locator handler callbacks to avoid deadlocks. if (this._locatorHandlerRunningCounter) @@ -460,7 +466,12 @@ export class Page extends SdkObject { if (handler.resolved) { ++this._locatorHandlerRunningCounter; progress.log(` found ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)}, intercepting action to run the handler`); - await this.openScope.race(handler.resolved).finally(() => --this._locatorHandlerRunningCounter); + const promise = handler.resolved.then(async () => { + progress.throwIfAborted(); + if (!handler.allowStayingVisible) + await this.mainFrame().waitForSelectorInternal(progress, handler.selector, { state: 'hidden' }); + }); + await this.openScope.race(promise).finally(() => --this._locatorHandlerRunningCounter); // Avoid side-effects after long-running operation. progress.throwIfAborted(); progress.log(` interception handler has finished, continuing`); diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 23eee3fe17..fbc4105513 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -1802,12 +1802,14 @@ export interface Page { * Things to keep in mind: * - When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as * a part of your normal test flow, instead of using - * [page.addLocatorHandler(locator, handler)](https://playwright.dev/docs/api/class-page#page-add-locator-handler). + * [page.addLocatorHandler(locator, handler[, options])](https://playwright.dev/docs/api/class-page#page-add-locator-handler). * - Playwright checks for the overlay every time before executing or retrying an action that requires an * [actionability check](https://playwright.dev/docs/actionability), or before performing an auto-waiting assertion check. When overlay * is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the * handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't * perform any actions, the handler will not be triggered. + * - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible + * anymore. You can opt-out of this behavior with `allowStayingVisible`. * - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. * If your handler takes too long, it might cause timeouts. * - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the @@ -1857,24 +1859,47 @@ export interface Page { * ``` * * An example with a custom callback on every actionability check. It uses a `` locator that is always visible, - * so the handler is called before every actionability check: + * so the handler is called before every actionability check. It is important to specify `allowStayingVisible`, + * because the handler does not hide the `` element. * * ```js * // Setup the handler. * await page.addLocatorHandler(page.locator('body'), async () => { * await page.evaluate(() => window.removeObstructionsForTestIfNeeded()); - * }); + * }, { allowStayingVisible: true }); * * // Write the test as usual. * await page.goto('https://example.com'); * await page.getByRole('button', { name: 'Start here' }).click(); * ``` * + * Handler takes the original locator as an argument. You can also automatically remove the handler after a number of + * invocations by setting `times`: + * + * ```js + * await page.addLocatorHandler(page.getByLabel('Close'), async locator => { + * await locator.click(); + * }, { times: 1 }); + * ``` + * * @param locator Locator that triggers the handler. * @param handler Function that should be run once `locator` appears. This function should get rid of the element that blocks actions * like click. + * @param options */ - addLocatorHandler(locator: Locator, handler: Function): Promise; + addLocatorHandler(locator: Locator, handler: ((locator: Locator) => Promise), options?: { + /** + * By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then + * Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of + * this behavior, so that overlay can stay visible after the handler has run. + */ + allowStayingVisible?: boolean; + + /** + * Specifies the maximum number of times this handler should be called. Unlimited by default. + */ + times?: number; + }): Promise; /** * Adds a `