mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-05 19:04:43 +03:00
feat(dialogs): auto-dismiss dialogs when there are no listeners (#5269)
This makes dialogs disappear and prevents stalling. Pros: - No need to worry about dialogs for most users. - Those that wait for a specific dialog still get to control it. Cons: - Those who use Playwright to show interactive browser will have to add an empty 'dialog' handler to prevent auto-dismiss. We do this in cli.
This commit is contained in:
parent
bbfbb1b2f7
commit
53ed35ef96
@ -60,6 +60,11 @@ with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
```
|
||||
|
||||
:::note
|
||||
Dialogs are dismissed automatically, unless there is a [`event: Page.dialog`] listener.
|
||||
When listener is present, it **must** either [`method: Dialog.accept`] or [`method: Dialog.dismiss`] the dialog - otherwise the page will [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and actions like click will never finish.
|
||||
:::
|
||||
|
||||
## async method: Dialog.accept
|
||||
|
||||
Returns when the dialog has been accepted.
|
||||
|
@ -171,8 +171,11 @@ except Error as e:
|
||||
## event: Page.dialog
|
||||
- type: <[Dialog]>
|
||||
|
||||
Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Playwright can respond
|
||||
to the dialog via [`method: Dialog.accept`] or [`method: Dialog.dismiss`] methods.
|
||||
Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** either [`method: Dialog.accept`] or [`method: Dialog.dismiss`] the dialog - otherwise the page will [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and actions like click will never finish.
|
||||
|
||||
:::note
|
||||
When no [`event: Page.dialog`] listeners are present, all dialogs are automatically dismissed.
|
||||
:::
|
||||
|
||||
## event: Page.domcontentloaded
|
||||
- type: <[Page]>
|
||||
@ -808,7 +811,7 @@ If the function passed to the [`method: Page.evaluate`] returns a [Promise], the
|
||||
for the promise to resolve and return its value.
|
||||
|
||||
If the function passed to the [`method: Page.evaluate`] returns a non-[Serializable] value, then
|
||||
[`method: Page.evaluate`] resolves to `undefined`. Playwright also supports transferring some
|
||||
[`method: Page.evaluate`] resolves to `undefined`. Playwright also supports transferring some
|
||||
additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
|
||||
|
||||
Passing argument to [`param: expression`]:
|
||||
|
@ -9,7 +9,7 @@ Playwright can interact with the web page dialogs such as [`alert`](https://deve
|
||||
|
||||
## alert(), confirm(), prompt() dialogs
|
||||
|
||||
You can register a dialog handler before the action that triggers the dialog to accept or decline it.
|
||||
By default, dialogs are auto-dismissed by Playwright, so you don't have to handle them. However, you can register a dialog handler before the action that triggers the dialog to accept or decline it.
|
||||
|
||||
```js
|
||||
page.on('dialog', dialog => dialog.accept());
|
||||
@ -27,7 +27,7 @@ page.click("button")
|
||||
```
|
||||
|
||||
:::note
|
||||
If your action, be it [`method: Page.click`], [`method: Page.evaluate`] or any other, results in a dialog, the action will stall until the dialog is handled. That's because dialogs in Web are modal and block further page execution until they are handled.
|
||||
[`event: Page.dialog`] listener **must handle** the dialog. Otherwise your action will stall, be it [`method: Page.click`], [`method: Page.evaluate`] or any other. That's because dialogs in Web are modal and block further page execution until they are handled.
|
||||
:::
|
||||
|
||||
As a result, following snippet will never resolve:
|
||||
@ -37,24 +37,24 @@ WRONG!
|
||||
:::
|
||||
|
||||
```js
|
||||
page.on('dialog', dialog => console.log(dialog.message()));
|
||||
await page.click('button'); // Will hang here
|
||||
page.on('dialog', dialog => dialog.accept())
|
||||
```
|
||||
|
||||
:::warn
|
||||
WRONG!
|
||||
:::
|
||||
|
||||
```python async
|
||||
page.on("dialog", lambda dialog: print(dialog.message))
|
||||
await page.click("button") # Will hang here
|
||||
page.on("dialog", lambda dialog: dialog.accept())
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.on("dialog", lambda dialog: print(dialog.message))
|
||||
page.click("button") # Will hang here
|
||||
page.on("dialog", lambda dialog: dialog.accept())
|
||||
```
|
||||
|
||||
:::note
|
||||
If there is no listener for [`event: Page.dialog`], all dialogs are automatically dismissed.
|
||||
:::
|
||||
|
||||
### API reference
|
||||
|
||||
- [`Dialog`]
|
||||
|
@ -284,6 +284,7 @@ async function launchContext(options: Options, headless: boolean): Promise<{ bro
|
||||
}
|
||||
|
||||
context.on('page', page => {
|
||||
page.on('dialog', () => {}); // Prevent dialogs from being automatically dismissed.
|
||||
page.on('close', () => {
|
||||
const hasPage = browser.contexts().some(context => context.pages().length > 0);
|
||||
if (hasPage)
|
||||
|
@ -116,7 +116,10 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
||||
this._channel.on('close', () => this._onClose());
|
||||
this._channel.on('console', ({ message }) => this.emit(Events.Page.Console, ConsoleMessage.from(message)));
|
||||
this._channel.on('crash', () => this._onCrash());
|
||||
this._channel.on('dialog', ({ dialog }) => this.emit(Events.Page.Dialog, Dialog.from(dialog)));
|
||||
this._channel.on('dialog', ({ dialog }) => {
|
||||
if (!this.emit(Events.Page.Dialog, Dialog.from(dialog)))
|
||||
dialog.dismiss().catch(() => {});
|
||||
});
|
||||
this._channel.on('domcontentloaded', () => this.emit(Events.Page.DOMContentLoaded, this));
|
||||
this._channel.on('download', ({ download }) => this.emit(Events.Page.Download, Download.from(download)));
|
||||
this._channel.on('fileChooser', ({ element, isMultiple }) => this.emit(Events.Page.FileChooser, new FileChooser(this, ElementHandle.from(element), isMultiple)));
|
||||
|
@ -62,9 +62,7 @@ it('should dismiss the confirm prompt', async ({page}) => {
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should be able to close context with open alert', (test, { browserName, platform }) => {
|
||||
test.fixme(browserName === 'webkit' && platform === 'darwin');
|
||||
}, async ({context}) => {
|
||||
it('should be able to close context with open alert', async ({context}) => {
|
||||
const page = await context.newPage();
|
||||
const alertPromise = page.waitForEvent('dialog');
|
||||
await page.evaluate(() => {
|
||||
@ -102,3 +100,14 @@ it('should handle multiple confirms', async ({page}) => {
|
||||
`);
|
||||
expect(await page.textContent('p')).toBe('Hello World');
|
||||
});
|
||||
|
||||
it('should auto-dismiss the prompt without listeners', async ({page}) => {
|
||||
const result = await page.evaluate(() => prompt('question?'));
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
|
||||
it('should auto-dismiss the alert without listeners', async ({page}) => {
|
||||
await page.setContent(`<div onclick="window.alert(123); window._clicked=true">Click me</div>`);
|
||||
await page.click('div');
|
||||
expect(await page.evaluate('window._clicked')).toBe(true);
|
||||
});
|
||||
|
72
types/types.d.ts
vendored
72
types/types.d.ts
vendored
@ -411,9 +411,14 @@ export interface Page {
|
||||
on(event: 'crash', listener: (page: Page) => void): this;
|
||||
|
||||
/**
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Playwright can respond
|
||||
* to the dialog via [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) methods.
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**
|
||||
* either [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) the dialog - otherwise the page will
|
||||
* [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
|
||||
* actions like click will never finish.
|
||||
*
|
||||
* > NOTE: When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#pageondialog) listeners are present, all
|
||||
* dialogs are automatically dismissed.
|
||||
*/
|
||||
on(event: 'dialog', listener: (dialog: Dialog) => void): this;
|
||||
|
||||
@ -580,9 +585,14 @@ export interface Page {
|
||||
once(event: 'crash', listener: (page: Page) => void): this;
|
||||
|
||||
/**
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Playwright can respond
|
||||
* to the dialog via [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) methods.
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**
|
||||
* either [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) the dialog - otherwise the page will
|
||||
* [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
|
||||
* actions like click will never finish.
|
||||
*
|
||||
* > NOTE: When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#pageondialog) listeners are present, all
|
||||
* dialogs are automatically dismissed.
|
||||
*/
|
||||
once(event: 'dialog', listener: (dialog: Dialog) => void): this;
|
||||
|
||||
@ -749,9 +759,14 @@ export interface Page {
|
||||
addListener(event: 'crash', listener: (page: Page) => void): this;
|
||||
|
||||
/**
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Playwright can respond
|
||||
* to the dialog via [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) methods.
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**
|
||||
* either [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) the dialog - otherwise the page will
|
||||
* [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
|
||||
* actions like click will never finish.
|
||||
*
|
||||
* > NOTE: When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#pageondialog) listeners are present, all
|
||||
* dialogs are automatically dismissed.
|
||||
*/
|
||||
addListener(event: 'dialog', listener: (dialog: Dialog) => void): this;
|
||||
|
||||
@ -918,9 +933,14 @@ export interface Page {
|
||||
removeListener(event: 'crash', listener: (page: Page) => void): this;
|
||||
|
||||
/**
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Playwright can respond
|
||||
* to the dialog via [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) methods.
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**
|
||||
* either [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) the dialog - otherwise the page will
|
||||
* [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
|
||||
* actions like click will never finish.
|
||||
*
|
||||
* > NOTE: When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#pageondialog) listeners are present, all
|
||||
* dialogs are automatically dismissed.
|
||||
*/
|
||||
removeListener(event: 'dialog', listener: (dialog: Dialog) => void): this;
|
||||
|
||||
@ -1087,9 +1107,14 @@ export interface Page {
|
||||
off(event: 'crash', listener: (page: Page) => void): this;
|
||||
|
||||
/**
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Playwright can respond
|
||||
* to the dialog via [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) methods.
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**
|
||||
* either [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) the dialog - otherwise the page will
|
||||
* [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
|
||||
* actions like click will never finish.
|
||||
*
|
||||
* > NOTE: When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#pageondialog) listeners are present, all
|
||||
* dialogs are automatically dismissed.
|
||||
*/
|
||||
off(event: 'dialog', listener: (dialog: Dialog) => void): this;
|
||||
|
||||
@ -2856,9 +2881,14 @@ export interface Page {
|
||||
waitForEvent(event: 'crash', optionsOrPredicate?: { predicate?: (page: Page) => boolean, timeout?: number } | ((page: Page) => boolean)): Promise<Page>;
|
||||
|
||||
/**
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Playwright can respond
|
||||
* to the dialog via [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) methods.
|
||||
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**
|
||||
* either [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) the dialog - otherwise the page will
|
||||
* [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
|
||||
* actions like click will never finish.
|
||||
*
|
||||
* > NOTE: When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#pageondialog) listeners are present, all
|
||||
* dialogs are automatically dismissed.
|
||||
*/
|
||||
waitForEvent(event: 'dialog', optionsOrPredicate?: { predicate?: (dialog: Dialog) => boolean, timeout?: number } | ((dialog: Dialog) => boolean)): Promise<Dialog>;
|
||||
|
||||
@ -8074,6 +8104,12 @@ export interface ConsoleMessage {
|
||||
* })();
|
||||
* ```
|
||||
*
|
||||
* > NOTE: Dialogs are dismissed automatically, unless there is a
|
||||
* [page.on('dialog')](https://playwright.dev/docs/api/class-page#pageondialog) listener. When listener is present, it
|
||||
* **must** either [dialog.accept([promptText])](https://playwright.dev/docs/api/class-dialog#dialogacceptprompttext) or
|
||||
* [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialogdismiss) the dialog - otherwise the page will
|
||||
* [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
|
||||
* actions like click will never finish.
|
||||
*/
|
||||
export interface Dialog {
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user