diff --git a/docs/examples/upload.js b/docs/examples/upload.js deleted file mode 100644 index d0346cd207..0000000000 --- a/docs/examples/upload.js +++ /dev/null @@ -1,34 +0,0 @@ -const { firefox } = require("playwright"); - -/** - * In this script, we will upload a file to a web page. - * - * Steps summary - * 1. Open the sample file upload at https://cgi-lib.berkeley.edu/ex/fup.html - * 2. Automate file upload with setInputFiles - */ - -(async () => { - // Launch a headless browser instance of chromium, webkit or firefox - const browser = await firefox.launch(); - - // Use the default browser context to create a new tab and navigate to URL - const page = await browser.newPage(); - await page.goto('https://cgi-lib.berkeley.edu/ex/fup.html'); - - // Get an element handle to the file upload input - const handle = await page.$('input[type="file"]'); - - // Use the setInputFiles API to upload this file. File paths are relative to - // the current working directory. It is also possible to upload multiple files - // or use base64 encoded data, instead of a file. See API docs. - // https://github.com/microsoft/playwright/blob/master/docs/api.md#elementhandlesetinputfilesfiles - await handle.setInputFiles('upload.js'); - - // Click on the form submit element - await page.click('input[type="submit"]'); - - // Take a screenshot of the uploaded state and close the browser - await page.screenshot({ path: 'uploaded.png' }); - await browser.close(); -})(); diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index 22f91462eb..0c6b29b407 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -716,7 +716,7 @@ Returns storage state for this browser context, contains current cookies and loc - `path` <[path]> The file path to save the storage state to. If [`option: path`] is a relative path, then it is resolved relative to -[current working directory](https://nodejs.org/api/process.html#process_process_cwd). If no path is provided, storage +current working directory. If no path is provided, storage state is still returned, but won't be saved to the disk. ## async method: BrowserContext.unroute diff --git a/docs/src/api/class-chromiumbrowsercontext.md b/docs/src/api/class-chromiumbrowsercontext.md index 0f803dc63c..08e96c53d8 100644 --- a/docs/src/api/class-chromiumbrowsercontext.md +++ b/docs/src/api/class-chromiumbrowsercontext.md @@ -8,12 +8,10 @@ const backgroundPage = await context.waitForEvent('backgroundpage'); ``` ```python async -# FIXME background_page = await context.wait_for_event("backgroundpage") ``` ```python sync -# FIXME background_page = context.wait_for_event("backgroundpage") ``` diff --git a/docs/src/api/class-dialog.md b/docs/src/api/class-dialog.md index 1adaa7844b..406febe549 100644 --- a/docs/src/api/class-dialog.md +++ b/docs/src/api/class-dialog.md @@ -43,7 +43,6 @@ asyncio.run(main()) ``` ```python sync -# FIXME from playwright.sync_api import sync_playwright def handle_dialog(dialog): diff --git a/docs/src/api/class-elementhandle.md b/docs/src/api/class-elementhandle.md index 6a0320b05d..7e464a1063 100644 --- a/docs/src/api/class-elementhandle.md +++ b/docs/src/api/class-elementhandle.md @@ -154,7 +154,6 @@ expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))). ``` ```python async -# FIXME feed_handle = await page.query_selector(".feed") assert await feed_handle.eval_on_selector_all(".tweet", "nodes => nodes.map(n => n.innerText)") == ["hello!", "hi!"] ``` @@ -577,7 +576,6 @@ handle.select_option(value=["red", "green", "blue"]) ``` ```python sync -# FIXME # single selection matching the value handle.select_option("blue") # single selection matching both the value and the label diff --git a/docs/src/api/class-filechooser.md b/docs/src/api/class-filechooser.md index ccfc8a2b99..6adc42d9d7 100644 --- a/docs/src/api/class-filechooser.md +++ b/docs/src/api/class-filechooser.md @@ -3,17 +3,25 @@ [FileChooser] objects are dispatched by the page in the [`event: Page.filechooser`] event. ```js -page.on('filechooser', async (fileChooser) => { - await fileChooser.setFiles('/tmp/myfile.pdf'); -}); +const [fileChooser] = await Promise.all([ + page.waitForEvent('filechooser'), + page.click('upload') +]); +await fileChooser.setFiles('myfile.pdf'); ``` ```python async -page.on("filechooser", lambda file_chooser: file_chooser.set_files("/tmp/myfile.pdf")) +async with page.expect_file_chooser() as fc_info: + await page.click("upload") +file_chooser = await fc_info.value +await file_chooser.set_files("myfile.pdf") ``` ```python sync -page.on("filechooser", lambda file_chooser: file_chooser.set_files("/tmp/myfile.pdf")) +with page.expect_file_chooser() as fc_info: + page.click("upload") +file_chooser = fc_info.value +file_chooser.set_files("myfile.pdf") ``` ## method: FileChooser.element diff --git a/docs/src/api/class-frame.md b/docs/src/api/class-frame.md index 1ed7abc7b2..5485606f86 100644 --- a/docs/src/api/class-frame.md +++ b/docs/src/api/class-frame.md @@ -505,7 +505,6 @@ aWindowHandle; // Handle for the window object. ``` ```python async -# FIXME a_window_handle = await frame.evaluate_handle("Promise.resolve(window)") a_window_handle # handle for the window object. ``` diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index b868df5006..d2c4bc234e 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -925,7 +925,6 @@ aWindowHandle; // Handle for the window object. ``` ```python async -# FIXME a_window_handle = await page.evaluate_handle("Promise.resolve(window)") a_window_handle # handle for the window object. ``` diff --git a/docs/src/auth.md b/docs/src/auth.md index dea1fb7e9d..95b689ecef 100644 --- a/docs/src/auth.md +++ b/docs/src/auth.md @@ -112,10 +112,26 @@ storage_state = json.loads(os.environ["STORAGE"]) context = browser.new_context(storage_state=storage_state) ``` -### Session storage +Logging in via the UI and then reusing authentication state can be combined to +implement **login once and run multiple scenarios**. The lifecycle looks like: -Session storage ([`window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)) -is specific to a particular domain. Playwright does not provide API to persist session storage, but the following snippet can be used to save/load session storage. +1. Run tests (for example, with `npm run test`). +1. Login via UI and retrieve authentication state. + * In Jest, this can be executed in [`globalSetup`](https://jestjs.io/docs/en/configuration#globalsetup-string). +1. In each test, load authentication state in `beforeEach` or `beforeAll` step. + +This approach will also **work in CI environments**, since it does not rely on any external state. + +### API reference +- [`method: BrowserContext.storageState`] +- [`method: Browser.newContext`] + +## Session storage + +Rarely, [session storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) is used for storing information +associated with the logged-in state. Session storage is specific to a particular domain and is not persisted across page loads. +Playwright does not provide API to persist session storage, but the following snippet can be used to +save/load session storage. ```js // Get session storage and store as env variable @@ -170,24 +186,6 @@ context.add_init_script(storage => { }, session_storage) ``` -### Lifecycle - -Logging in via the UI and then reusing authentication state can be combined to -implement **login once and run multiple scenarios**. The lifecycle looks like: - -1. Run tests (for example, with `npm run test`). -1. Login via UI and retrieve authentication state. - * In Jest, this can be executed in [`globalSetup`](https://jestjs.io/docs/en/configuration#globalsetup-string). -1. In each test, load authentication state in `beforeEach` or `beforeAll` step. - -This approach will also **work in CI environments**, since it does not rely -on any external state. - -### Example - -[This example script](https://github.com/microsoft/playwright/blob/master/docs/examples/authentication.js) logs in -on GitHub.com with Chromium, and then reuses the logged in storage state in WebKit. - ### API reference - [`method: BrowserContext.storageState`] - [`method: Browser.newContext`] @@ -195,6 +193,7 @@ on GitHub.com with Chromium, and then reuses the logged in storage state in WebK - [`method: BrowserContext.addInitScript`] ## Multi-factor authentication + Accounts with multi-factor authentication (MFA) cannot be fully automated, and need manual intervention. Persistent authentication can be used to partially automate MFA scenarios. diff --git a/docs/src/input.md b/docs/src/input.md index 5677b91129..cbc355dcc5 100644 --- a/docs/src/input.md +++ b/docs/src/input.md @@ -429,6 +429,8 @@ Note that you still need to specify the capital `A` in `Shift-A` to produce the ## Upload files +You can select input files for upload using the [`method: Page.setInputFiles`] method. It expects first argument to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) with the type `"file"`. Multiple files can be passed in the array. If some of the file paths are relative, they are resolved relative to the current working directory. Empty array clears the selected files. + ```js // Select one file await page.setInputFiles('input#upload', 'myfile.pdf'); @@ -485,14 +487,33 @@ page.set_input_files( ) ``` -You can select input files for upload using the [`method: Page.setInputFiles`] method. It expects first argument to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) with the type `"file"`. Multiple files can be passed in the array. If some of the file paths are relative, they are resolved relative to the [current working directory](https://nodejs.org/api/process.html#process_process_cwd). Empty array clears the selected files. +If you don't have input element in hand (it is created dynamically), you can handle the [`event: Page.filechooser`] event +or use a corresponding waiting method upon your action: -#### Example +```js +const [fileChooser] = await Promise.all([ + page.waitForEvent('filechooser'), + page.click('upload') +]); +await fileChooser.setFiles('myfile.pdf'); +``` -[This script](https://github.com/microsoft/playwright/blob/master/utils/docs/examples/upload.js) uploads a file to an `input` element that accepts file uploads. +```python async +async with page.expect_file_chooser() as fc_info: + await page.click("upload") +file_chooser = await fc_info.value +await file_chooser.set_files("myfile.pdf") +``` + +```python sync +with page.expect_file_chooser() as fc_info: + page.click("upload") +file_chooser = fc_info.value +file_chooser.set_files("myfile.pdf") +``` ### API reference - +- [FileChooser] - [`method: Page.setInputFiles`] - [`method: Frame.setInputFiles`] - [`method: ElementHandle.setInputFiles`] diff --git a/docs/src/navigations.md b/docs/src/navigations.md index 0533abfba5..a58eb2af01 100644 --- a/docs/src/navigations.md +++ b/docs/src/navigations.md @@ -324,7 +324,6 @@ await page.screenshot() ``` ```python sync -# FIXME page.goto("http://example.com") page.wait_for_function("() => window.amILoadedYet()") # Ready to take a screenshot, according to the page itself. diff --git a/types/types.d.ts b/types/types.d.ts index cabff9d189..2f2b8ac754 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -5070,9 +5070,8 @@ export interface BrowserContext { */ storageState(options?: { /** - * The file path to save the storage state to. If `path` is a relative path, then it is resolved relative to - * [current working directory](https://nodejs.org/api/process.html#process_process_cwd). If no path is provided, storage - * state is still returned, but won't be saved to the disk. + * The file path to save the storage state to. If `path` is a relative path, then it is resolved relative to current + * working directory. If no path is provided, storage state is still returned, but won't be saved to the disk. */ path?: string; }): Promise<{ @@ -8082,9 +8081,11 @@ export interface Download { * [page.on('filechooser')](https://github.com/microsoft/playwright/blob/master/docs/api.md#pageonfilechooser) event. * * ```js - * page.on('filechooser', async (fileChooser) => { - * await fileChooser.setFiles('/tmp/myfile.pdf'); - * }); + * const [fileChooser] = await Promise.all([ + * page.waitForEvent('filechooser'), + * page.click('upload') + * ]); + * await fileChooser.setFiles('myfile.pdf'); * ``` * */