Skip to content

Commit

Permalink
docs: add dialogs and downloads docs (#5042)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Jan 16, 2021
1 parent 2db02c9 commit 1fc02e8
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 64 deletions.
15 changes: 14 additions & 1 deletion docs/src/actionability.md
Expand Up @@ -32,7 +32,20 @@ Some actions like [`method: Page.click`] support `force` option that disables no
| textContent | Yes | - | - | - | - | - |
| type | Yes | - | - | - | - | - |

<br/>
You can check the actionability state of the element using one of the following methods:

- [`method: ElementHandle.isChecked`]
- [`method: ElementHandle.isDisabled`]
- [`method: ElementHandle.isEditable`]
- [`method: ElementHandle.isEnabled`]
- [`method: ElementHandle.isHidden`]
- [`method: ElementHandle.isVisible`]
- [`method: Page.isChecked`]
- [`method: Page.isDisabled`]
- [`method: Page.isEditable`]
- [`method: Page.isEnabled`]
- [`method: Page.isHidden`]
- [`method: Page.isVisible`]

### Visible

Expand Down
94 changes: 94 additions & 0 deletions docs/src/dialogs.md
@@ -0,0 +1,94 @@
---
id: dialogs
title: "Dialogs"
---

Playwright can interact with the web page dialogs such as [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert), [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm), [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) as well as [`beforeunload`](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event) confirmation.

<!-- TOC -->

## alert(), confirm(), prompt() dialogs

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());
await page.click('button');
```

```python async
page.on("dialog", lambda dialog: dialog.accept())
await page.click("button")
```

```python sync
page.on("dialog", lambda dialog: dialog.accept())
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.
:::

As a result, following snippet will never resolve:

:::warn
WRONG!
:::

```js
await page.click('button'); // Will hang here
page.on('dialog', dialog => dialog.accept())
```

:::warn
WRONG!
:::

```python async
await page.click("button") # Will hang here
page.on("dialog", lambda dialog: dialog.accept())
```

```python sync
page.click("button") # Will hang here
page.on("dialog", lambda dialog: dialog.accept())
```

#### API reference

- [`Dialog`]
- [`method: Dialog.accept`]
- [`method: Dialog.dismiss`]

## beforeunload dialog

When [`method: Page.close`] is invoked with the truthy [`option: runBeforeUnload`] value, it page runs its unload handlers. This is the only case when [`method: Page.close`] does not wait for the page to actually close, because it might be that the page stays open in the end of the operation.

You can register a dialog handler to handle the beforeunload dialog yourself:

```js
page.on('dialog', async dialog => {
assert(dialog.type() === 'beforeunload');
await dialog.dismiss();
});
await page.close({runBeforeUnload: true});
```

```python async
async def handle_dialog(dialog):
assert dialog.type == 'beforeunload'
await dialog.dismiss()

page.on('dialog', lambda: handle_dialog)
await page.close(run_before_unload=True)
```

```python sync
def handle_dialog(dialog):
assert dialog.type == 'beforeunload'
dialog.dismiss()

page.on('dialog', lambda: handle_dialog)
page.close(run_before_unload=True)
```
77 changes: 77 additions & 0 deletions docs/src/downloads.md
@@ -0,0 +1,77 @@
---
id: downloads
title: "Downloads"
---

:::note
For uploading files, see the [uploading files](./input.md#upload-files) section.
:::

For every attachment downloaded by the page, [`event: Page.download`] event is emitted. If you create a browser context
with the [`option: acceptDownloads`] set, all these attachments are going to be downloaded into a temporary folder. You
can obtain the download url, file system path and payload stream using the [Download] object from the event.

You can specify where to persist downloaded files using the [`option: downloadsPath`] option in [`method: BrowserType.launch`].

:::note
Unless [`option: downloadsPath`] is set, downloaded files are deleted when the browser context that produced them is closed.
:::

Here is the simplest way to handle the file download:

```js
const [ download ] = await Promise.all([
// Start waiting for the download
page.waitForEvent('download'),
// Perform the action that initiates download
page.click('button#delayed-download')
]);
// Wait for the download process to complete
const path = await download.path();
```

```python async
# Start waiting for the download
async with page.expect_download() as download_info:
# Perform the action that initiates download
await page.click("button#delayed-download")
download = await download_info.value
# Wait for the download process to complete
path = await download.path()
```

```python sync
# Start waiting for the download
with page.expect_download() as download_info:
# Perform the action that initiates download
page.click("button#delayed-download")
download = download_info.value
# Wait for the download process to complete
path = download.path()
```

#### Variations

If you have no idea what initiates the download, you can still handle the event:

```js
page.on('download', download => download.path().then(console.log));
```

```python async
async def handle_download(download):
print(await download.path())
page.on("download", handle_download)
```

```python sync
page.on("download", lambda download: print(download.path()))
```

Note that handling the event forks the control flow and makes script harder to follow. Your scenario might end while you
are downloading a file since your main control flow is not awaiting for this operation to resolve.

#### API reference
- [Download]
- [`event: Page.download`]
- [`method: Page.waitForEvent`]
2 changes: 1 addition & 1 deletion docs/src/emulation.md
@@ -1,6 +1,6 @@
---
id: emulation
title: "Device and environment emulation"
title: "Emulation"
---

Playwright allows overriding various parameters of the device where the browser is running:
Expand Down
62 changes: 0 additions & 62 deletions docs/src/network.md
Expand Up @@ -43,68 +43,6 @@ page.goto("https://example.com")
#### API reference
- [`method: Browser.newContext`]

<br/>

## Handle file downloads

```js
const [ download ] = await Promise.all([
page.waitForEvent('download'), // <-- start waiting for the download
page.click('button#delayed-download') // <-- perform the action that directly or indirectly initiates it
]);
const path = await download.path();
```

```python async
# Start waiting for the download
async with page.expect_download() as download_info:
# Perform the action that directly or indirectly initiates it
await page.click("button#delayed-download")
download = await download_info.value
path = await download.path()
```

```python sync
# Start waiting for the download
with page.expect_download() as download_info:
# Perform the action that directly or indirectly initiates it
page.click("button#delayed-download")
download = download_info.value
path = download.path()
```

For every attachment downloaded by the page, [`event: Page.download`] event is emitted. If you create a browser context
with the [`option: acceptDownloads`] set, all these attachments are going to be downloaded into a temporary folder. You
can obtain the download url, file system path and payload stream using the [Download] object from the event.

#### Variations

If you have no idea what initiates the download, you can still handle the event:

```js
page.on('download', download => download.path().then(console.log));
```

```python async
async def handle_download(download):
print(await download.path())
page.on("download", handle_download)
```

```python sync
page.on("download", lambda download: print(download.path()))
```

Note that handling the event forks the control flow and makes script harder to follow. Your scenario might end while you
are downloading a file since your main control flow is not awaiting for this operation to resolve.

#### API reference
- [Download]
- [`event: Page.download`]
- [`method: Page.waitForEvent`]

<br/>

## Network events

You can monitor all the requests and responses:
Expand Down

0 comments on commit 1fc02e8

Please sign in to comment.