diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index aa98489296176..d822d9aa504b4 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -1495,6 +1495,29 @@ its geolocation. Whether to emulate network being offline for the browser context. + +## async method: BrowserContext.setStorageState +* since: v1.55 + +Resets storage state in the context by clearing cookies, cache and storage, and then applying the new storage state. + +### param: BrowserContext.setStorageState.storageState = %%-js-python-context-option-storage-state-%% +* since: v1.55 + +### param: BrowserContext.setStorageState.storageState = %%-csharp-java-context-option-storage-state-%% +* since: v1.55 + + +## async method: BrowserContext.setStorageStatePath +* since: v1.55 +* langs: csharp, java + +Resets storage state in the context by clearing cookies, cache and storage, and then applying the new storage state from a file. + +### param: BrowserContext.setStorageStatePath.storageStatePath = %%-csharp-java-context-option-storage-state-path-%% +* since: v1.55 + + ## async method: BrowserContext.storageState * since: v1.8 - returns: <[Object]> diff --git a/docs/src/getting-started-vscode-js.md b/docs/src/getting-started-vscode-js.md index 03e29ff2e9660..7cdbeda3be720 100644 --- a/docs/src/getting-started-vscode-js.md +++ b/docs/src/getting-started-vscode-js.md @@ -7,229 +7,166 @@ import LiteYouTube from '@site/src/components/LiteYouTube'; ## Introduction -Playwright Test was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation of Google Chrome for Android and Mobile Safari. - -Get started by installing Playwright and generating a test to see it in action. Alternatively you can also get started and run your tests using the [CLI](./intro.md). +The Playwright VS Code extension brings the power of Playwright Test directly into your editor, allowing you to run, debug, and generate tests with a seamless UI-driven experience. This guide will walk you through setting up the extension and using its core features to supercharge your end-to-end testing workflow. -## Installation - -Playwright has a VS Code extension which is available when testing with Node.js. Install [it from the VS Code marketplace](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) or from the extensions tab in VS Code. - -![VS Code extension for Playwright](https://github.com/microsoft/playwright/assets/13063165/cab54568-3168-4b3f-bf3d-854976594903) - -Once installed, open the command panel and type: - -```bash -Install Playwright -``` - -![install playwright](https://github.com/microsoft/playwright/assets/13063165/14e91050-24ab-4ff1-a37b-57d7c15e5c35) +## Prerequisites +Before you begin, make sure you have the following installed: +- [Node.js](https://nodejs.org/) (LTS version recommended) +- [Visual Studio Code](https://code.visualstudio.com/) -Select **Test: Install Playwright** and Choose the browsers you would like to run your tests on. These can be later configured in the [playwright.config](./test-configuration.md) file. You can also choose if you would like to have a GitHub Actions setup to [run your tests on CI](./ci-intro.md). +## Getting Started -![choose browsers](https://github.com/microsoft/playwright/assets/13063165/c9e8a25a-e9e8-4419-aeb5-1b8ba58bd71d) +### Installation & Setup -### Opening the testing sidebar +1. **Install the Extension**: Open the Extensions view in VS Code (`Ctrl+Shift+X` or `Cmd+Shift+X`) and search for "Playwright". [Install the official extension from Microsoft](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). -The testing sidebar can be opened by clicking on the testing icon in the activity bar. This will give you access to the test explorer, which will show you all the tests in your project as well as the Playwright sidebar which includes projects, settings, tools and setup. -![Testing Sidebar](https://github.com/microsoft/playwright/assets/13063165/d203fe83-6015-4e7a-b816-35d373906b24) +![install playwright extension](./images/getting-started/vscode-extension.png) -## Running tests - -You can run a single test by clicking the green triangle next to your test block to run your test. Playwright will run through each line of the test and when it finishes you will see a green tick next to your test block as well as the time it took to run the test. - - -![run a single test](https://github.com/microsoft/playwright/assets/13063165/69dbccfc-4e9f-40e7-bcdf-7d5c5a11f988) +1. **Install Playwright**: Once the extension is installed, open the Command Palette (`Ctrl+Shift+P` or `Cmd+Shift+P`) and run the **Test: Install Playwright** command. +![install playwright](./images/getting-started/install-playwright.png) -### Run tests and show browsers +3. **Select Browsers**: Choose the browsers you want for your tests (e.g., Chromium, Firefox, WebKit). You can also add a GitHub Actions workflow to run tests in CI. These settings can be changed later in your `playwright.config.ts` file. -You can also run your tests and show the browsers by selecting the option **Show Browsers** in the testing sidebar. Then when you click the green triangle to run your test the browser will open and you will visually see it run through your test. Leave this selected if you want browsers open for all your tests or uncheck it if you prefer your tests to run in headless mode with no browser open. +![install browsers](./images/getting-started/install-browsers.png) -![show browsers while running tests](https://github.com/microsoft/playwright/assets/13063165/9f231530-0c43-466a-b944-8cf5102f714a) +### Opening the Testing Sidebar -Use the **Close all browsers** button to close all browsers. +Click the **Testing icon** in the VS Code Activity Bar to open the Test Explorer. Here, you'll find your tests, as well as the Playwright sidebar for managing projects, tools, and settings. -### View and run all tests +![Testing Sidebar](./images/getting-started/testing-sidebar.png) -View all tests in the testing sidebar and extend the tests by clicking on each test. Tests that have not been run will not have the green check next to them. Run all tests by clicking on the white triangle as you hover over the tests in the testing sidebar. +## Core Features -![run all tests](https://github.com/microsoft/playwright/assets/13063165/348e18ff-f819-4caa-8f7e-f16c20724f56) +### Running Your Tests -### Running tests on multiple browsers - -The first section in the Playwright sidebar is the projects section. Here you can see all your projects as defined in your Playwright config file. The default config when installing Playwright gives you 3 projects, Chromium, Firefox and WebKit. The first project is selected by default. - -![Projects section in VS Code extension](https://github.com/microsoft/playwright/assets/13063165/58fedea6-a2b9-4942-b2c7-2f3d482210cf) - -To run tests on multiple projects, select each project by checking the checkboxes next to the project name. Then when you run your tests from the sidebar or by pressing the play button next to the test name, the tests will run on all the selected projects. - -![Selecting projects to run tests on](https://github.com/microsoft/playwright/assets/13063165/6dc86ef4-6097-481c-9cab-b6e053ec7ea6) - -You can also individually run a test on a specific project by clicking the grey play button next to the project name of the test. - -![Running a test on a specific project](https://github.com/microsoft/playwright/assets/13063165/d29a27ab-07b5-4ca6-b4d7-1ad6d44bf222) - -### Run tests with trace viewer + -For a better developer experience you can run your tests with the **Show Trace Viewer** option. +- **Run a Single Test**: Click the green "play" icon next to any test to run it. The play button will change to a green checkmark if the test passes or a red X if the test fails. You'll be able to see how long the test took to run displayed next to the test name. Additionally, the Test Results panel will automatically open at the bottom of VS Code, showing a summary of the test execution including how many tests ran, how many passed, failed, or were skipped, along with the total execution time. -![run tests with trace viewer](https://github.com/microsoft/playwright/assets/13063165/fab8efa6-d5ef-496d-876d-c99e94e6a6b3) +![run a single test](./images/getting-started/run-single-test.png) -This will open up a full trace of your test where you can step through each action of your tests, explore the timeline, source code and more. +- **Run All Tests**: You can run all tests at different levels. Click the play icon next to a specific test file to run all tests within that file, or click the play icon at the very top of the Test Explorer to run all tests across your entire project. -![trace viewer](https://github.com/microsoft/playwright/assets/13063165/ee31a4fe-c786-4d4b-887e-2dcecacfba2c) +![run all tests](./images/getting-started/run-all-tests.png) -To learn more about the trace viewer see our [Trace Viewer guide](./trace-viewer.md). +- **Run on Multiple Browsers**: In the Playwright sidebar, check the boxes for the projects (browsers) you want to test against. Projects in Playwright represent different browser configurations - each project typically corresponds to a specific browser (like Chromium, Firefox, or WebKit) with its own settings such as viewport size, device emulation, or other browser-specific options. When you run a test, it will execute across all selected projects, allowing you to verify your application works consistently across different browsers and configurations. +![Selecting projects to run tests on](./images/getting-started/select-projects.png) +- **Show Browser**: To watch your tests execute in a live browser window, enable the **Show Browser** option in the sidebar. Disable it to run in headless mode (where tests run in the background without opening a visible browser window). -## Debugging tests +![show browsers while running tests](./images/getting-started/show-browser.png) -With the VS Code extension you can debug your tests right in VS Code see error messages, create breakpoints and live debug your tests. +### Debugging Your Tests -### Error messages - -If your test fails VS Code will show you error messages right in the editor showing what was expected, what was received as well as a complete call log. +The VS Code extension provides powerful debugging tools to help you identify and fix issues in your tests. You can set breakpoints, inspect variables, view detailed error messages, get AI-powered suggestions to resolve test failures, and use the comprehensive trace viewer to analyze test execution step-by-step. -![error messaging in vs code](https://github.com/microsoft/playwright/assets/13063165/3b8af12a-4805-4573-9d38-92055a0a7e75) +- **Using Breakpoints**: Set a breakpoint by clicking in the gutter next to a line number. Right-click the test and select **Debug Test**. The test will pause at your breakpoint, allowing you to inspect variables and step through the code. -### Live debugging + ![setting debug mode](./images/getting-started/debug-mode.png) -You can debug your test live in VS Code. After running a test with the `Show Browser` option checked, click on any of the locators in VS Code and it will be highlighted in the Browser window. Playwright will highlight it if it exists and show you if there is more than one result +- **Live Debugging**: With **Show Browsers** enabled, click on a locator in your code. Playwright will highlight the corresponding element in the browser, making it easy to verify locators. -![live debugging in vs code](https://github.com/microsoft/playwright/assets/13063165/7d236ebb-3d2d-4384-b73d-32a2b4e33b9e) + ![live debugging in vs code](./images/getting-started/live-debugging.png) -You can also edit the locators in VS Code and Playwright will show you the changes live in the browser window. +- **Viewing Error Messages**: If a test fails, the extension displays detailed error messages, including the expected vs. received values and a full call log, directly in the editor. -### Run in debug mode +![error messaging in vs code](./images/getting-started/error-messaging.png) -To set a breakpoint click next to the line number where you want the breakpoint to be until a red dot appears. Run the tests in debug mode by right clicking on the line next to the test you want to run. +- **Fix with AI**: When a test fails, click the sparkle icon next to the error to get an AI-powered fix suggestion from Copilot. Copilot analyzes the error and suggests a code change to resolve the issue. -![setting debug mode](https://github.com/microsoft/playwright/assets/13063165/31640629-efac-4cc7-b8b0-80ae18a3af83) +![fix with ai in vs code](./images/getting-started/fix-with-ai.png) -A browser window will open and the test will run and pause at where the breakpoint is set. You can step through the tests, pause the test and rerun the tests from the menu in VS Code. +- **Debugging with Trace Viewer**: For comprehensive debugging, enable the **Show Trace Viewer** option in the Playwright sidebar. When your test finishes, a detailed trace will automatically open, providing you with a complete timeline of your test execution. The trace viewer is particularly useful for: + - **Step-by-step analysis**: Navigate through each action your test performed with precise timestamps + - **DOM inspection**: View DOM snapshots at any point during test execution to see exactly what the page looked like + - **Network monitoring**: Examine all network requests and responses that occurred during the test + - **Console logs**: Access all console messages and errors from the browser + - **Source mapping**: Jump directly to the source code that executed each action + - **Visual debugging**: See screenshots and understand what the user would have seen at each step -![running in debug mode](https://github.com/microsoft/playwright/assets/13063165/b96a9f50-0f4d-49f9-a3d8-f093980a5673) + The trace viewer is especially valuable when debugging flaky tests or understanding complex user interactions. -![choosing a profile for debugging](https://github.com/microsoft/playwright/assets/13063165/48c1b428-8dd2-4229-9eb5-24f7168db834) +![trace viewer debugging](./images/getting-started/trace-viewer-debug.png) -To learn more about debugging, see [Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging). +To learn more, see our [Trace Viewer guide](./trace-viewer.md). -### Debug with trace viewer +### Generating Tests with CodeGen -For a better developer experience you can debug your tests with the **Show Trace Viewer** option. - -![run tests with trace viewer](https://github.com/microsoft/playwright/assets/13063165/fab8efa6-d5ef-496d-876d-c99e94e6a6b3) - -This will open up a full trace of your test where you can step through each action and see what happened before and after the action. You can also inspect the DOM snapshot, see console logs, network requests, the source code and more. - -![trace viewer](https://github.com/microsoft/playwright/assets/13063165/ee31a4fe-c786-4d4b-887e-2dcecacfba2c) - -To learn more about the trace viewer see our [Trace Viewer guide](./trace-viewer.md). - -## Generating tests - -CodeGen will auto generate your tests for you as you perform actions in the browser and is a great way to quickly get started. The viewport for the browser window is set to a specific width and height. See the [configuration guide](./test-configuration.md) to change the viewport or emulate different environments. +CodeGen is Playwright's powerful test generation tool that automatically creates test code by recording your interactions with a web page. Instead of writing tests from scratch, you can simply navigate through your application while CodeGen captures your actions and converts them into reliable test code with proper locators and assertions. -### Record a new test - -To record a test click on the **Record new** button from the Testing sidebar. This will create a `test-1.spec.ts` file as well as open up a browser window. In the browser go to the URL you wish to test and start clicking around. Playwright will record your actions and generate the test code directly in VS Code. You can also generate assertions by choosing one of the icons in the toolbar and then clicking on an element on the page to assert against. The following assertions can be generated: - * `'assert visibility'` to assert that an element is visible - * `'assert text'` to assert that an element contains specific text - * `'assert value'` to assert that an element has a specific value - -Once you are done recording click the **cancel** button or close the browser window. You can then inspect your `test-1.spec.ts` file and see your generated test. - - -![record a new test](https://github.com/microsoft/playwright/assets/13063165/0407f112-e1cd-41e7-a05d-ae64e24d27ed) - -### Record at cursor - -To record from a specific point in your test file click the **Record at cursor** button from the Testing sidebar. This generates actions into the existing test at the current cursor position. You can run the test, position the cursor at the end of the test and continue generating the test. - -![record at cursor](https://github.com/microsoft/playwright/assets/13063165/96933ea1-4c84-453a-acd7-22b4d3bde185) - -### Picking a locator - -Pick a [locator](./locators.md) and copy it into your test file by clicking the **Pick locator** button form the testing sidebar. Then in the browser click the element you require and it will now show up in the **Pick locator** box in VS Code. Press 'enter' on your keyboard to copy the locator into the clipboard and then paste anywhere in your code. Or press 'escape' if you want to cancel. - - -![pick locators](https://github.com/microsoft/playwright/assets/13063165/9a1b2da9-9ac7-4def-a9e0-f94770364fc2) - -Playwright will look at your page and figure out the best locator, prioritizing [role, text and test id locators](./locators.md). If the generator finds multiple elements matching the locator, it will improve the locator to make it resilient and uniquely identify the target element, so you don't have to worry about failing tests due to locators. - -## Project Dependencies - -You can use [project dependencies](./test-projects.md) to run tests that depend on other tests. This is useful for **setup** tests such as logging in to a website. - -### Running setup tests - -To run your setup tests select the **setup** project, as defined in your configuration file, from the project section in the Playwright sidebar. This will give you access to the **setup** tests in the test explorer. - -![setup tests in vscode](https://github.com/microsoft/playwright/assets/13063165/7a9eccd5-a5b3-431f-9eff-9b2971501e07) +- **Record a New Test**: Click **Record new** in the sidebar. A browser window will open. As you interact with the page, Playwright will automatically generate the test code. You can also generate assertions from the recording toolbar. -When you run a test that depends on the **setup** tests, the **setup** test will run first. Each time you run the test, the **setup** test will run again. +![record a new test](./images/getting-started/record-new-test.png) -![running setup tests in vscode](https://github.com/microsoft/playwright/assets/13063165/a54b3868-3f9f-4e74-8d42-a93443f099fc) +- **Record at Cursor**: Place your cursor inside an existing test and click **Record at cursor** to add new actions at that specific point. +![record at cursor](./images/getting-started/record-at-cursor.png) -### Running setup tests only once +- **Pick a Locator**: Use the **Pick locator** tool to click on any element in the opened browser. Playwright will determine the best locator and copy it to your clipboard, ready to be pasted into your code. -To run the **setup** test only once, deselect it from the projects section in the Playwright sidebar. The **setup** test is now removed from the test explorer. When you run a test that depends on the **setup** test, it will no longer run the **setup** test, making it much faster and therefore a much better developer experience. +![pick locators](./images/getting-started/pick-locator.png) -![deselecting setup tests in vscode](https://github.com/microsoft/playwright/assets/13063165/ebc50e38-c98d-4538-82fe-ec08491f8487) +To learn more, see our [CodeGen guide](./codegen.md). -## Global Setup -**Global setup** runs when you execute your first test. It runs only once and is useful for setting up a database or starting a server. You can manually run **global setup** by clicking the `Run global setup` option from the **Setup** section in the Playwright sidebar. **Global teardown** does not run by default; you need to manually initiate it by clicking the `Run global teardown` option. +## Advanced Features -Global setup will re-run when you debug tests as this ensures an isolated environment and dedicated setup for the test. +### Project Dependencies -![running global setup](https://github.com/microsoft/playwright/assets/13063165/bcf5fec7-2d7d-4cb9-a277-5f41e19b8d52) +Use [project dependencies](./test-projects.md) to define setup tests that run before other tests. For example, you can create a login test that runs first, then reuse that authenticated state across multiple tests without having to log in again for each test. In VS Code, you can see these setup tests in the Test Explorer and run them independently when needed. -## Multiple configurations +![setup tests in vscode](./images/getting-started/setup-tests.png) -If your project contains more than one playwright configuration file, you can switch between them by first clicking on the gear icon in the top right corner of the Playwright sidebar. This will show you all the configuration files in your project. Select the configuration files you want to use by checking the checkbox next to each one and clicking on the 'ok' button. +To learn more, see our [Project Dependencies guide](./test-projects.md). -![Selecting a configuration file](https://github.com/microsoft/playwright/assets/13063165/ff9ff838-d27a-403d-b939-94e6c295e1d7) +### Global Setup -You will now have access to all your tests in the test explorer. To run a test click on the grey triangle next to the file or project name. +For tasks that need to run only once before all tests (like seeding a database), use **Global Setup**. You can trigger the global setup and teardown manually from the Playwright sidebar. -![Switching between configuration files](https://github.com/microsoft/playwright/assets/13063165/70930de5-0a0c-45e0-a6ee-b51f727f0e35) +![running global setup](./images/getting-started/global-setup.png) -To run all tests from all configurations click on the grey triangle at the top of the test explorer. +### Multiple Configurations -![Running all tests from all configurations](https://github.com/microsoft/playwright/assets/13063165/b3de4ce1-d311-4527-b2c7-b3e2f179a685) +If you have multiple `playwright.config.ts` files, you can switch between them using the gear icon in the Playwright sidebar. This allows you to easily work with different test suites or environments. -To choose a configuration file to work with simply toggle between them by clicking on the configuration file name in the Playwright sidebar. Now when you use the tools, such as Record a test, it will record a test for the selected configuration file. +![Selecting a configuration file](./images/getting-started/selecting-configuration.png) -![Recording a test for a specific configuration file](https://github.com/microsoft/playwright/assets/13063165/a8ecbcd1-fab8-4012-bdaa-428951f233a2) +## Quick Reference -You can easily toggle back and forth between configurations by clicking on the configuration file name in the Playwright sidebar. +| Action | How to do it in VS Code | +| ----------------------- | ----------------------------------------------------------- | +| **Install Playwright** | Command Palette → `Test: Install Playwright` | +| **Run a Test** | Click the "play" icon next to the test | +| **Debug a Test** | Set a breakpoint, right-click the test → `Debug Test` | +| **Show Live Browser** | Enable `Show Browsers` in the Playwright sidebar | +| **Record a New Test** | Click `Record new` in the Playwright sidebar | +| **Pick a Locator** | Click `Pick locator` in the Playwright sidebar | +| **View Test Trace** | Enable `Show Trace Viewer` in the Playwright sidebar | -## What's next +## What's Next -- [Write tests using web first assertions, page fixtures and locators](./writing-tests.md) -- [Run your tests on CI](./ci-intro.md) -- [Learn more about the Trace Viewer](./trace-viewer.md) +- [Write tests using web-first assertions, page fixtures, and locators](./writing-tests.md) +- [Run your tests on CI](./ci-intro.md) +- [Learn more about the Trace Viewer](./trace-viewer.md) diff --git a/docs/src/images/getting-started/debug-mode.png b/docs/src/images/getting-started/debug-mode.png new file mode 100644 index 0000000000000..73c48a568df46 Binary files /dev/null and b/docs/src/images/getting-started/debug-mode.png differ diff --git a/docs/src/images/getting-started/error-messaging.png b/docs/src/images/getting-started/error-messaging.png new file mode 100644 index 0000000000000..ced1f2f8c8a7f Binary files /dev/null and b/docs/src/images/getting-started/error-messaging.png differ diff --git a/docs/src/images/getting-started/fix-with-ai.png b/docs/src/images/getting-started/fix-with-ai.png new file mode 100644 index 0000000000000..d1adcdbe3815c Binary files /dev/null and b/docs/src/images/getting-started/fix-with-ai.png differ diff --git a/docs/src/images/getting-started/global-setup.png b/docs/src/images/getting-started/global-setup.png new file mode 100644 index 0000000000000..35d6aa885709f Binary files /dev/null and b/docs/src/images/getting-started/global-setup.png differ diff --git a/docs/src/images/getting-started/install-browsers.png b/docs/src/images/getting-started/install-browsers.png new file mode 100644 index 0000000000000..715e620229f60 Binary files /dev/null and b/docs/src/images/getting-started/install-browsers.png differ diff --git a/docs/src/images/getting-started/install-playwright.png b/docs/src/images/getting-started/install-playwright.png new file mode 100644 index 0000000000000..38c2363909d1e Binary files /dev/null and b/docs/src/images/getting-started/install-playwright.png differ diff --git a/docs/src/images/getting-started/live-debugging.png b/docs/src/images/getting-started/live-debugging.png new file mode 100644 index 0000000000000..41f6d8e7e4ed4 Binary files /dev/null and b/docs/src/images/getting-started/live-debugging.png differ diff --git a/docs/src/images/getting-started/pick-locator.png b/docs/src/images/getting-started/pick-locator.png new file mode 100644 index 0000000000000..26408dd356be9 Binary files /dev/null and b/docs/src/images/getting-started/pick-locator.png differ diff --git a/docs/src/images/getting-started/record-at-cursor.png b/docs/src/images/getting-started/record-at-cursor.png new file mode 100644 index 0000000000000..0ab2fc09394c0 Binary files /dev/null and b/docs/src/images/getting-started/record-at-cursor.png differ diff --git a/docs/src/images/getting-started/record-new-test.png b/docs/src/images/getting-started/record-new-test.png new file mode 100644 index 0000000000000..085ff97aa56fb Binary files /dev/null and b/docs/src/images/getting-started/record-new-test.png differ diff --git a/docs/src/images/getting-started/run-all-tests.png b/docs/src/images/getting-started/run-all-tests.png new file mode 100644 index 0000000000000..d339bf77b56fd Binary files /dev/null and b/docs/src/images/getting-started/run-all-tests.png differ diff --git a/docs/src/images/getting-started/run-single-test.png b/docs/src/images/getting-started/run-single-test.png new file mode 100644 index 0000000000000..32c226c9605c0 Binary files /dev/null and b/docs/src/images/getting-started/run-single-test.png differ diff --git a/docs/src/images/getting-started/select-projects.png b/docs/src/images/getting-started/select-projects.png new file mode 100644 index 0000000000000..813b8553f04ab Binary files /dev/null and b/docs/src/images/getting-started/select-projects.png differ diff --git a/docs/src/images/getting-started/selecting-configuration.png b/docs/src/images/getting-started/selecting-configuration.png new file mode 100644 index 0000000000000..6fa106d57d498 Binary files /dev/null and b/docs/src/images/getting-started/selecting-configuration.png differ diff --git a/docs/src/images/getting-started/setup-tests.png b/docs/src/images/getting-started/setup-tests.png new file mode 100644 index 0000000000000..f90faa7190835 Binary files /dev/null and b/docs/src/images/getting-started/setup-tests.png differ diff --git a/docs/src/images/getting-started/show-browser.png b/docs/src/images/getting-started/show-browser.png new file mode 100644 index 0000000000000..c5389d371e711 Binary files /dev/null and b/docs/src/images/getting-started/show-browser.png differ diff --git a/docs/src/images/getting-started/testing-sidebar.png b/docs/src/images/getting-started/testing-sidebar.png new file mode 100644 index 0000000000000..3dcc449cca918 Binary files /dev/null and b/docs/src/images/getting-started/testing-sidebar.png differ diff --git a/docs/src/images/getting-started/trace-viewer-debug.png b/docs/src/images/getting-started/trace-viewer-debug.png new file mode 100644 index 0000000000000..9dbcbcaf1faed Binary files /dev/null and b/docs/src/images/getting-started/trace-viewer-debug.png differ diff --git a/docs/src/images/getting-started/trace-viewer.png b/docs/src/images/getting-started/trace-viewer.png new file mode 100644 index 0000000000000..cb282aa18c7c1 Binary files /dev/null and b/docs/src/images/getting-started/trace-viewer.png differ diff --git a/docs/src/images/getting-started/vscode-extension.png b/docs/src/images/getting-started/vscode-extension.png new file mode 100644 index 0000000000000..9f360e5a0dd2c Binary files /dev/null and b/docs/src/images/getting-started/vscode-extension.png differ diff --git a/docs/src/running-tests-js.md b/docs/src/running-tests-js.md index 4ad9ea9c67c46..5cdc008bdc19c 100644 --- a/docs/src/running-tests-js.md +++ b/docs/src/running-tests-js.md @@ -94,9 +94,9 @@ npx playwright test --last-failed ### Run tests in VS Code -Tests can be run right from VS Code using the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). Once installed you can simply click the green triangle next to the test you want to run or run all tests from the testing sidebar. Check out our [Getting Started with VS Code](./getting-started-vscode.md#running-tests) guide for more details. +Tests can be run right from VS Code using the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). Once installed you can simply click the green triangle next to the test you want to run or run all tests from the testing sidebar. Check out our [Getting Started with VS Code](./getting-started-vscode.md) guide for more details. -![Playwright VS Code extension](https://github.com/microsoft/playwright/assets/13063165/47726e70-683b-4bd5-94de-7d03dd45c30f) +![install playwright extension](./images/getting-started/vscode-extension.png) ## Debugging tests diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 01cc047648030..a72dc2a485e37 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -9273,6 +9273,64 @@ export interface BrowserContext { */ setOffline(offline: boolean): Promise; + /** + * Resets storage state in the context by clearing cookies, cache and storage, and then applying the new storage + * state. + * @param storageState Learn more about [storage state and auth](https://playwright.dev/docs/auth). + * + * Populates context with given storage state. This option can be used to initialize context with logged-in + * information obtained via + * [browserContext.storageState([options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state). + */ + setStorageState(storageState: string|{ + /** + * Cookies to set for context + */ + cookies: Array<{ + name: string; + + value: string; + + /** + * Domain and path are required. For the cookie to apply to all subdomains as well, prefix domain with a dot, like + * this: ".example.com" + */ + domain: string; + + /** + * Domain and path are required + */ + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + /** + * sameSite flag + */ + sameSite: "Strict"|"Lax"|"None"; + }>; + + origins: Array<{ + origin: string; + + /** + * localStorage to set for context + */ + localStorage: Array<{ + name: string; + + value: string; + }>; + }>; + }): Promise; + /** * Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB * snapshot. diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index d04f9eb2b625d..9835ac736af43 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -15,15 +15,15 @@ }, { "name": "chromium-tip-of-tree", - "revision": "1354", + "revision": "1355", "installByDefault": false, - "browserVersion": "140.0.7325.0" + "browserVersion": "140.0.7329.0" }, { "name": "chromium-tip-of-tree-headless-shell", - "revision": "1354", + "revision": "1355", "installByDefault": false, - "browserVersion": "140.0.7325.0" + "browserVersion": "140.0.7329.0" }, { "name": "firefox", diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 1d177df991bcd..5a1bad8987459 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -41,7 +41,7 @@ import { urlMatchesEqual } from '../utils/isomorphic/urlMatch'; import { isRegExp, isString } from '../utils/isomorphic/rtti'; import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace'; -import type { BrowserContextOptions, Headers, StorageState, WaitForEventOptions } from './types'; +import type { BrowserContextOptions, Headers, SetStorageState, StorageState, WaitForEventOptions } from './types'; import type * as structs from '../../types/structs'; import type * as api from '../../types/types'; import type { URLMatch } from '../utils/isomorphic/urlMatch'; @@ -455,6 +455,10 @@ export class BrowserContext extends ChannelOwner return state; } + async setStorageState(storageState: string | SetStorageState) { + await this._channel.setStorageState({ storageState: await prepareStorageState(this._platform, storageState) }); + } + backgroundPages(): Page[] { return [...this._backgroundPages]; } @@ -527,13 +531,13 @@ export class BrowserContext extends ChannelOwner } } -async function prepareStorageState(platform: Platform, options: BrowserContextOptions): Promise { - if (typeof options.storageState !== 'string') - return options.storageState as any; +async function prepareStorageState(platform: Platform, storageState: string | SetStorageState): Promise> { + if (typeof storageState !== 'string') + return storageState as any; try { - return JSON.parse(await platform.fs().promises.readFile(options.storageState, 'utf8')); + return JSON.parse(await platform.fs().promises.readFile(storageState, 'utf8')); } catch (e) { - rewriteErrorMessage(e, `Error reading storage state from ${options.storageState}:\n` + e.message); + rewriteErrorMessage(e, `Error reading storage state from ${storageState}:\n` + e.message); throw e; } } @@ -548,7 +552,7 @@ export async function prepareBrowserContextParams(platform: Platform, options: B viewport: options.viewport === null ? undefined : options.viewport, noDefaultViewport: options.viewport === null, extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined, - storageState: await prepareStorageState(platform, options), + storageState: options.storageState ? await prepareStorageState(platform, options.storageState) : undefined, serviceWorkers: options.serviceWorkers, colorScheme: options.colorScheme === null ? 'no-override' : options.colorScheme, reducedMotion: options.reducedMotion === null ? 'no-override' : options.reducedMotion, diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 8eb22ce61a918..77a60cc858180 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -16,42 +16,42 @@ // This file is generated by generate_channels.js, do not edit manually. -import { scheme, tOptional, tObject, tBoolean, tNumber, tString, tAny, tEnum, tArray, tBinary, tChannel, tType } from './validatorPrimitives'; +import { scheme, tOptional, tObject, tBoolean, tInt, tFloat, tString, tAny, tEnum, tArray, tBinary, tChannel, tType } from './validatorPrimitives'; export type { Validator, ValidatorContext } from './validatorPrimitives'; export { ValidationError, findValidator, maybeFindValidator, createMetadataValidator } from './validatorPrimitives'; scheme.StackFrame = tObject({ file: tString, - line: tNumber, - column: tNumber, + line: tInt, + column: tInt, function: tOptional(tString), }); scheme.Metadata = tObject({ location: tOptional(tObject({ file: tString, - line: tOptional(tNumber), - column: tOptional(tNumber), + line: tOptional(tInt), + column: tOptional(tInt), })), title: tOptional(tString), internal: tOptional(tBoolean), stepId: tOptional(tString), }); scheme.ClientSideCallMetadata = tObject({ - id: tNumber, + id: tInt, stack: tOptional(tArray(tType('StackFrame'))), }); scheme.Point = tObject({ - x: tNumber, - y: tNumber, + x: tFloat, + y: tFloat, }); scheme.Rect = tObject({ - x: tNumber, - y: tNumber, - width: tNumber, - height: tNumber, + x: tFloat, + y: tFloat, + width: tFloat, + height: tFloat, }); scheme.SerializedValue = tObject({ - n: tOptional(tNumber), + n: tOptional(tFloat), b: tOptional(tBoolean), s: tOptional(tString), v: tOptional(tEnum(['null', 'undefined', 'NaN', 'Infinity', '-Infinity', '-0'])), @@ -76,9 +76,9 @@ scheme.SerializedValue = tObject({ k: tString, v: tType('SerializedValue'), }))), - h: tOptional(tNumber), - id: tOptional(tNumber), - ref: tOptional(tNumber), + h: tOptional(tInt), + id: tOptional(tInt), + ref: tOptional(tInt), }); scheme.SerializedArgument = tObject({ value: tType('SerializedValue'), @@ -101,7 +101,7 @@ scheme.AXNode = tObject({ role: tString, name: tString, valueString: tOptional(tString), - valueNumber: tOptional(tNumber), + valueNumber: tOptional(tFloat), description: tOptional(tString), keyshortcuts: tOptional(tString), roledescription: tOptional(tString), @@ -117,9 +117,9 @@ scheme.AXNode = tObject({ selected: tOptional(tBoolean), checked: tOptional(tEnum(['checked', 'unchecked', 'mixed'])), pressed: tOptional(tEnum(['pressed', 'released', 'mixed'])), - level: tOptional(tNumber), - valuemin: tOptional(tNumber), - valuemax: tOptional(tNumber), + level: tOptional(tInt), + valuemin: tOptional(tFloat), + valuemax: tOptional(tFloat), autocomplete: tOptional(tString), haspopup: tOptional(tString), invalid: tOptional(tString), @@ -132,7 +132,7 @@ scheme.SetNetworkCookie = tObject({ url: tOptional(tString), domain: tOptional(tString), path: tOptional(tString), - expires: tOptional(tNumber), + expires: tOptional(tFloat), httpOnly: tOptional(tBoolean), secure: tOptional(tBoolean), sameSite: tOptional(tEnum(['Strict', 'Lax', 'None'])), @@ -144,7 +144,7 @@ scheme.NetworkCookie = tObject({ value: tString, domain: tString, path: tString, - expires: tNumber, + expires: tFloat, httpOnly: tBoolean, secure: tBoolean, sameSite: tEnum(['Strict', 'Lax', 'None']), @@ -157,7 +157,7 @@ scheme.NameValue = tObject({ }); scheme.IndexedDBDatabase = tObject({ name: tString, - version: tNumber, + version: tInt, stores: tArray(tObject({ name: tString, autoIncrement: tBoolean, @@ -227,11 +227,11 @@ scheme.APIRequestContextFetchParams = tObject({ jsonData: tOptional(tString), formData: tOptional(tArray(tType('NameValue'))), multipartData: tOptional(tArray(tType('FormField'))), - timeout: tNumber, + timeout: tFloat, failOnStatusCode: tOptional(tBoolean), ignoreHTTPSErrors: tOptional(tBoolean), - maxRedirects: tOptional(tNumber), - maxRetries: tOptional(tNumber), + maxRedirects: tOptional(tInt), + maxRetries: tOptional(tInt), }); scheme.APIRequestContextFetchResult = tObject({ response: tType('APIResponse'), @@ -266,7 +266,7 @@ scheme.APIRequestContextDisposeResult = tOptional(tObject({})); scheme.APIResponse = tObject({ fetchUid: tString, url: tString, - status: tNumber, + status: tInt, statusText: tString, headers: tArray(tType('NameValue')), }); @@ -277,14 +277,14 @@ scheme.LocalUtilsInitializer = tObject({ descriptor: tObject({ userAgent: tString, viewport: tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, }), screen: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), - deviceScaleFactor: tNumber, + deviceScaleFactor: tFloat, isMobile: tBoolean, hasTouch: tBoolean, defaultBrowserType: tEnum(['chromium', 'firefox', 'webkit']), @@ -318,7 +318,7 @@ scheme.LocalUtilsHarLookupResult = tObject({ action: tEnum(['error', 'redirect', 'fulfill', 'noentry']), message: tOptional(tString), redirectURL: tOptional(tString), - status: tOptional(tNumber), + status: tOptional(tInt), headers: tOptional(tArray(tType('NameValue'))), body: tOptional(tBinary), }); @@ -335,9 +335,9 @@ scheme.LocalUtilsConnectParams = tObject({ wsEndpoint: tString, headers: tOptional(tAny), exposeNetwork: tOptional(tString), - slowMo: tOptional(tNumber), - timeout: tNumber, - socksProxyRedirectPortForTest: tOptional(tNumber), + slowMo: tOptional(tFloat), + timeout: tFloat, + socksProxyRedirectPortForTest: tOptional(tInt), }); scheme.LocalUtilsConnectResult = tObject({ pipe: tChannel(['JsonPipe']), @@ -391,7 +391,7 @@ scheme.PlaywrightNewRequestParams = tObject({ passphrase: tOptional(tString), pfx: tOptional(tBinary), }))), - maxRedirects: tOptional(tNumber), + maxRedirects: tOptional(tInt), httpCredentials: tOptional(tObject({ username: tString, password: tString, @@ -420,10 +420,10 @@ scheme.RecorderSource = tObject({ text: tString, language: tString, highlight: tArray(tObject({ - line: tNumber, + line: tInt, type: tString, })), - revealLine: tOptional(tNumber), + revealLine: tOptional(tInt), group: tOptional(tString), }); scheme.DebugControllerInitializer = tOptional(tObject({})); @@ -436,7 +436,7 @@ scheme.DebugControllerSetModeRequestedEvent = tObject({ mode: tString, }); scheme.DebugControllerStateChangedEvent = tObject({ - pageCount: tNumber, + pageCount: tInt, }); scheme.DebugControllerSourceChangedEvent = tObject({ text: tString, @@ -476,7 +476,7 @@ scheme.SocksSupportInitializer = tOptional(tObject({})); scheme.SocksSupportSocksRequestedEvent = tObject({ uid: tString, host: tString, - port: tNumber, + port: tInt, }); scheme.SocksSupportSocksDataEvent = tObject({ uid: tString, @@ -488,7 +488,7 @@ scheme.SocksSupportSocksClosedEvent = tObject({ scheme.SocksSupportSocksConnectedParams = tObject({ uid: tString, host: tString, - port: tNumber, + port: tInt, }); scheme.SocksSupportSocksConnectedResult = tOptional(tObject({})); scheme.SocksSupportSocksFailedParams = tObject({ @@ -524,7 +524,7 @@ scheme.BrowserTypeLaunchParams = tObject({ handleSIGINT: tOptional(tBoolean), handleSIGTERM: tOptional(tBoolean), handleSIGHUP: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, env: tOptional(tArray(tType('NameValue'))), headless: tOptional(tBoolean), devtools: tOptional(tBoolean), @@ -538,8 +538,8 @@ scheme.BrowserTypeLaunchParams = tObject({ tracesDir: tOptional(tString), chromiumSandbox: tOptional(tBoolean), firefoxUserPrefs: tOptional(tAny), - cdpPort: tOptional(tNumber), - slowMo: tOptional(tNumber), + cdpPort: tOptional(tInt), + slowMo: tOptional(tFloat), }); scheme.BrowserTypeLaunchResult = tObject({ browser: tChannel(['Browser']), @@ -554,7 +554,7 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({ handleSIGINT: tOptional(tBoolean), handleSIGTERM: tOptional(tBoolean), handleSIGHUP: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, env: tOptional(tArray(tType('NameValue'))), headless: tOptional(tBoolean), devtools: tOptional(tBoolean), @@ -568,15 +568,15 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({ tracesDir: tOptional(tString), chromiumSandbox: tOptional(tBoolean), firefoxUserPrefs: tOptional(tAny), - cdpPort: tOptional(tNumber), + cdpPort: tOptional(tInt), noDefaultViewport: tOptional(tBoolean), viewport: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), screen: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), ignoreHTTPSErrors: tOptional(tBoolean), clientCertificates: tOptional(tArray(tObject({ @@ -592,9 +592,9 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({ locale: tOptional(tString), timezoneId: tOptional(tString), geolocation: tOptional(tObject({ - longitude: tNumber, - latitude: tNumber, - accuracy: tOptional(tNumber), + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat), })), permissions: tOptional(tArray(tString)), extraHTTPHeaders: tOptional(tArray(tType('NameValue'))), @@ -605,7 +605,7 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({ origin: tOptional(tString), send: tOptional(tEnum(['always', 'unauthorized'])), })), - deviceScaleFactor: tOptional(tNumber), + deviceScaleFactor: tOptional(tFloat), isMobile: tOptional(tBoolean), hasTouch: tOptional(tBoolean), colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference', 'no-override'])), @@ -617,8 +617,8 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({ recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), })), strictSelectors: tOptional(tBoolean), @@ -626,7 +626,7 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({ selectorEngines: tOptional(tArray(tType('SelectorEngine'))), testIdAttributeName: tOptional(tString), userDataDir: tString, - slowMo: tOptional(tNumber), + slowMo: tOptional(tFloat), }); scheme.BrowserTypeLaunchPersistentContextResult = tObject({ browser: tChannel(['Browser']), @@ -635,8 +635,8 @@ scheme.BrowserTypeLaunchPersistentContextResult = tObject({ scheme.BrowserTypeConnectOverCDPParams = tObject({ endpointURL: tString, headers: tOptional(tArray(tType('NameValue'))), - slowMo: tOptional(tNumber), - timeout: tNumber, + slowMo: tOptional(tFloat), + timeout: tFloat, }); scheme.BrowserTypeConnectOverCDPResult = tObject({ browser: tChannel(['Browser']), @@ -663,12 +663,12 @@ scheme.BrowserDefaultUserAgentForTestResult = tObject({ scheme.BrowserNewContextParams = tObject({ noDefaultViewport: tOptional(tBoolean), viewport: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), screen: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), ignoreHTTPSErrors: tOptional(tBoolean), clientCertificates: tOptional(tArray(tObject({ @@ -684,9 +684,9 @@ scheme.BrowserNewContextParams = tObject({ locale: tOptional(tString), timezoneId: tOptional(tString), geolocation: tOptional(tObject({ - longitude: tNumber, - latitude: tNumber, - accuracy: tOptional(tNumber), + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat), })), permissions: tOptional(tArray(tString)), extraHTTPHeaders: tOptional(tArray(tType('NameValue'))), @@ -697,7 +697,7 @@ scheme.BrowserNewContextParams = tObject({ origin: tOptional(tString), send: tOptional(tEnum(['always', 'unauthorized'])), })), - deviceScaleFactor: tOptional(tNumber), + deviceScaleFactor: tOptional(tFloat), isMobile: tOptional(tBoolean), hasTouch: tOptional(tBoolean), colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference', 'no-override'])), @@ -709,8 +709,8 @@ scheme.BrowserNewContextParams = tObject({ recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), })), strictSelectors: tOptional(tBoolean), @@ -734,12 +734,12 @@ scheme.BrowserNewContextResult = tObject({ scheme.BrowserNewContextForReuseParams = tObject({ noDefaultViewport: tOptional(tBoolean), viewport: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), screen: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), ignoreHTTPSErrors: tOptional(tBoolean), clientCertificates: tOptional(tArray(tObject({ @@ -755,9 +755,9 @@ scheme.BrowserNewContextForReuseParams = tObject({ locale: tOptional(tString), timezoneId: tOptional(tString), geolocation: tOptional(tObject({ - longitude: tNumber, - latitude: tNumber, - accuracy: tOptional(tNumber), + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat), })), permissions: tOptional(tArray(tString)), extraHTTPHeaders: tOptional(tArray(tType('NameValue'))), @@ -768,7 +768,7 @@ scheme.BrowserNewContextForReuseParams = tObject({ origin: tOptional(tString), send: tOptional(tEnum(['always', 'unauthorized'])), })), - deviceScaleFactor: tOptional(tNumber), + deviceScaleFactor: tOptional(tFloat), isMobile: tOptional(tBoolean), hasTouch: tOptional(tBoolean), colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference', 'no-override'])), @@ -780,8 +780,8 @@ scheme.BrowserNewContextForReuseParams = tObject({ recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), })), strictSelectors: tOptional(tBoolean), @@ -848,12 +848,12 @@ scheme.BrowserContextInitializer = tObject({ options: tObject({ noDefaultViewport: tOptional(tBoolean), viewport: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), screen: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), ignoreHTTPSErrors: tOptional(tBoolean), clientCertificates: tOptional(tArray(tObject({ @@ -869,9 +869,9 @@ scheme.BrowserContextInitializer = tObject({ locale: tOptional(tString), timezoneId: tOptional(tString), geolocation: tOptional(tObject({ - longitude: tNumber, - latitude: tNumber, - accuracy: tOptional(tNumber), + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat), })), permissions: tOptional(tArray(tString)), extraHTTPHeaders: tOptional(tArray(tType('NameValue'))), @@ -882,7 +882,7 @@ scheme.BrowserContextInitializer = tObject({ origin: tOptional(tString), send: tOptional(tEnum(['always', 'unauthorized'])), })), - deviceScaleFactor: tOptional(tNumber), + deviceScaleFactor: tOptional(tFloat), isMobile: tOptional(tBoolean), hasTouch: tOptional(tBoolean), colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference', 'no-override'])), @@ -894,8 +894,8 @@ scheme.BrowserContextInitializer = tObject({ recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), })), strictSelectors: tOptional(tBoolean), @@ -913,8 +913,8 @@ scheme.BrowserContextConsoleEvent = tObject({ args: tArray(tChannel(['ElementHandle', 'JSHandle'])), location: tObject({ url: tString, - lineNumber: tNumber, - columnNumber: tNumber, + lineNumber: tInt, + columnNumber: tInt, }), page: tChannel(['Page']), }); @@ -951,13 +951,13 @@ scheme.BrowserContextRequestEvent = tObject({ scheme.BrowserContextRequestFailedEvent = tObject({ request: tChannel(['Request']), failureText: tOptional(tString), - responseEndTiming: tNumber, + responseEndTiming: tFloat, page: tOptional(tChannel(['Page'])), }); scheme.BrowserContextRequestFinishedEvent = tObject({ request: tChannel(['Request']), response: tOptional(tChannel(['Response'])), - responseEndTiming: tNumber, + responseEndTiming: tFloat, page: tOptional(tChannel(['Page'])), }); scheme.BrowserContextResponseEvent = tObject({ @@ -1030,9 +1030,9 @@ scheme.BrowserContextSetExtraHTTPHeadersParams = tObject({ scheme.BrowserContextSetExtraHTTPHeadersResult = tOptional(tObject({})); scheme.BrowserContextSetGeolocationParams = tObject({ geolocation: tOptional(tObject({ - longitude: tNumber, - latitude: tNumber, - accuracy: tOptional(tNumber), + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat), })), }); scheme.BrowserContextSetGeolocationResult = tOptional(tObject({})); @@ -1071,6 +1071,13 @@ scheme.BrowserContextStorageStateResult = tObject({ cookies: tArray(tType('NetworkCookie')), origins: tArray(tType('OriginStorage')), }); +scheme.BrowserContextSetStorageStateParams = tObject({ + storageState: tObject({ + cookies: tOptional(tArray(tType('SetNetworkCookie'))), + origins: tOptional(tArray(tType('SetOriginStorage'))), + }), +}); +scheme.BrowserContextSetStorageStateResult = tOptional(tObject({})); scheme.BrowserContextPauseParams = tOptional(tObject({})); scheme.BrowserContextPauseResult = tOptional(tObject({})); scheme.BrowserContextEnableRecorderParams = tObject({ @@ -1114,7 +1121,7 @@ scheme.BrowserContextCreateTempFilesParams = tObject({ rootDirName: tOptional(tString), items: tArray(tObject({ name: tString, - lastModifiedMs: tOptional(tNumber), + lastModifiedMs: tOptional(tFloat), })), }); scheme.BrowserContextCreateTempFilesResult = tObject({ @@ -1127,34 +1134,34 @@ scheme.BrowserContextUpdateSubscriptionParams = tObject({ }); scheme.BrowserContextUpdateSubscriptionResult = tOptional(tObject({})); scheme.BrowserContextClockFastForwardParams = tObject({ - ticksNumber: tOptional(tNumber), + ticksNumber: tOptional(tFloat), ticksString: tOptional(tString), }); scheme.BrowserContextClockFastForwardResult = tOptional(tObject({})); scheme.BrowserContextClockInstallParams = tObject({ - timeNumber: tOptional(tNumber), + timeNumber: tOptional(tFloat), timeString: tOptional(tString), }); scheme.BrowserContextClockInstallResult = tOptional(tObject({})); scheme.BrowserContextClockPauseAtParams = tObject({ - timeNumber: tOptional(tNumber), + timeNumber: tOptional(tFloat), timeString: tOptional(tString), }); scheme.BrowserContextClockPauseAtResult = tOptional(tObject({})); scheme.BrowserContextClockResumeParams = tOptional(tObject({})); scheme.BrowserContextClockResumeResult = tOptional(tObject({})); scheme.BrowserContextClockRunForParams = tObject({ - ticksNumber: tOptional(tNumber), + ticksNumber: tOptional(tFloat), ticksString: tOptional(tString), }); scheme.BrowserContextClockRunForResult = tOptional(tObject({})); scheme.BrowserContextClockSetFixedTimeParams = tObject({ - timeNumber: tOptional(tNumber), + timeNumber: tOptional(tFloat), timeString: tOptional(tString), }); scheme.BrowserContextClockSetFixedTimeResult = tOptional(tObject({})); scheme.BrowserContextClockSetSystemTimeParams = tObject({ - timeNumber: tOptional(tNumber), + timeNumber: tOptional(tFloat), timeString: tOptional(tString), }); scheme.BrowserContextClockSetSystemTimeResult = tOptional(tObject({})); @@ -1163,8 +1170,8 @@ scheme.BrowserContextClockUninstallResult = tOptional(tObject({})); scheme.PageInitializer = tObject({ mainFrame: tChannel(['Frame']), viewportSize: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), isClosed: tBoolean, opener: tOptional(tChannel(['Page'])), @@ -1181,8 +1188,8 @@ scheme.PageDownloadEvent = tObject({ }); scheme.PageViewportSizeChangedEvent = tObject({ viewportSize: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), }); scheme.PageFileChooserEvent = tObject({ @@ -1196,7 +1203,7 @@ scheme.PageFrameDetachedEvent = tObject({ frame: tChannel(['Frame']), }); scheme.PageLocatorHandlerTriggeredEvent = tObject({ - uid: tNumber, + uid: tInt, }); scheme.PageRouteEvent = tObject({ route: tChannel(['Route']), @@ -1236,14 +1243,14 @@ scheme.PageExposeBindingParams = tObject({ }); scheme.PageExposeBindingResult = tOptional(tObject({})); scheme.PageGoBackParams = tObject({ - timeout: tNumber, + timeout: tFloat, waitUntil: tOptional(tType('LifecycleEvent')), }); scheme.PageGoBackResult = tObject({ response: tOptional(tChannel(['Response'])), }); scheme.PageGoForwardParams = tObject({ - timeout: tNumber, + timeout: tFloat, waitUntil: tOptional(tType('LifecycleEvent')), }); scheme.PageGoForwardResult = tObject({ @@ -1256,19 +1263,19 @@ scheme.PageRegisterLocatorHandlerParams = tObject({ noWaitAfter: tOptional(tBoolean), }); scheme.PageRegisterLocatorHandlerResult = tObject({ - uid: tNumber, + uid: tInt, }); scheme.PageResolveLocatorHandlerNoReplyParams = tObject({ - uid: tNumber, + uid: tInt, remove: tOptional(tBoolean), }); scheme.PageResolveLocatorHandlerNoReplyResult = tOptional(tObject({})); scheme.PageUnregisterLocatorHandlerParams = tObject({ - uid: tNumber, + uid: tInt, }); scheme.PageUnregisterLocatorHandlerResult = tOptional(tObject({})); scheme.PageReloadParams = tObject({ - timeout: tNumber, + timeout: tFloat, waitUntil: tOptional(tType('LifecycleEvent')), }); scheme.PageReloadResult = tObject({ @@ -1276,16 +1283,16 @@ scheme.PageReloadResult = tObject({ }); scheme.PageExpectScreenshotParams = tObject({ expected: tOptional(tBinary), - timeout: tNumber, + timeout: tFloat, isNot: tBoolean, locator: tOptional(tObject({ frame: tChannel(['Frame']), selector: tString, })), comparator: tOptional(tString), - maxDiffPixels: tOptional(tNumber), - maxDiffPixelRatio: tOptional(tNumber), - threshold: tOptional(tNumber), + maxDiffPixels: tOptional(tInt), + maxDiffPixelRatio: tOptional(tFloat), + threshold: tOptional(tFloat), fullPage: tOptional(tBoolean), clip: tOptional(tType('Rect')), omitBackground: tOptional(tBoolean), @@ -1308,9 +1315,9 @@ scheme.PageExpectScreenshotResult = tObject({ log: tOptional(tArray(tString)), }); scheme.PageScreenshotParams = tObject({ - timeout: tNumber, + timeout: tFloat, type: tOptional(tEnum(['png', 'jpeg'])), - quality: tOptional(tNumber), + quality: tOptional(tInt), fullPage: tOptional(tBoolean), clip: tOptional(tType('Rect')), omitBackground: tOptional(tBoolean), @@ -1349,8 +1356,8 @@ scheme.PageSetWebSocketInterceptionPatternsParams = tObject({ scheme.PageSetWebSocketInterceptionPatternsResult = tOptional(tObject({})); scheme.PageSetViewportSizeParams = tObject({ viewportSize: tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, }), }); scheme.PageSetViewportSizeResult = tOptional(tObject({})); @@ -1368,46 +1375,46 @@ scheme.PageKeyboardInsertTextParams = tObject({ scheme.PageKeyboardInsertTextResult = tOptional(tObject({})); scheme.PageKeyboardTypeParams = tObject({ text: tString, - delay: tOptional(tNumber), + delay: tOptional(tFloat), }); scheme.PageKeyboardTypeResult = tOptional(tObject({})); scheme.PageKeyboardPressParams = tObject({ key: tString, - delay: tOptional(tNumber), + delay: tOptional(tFloat), }); scheme.PageKeyboardPressResult = tOptional(tObject({})); scheme.PageMouseMoveParams = tObject({ - x: tNumber, - y: tNumber, - steps: tOptional(tNumber), + x: tFloat, + y: tFloat, + steps: tOptional(tInt), }); scheme.PageMouseMoveResult = tOptional(tObject({})); scheme.PageMouseDownParams = tObject({ button: tOptional(tEnum(['left', 'right', 'middle'])), - clickCount: tOptional(tNumber), + clickCount: tOptional(tInt), }); scheme.PageMouseDownResult = tOptional(tObject({})); scheme.PageMouseUpParams = tObject({ button: tOptional(tEnum(['left', 'right', 'middle'])), - clickCount: tOptional(tNumber), + clickCount: tOptional(tInt), }); scheme.PageMouseUpResult = tOptional(tObject({})); scheme.PageMouseClickParams = tObject({ - x: tNumber, - y: tNumber, - delay: tOptional(tNumber), + x: tFloat, + y: tFloat, + delay: tOptional(tFloat), button: tOptional(tEnum(['left', 'right', 'middle'])), - clickCount: tOptional(tNumber), + clickCount: tOptional(tInt), }); scheme.PageMouseClickResult = tOptional(tObject({})); scheme.PageMouseWheelParams = tObject({ - deltaX: tNumber, - deltaY: tNumber, + deltaX: tFloat, + deltaY: tFloat, }); scheme.PageMouseWheelResult = tOptional(tObject({})); scheme.PageTouchscreenTapParams = tObject({ - x: tNumber, - y: tNumber, + x: tFloat, + y: tFloat, }); scheme.PageTouchscreenTapResult = tOptional(tObject({})); scheme.PageAccessibilitySnapshotParams = tObject({ @@ -1418,7 +1425,7 @@ scheme.PageAccessibilitySnapshotResult = tObject({ rootAXNode: tOptional(tType('AXNode')), }); scheme.PagePdfParams = tObject({ - scale: tOptional(tNumber), + scale: tOptional(tFloat), displayHeaderFooter: tOptional(tBoolean), headerTemplate: tOptional(tString), footerTemplate: tOptional(tString), @@ -1442,7 +1449,7 @@ scheme.PagePdfResult = tObject({ pdf: tBinary, }); scheme.PageSnapshotForAIParams = tObject({ - timeout: tNumber, + timeout: tFloat, }); scheme.PageSnapshotForAIResult = tObject({ snapshot: tString, @@ -1462,9 +1469,9 @@ scheme.PageStopJSCoverageResult = tObject({ functionName: tString, isBlockCoverage: tBoolean, ranges: tArray(tObject({ - startOffset: tNumber, - endOffset: tNumber, - count: tNumber, + startOffset: tInt, + endOffset: tInt, + count: tInt, })), })), })), @@ -1479,8 +1486,8 @@ scheme.PageStopCSSCoverageResult = tObject({ url: tString, text: tOptional(tString), ranges: tArray(tObject({ - start: tNumber, - end: tNumber, + start: tInt, + end: tInt, })), })), }); @@ -1545,7 +1552,7 @@ scheme.FrameAddStyleTagResult = tObject({ }); scheme.FrameAriaSnapshotParams = tObject({ selector: tString, - timeout: tNumber, + timeout: tFloat, }); scheme.FrameAriaSnapshotResult = tObject({ snapshot: tString, @@ -1553,7 +1560,7 @@ scheme.FrameAriaSnapshotResult = tObject({ scheme.FrameBlurParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameBlurResult = tOptional(tObject({})); scheme.FrameCheckParams = tObject({ @@ -1561,7 +1568,7 @@ scheme.FrameCheckParams = tObject({ strict: tOptional(tBoolean), force: tOptional(tBoolean), position: tOptional(tType('Point')), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.FrameCheckResult = tOptional(tObject({})); @@ -1572,10 +1579,10 @@ scheme.FrameClickParams = tObject({ noWaitAfter: tOptional(tBoolean), modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), - delay: tOptional(tNumber), + delay: tOptional(tFloat), button: tOptional(tEnum(['left', 'right', 'middle'])), - clickCount: tOptional(tNumber), - timeout: tNumber, + clickCount: tOptional(tInt), + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.FrameClickResult = tOptional(tObject({})); @@ -1587,7 +1594,7 @@ scheme.FrameDragAndDropParams = tObject({ source: tString, target: tString, force: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), sourcePosition: tOptional(tType('Point')), targetPosition: tOptional(tType('Point')), @@ -1600,9 +1607,9 @@ scheme.FrameDblclickParams = tObject({ force: tOptional(tBoolean), modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), - delay: tOptional(tNumber), + delay: tOptional(tFloat), button: tOptional(tEnum(['left', 'right', 'middle'])), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.FrameDblclickResult = tOptional(tObject({})); @@ -1611,7 +1618,7 @@ scheme.FrameDispatchEventParams = tObject({ strict: tOptional(tBoolean), type: tString, eventInit: tType('SerializedArgument'), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameDispatchEventResult = tOptional(tObject({})); scheme.FrameEvaluateExpressionParams = tObject({ @@ -1635,13 +1642,13 @@ scheme.FrameFillParams = tObject({ strict: tOptional(tBoolean), value: tString, force: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameFillResult = tOptional(tObject({})); scheme.FrameFocusParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameFocusResult = tOptional(tObject({})); scheme.FrameFrameElementParams = tOptional(tObject({})); @@ -1662,14 +1669,14 @@ scheme.FrameGetAttributeParams = tObject({ selector: tString, strict: tOptional(tBoolean), name: tString, - timeout: tNumber, + timeout: tFloat, }); scheme.FrameGetAttributeResult = tObject({ value: tOptional(tString), }); scheme.FrameGotoParams = tObject({ url: tString, - timeout: tNumber, + timeout: tFloat, waitUntil: tOptional(tType('LifecycleEvent')), referer: tOptional(tString), }); @@ -1682,14 +1689,14 @@ scheme.FrameHoverParams = tObject({ force: tOptional(tBoolean), modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.FrameHoverResult = tOptional(tObject({})); scheme.FrameInnerHTMLParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameInnerHTMLResult = tObject({ value: tString, @@ -1697,7 +1704,7 @@ scheme.FrameInnerHTMLResult = tObject({ scheme.FrameInnerTextParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameInnerTextResult = tObject({ value: tString, @@ -1705,7 +1712,7 @@ scheme.FrameInnerTextResult = tObject({ scheme.FrameInputValueParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameInputValueResult = tObject({ value: tString, @@ -1713,7 +1720,7 @@ scheme.FrameInputValueResult = tObject({ scheme.FrameIsCheckedParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameIsCheckedResult = tObject({ value: tBoolean, @@ -1721,7 +1728,7 @@ scheme.FrameIsCheckedResult = tObject({ scheme.FrameIsDisabledParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameIsDisabledResult = tObject({ value: tBoolean, @@ -1729,7 +1736,7 @@ scheme.FrameIsDisabledResult = tObject({ scheme.FrameIsEnabledParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameIsEnabledResult = tObject({ value: tBoolean, @@ -1751,7 +1758,7 @@ scheme.FrameIsVisibleResult = tObject({ scheme.FrameIsEditableParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameIsEditableResult = tObject({ value: tBoolean, @@ -1760,9 +1767,9 @@ scheme.FramePressParams = tObject({ selector: tString, strict: tOptional(tBoolean), key: tString, - delay: tOptional(tNumber), + delay: tOptional(tFloat), noWaitAfter: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FramePressResult = tOptional(tObject({})); scheme.FrameQuerySelectorParams = tObject({ @@ -1782,7 +1789,7 @@ scheme.FrameQueryCountParams = tObject({ selector: tString, }); scheme.FrameQueryCountResult = tObject({ - value: tNumber, + value: tInt, }); scheme.FrameSelectOptionParams = tObject({ selector: tString, @@ -1792,17 +1799,17 @@ scheme.FrameSelectOptionParams = tObject({ valueOrLabel: tOptional(tString), value: tOptional(tString), label: tOptional(tString), - index: tOptional(tNumber), + index: tOptional(tInt), }))), force: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameSelectOptionResult = tObject({ values: tArray(tString), }); scheme.FrameSetContentParams = tObject({ html: tString, - timeout: tNumber, + timeout: tFloat, waitUntil: tOptional(tType('LifecycleEvent')), }); scheme.FrameSetContentResult = tOptional(tObject({})); @@ -1818,7 +1825,7 @@ scheme.FrameSetInputFilesParams = tObject({ directoryStream: tOptional(tChannel(['WritableStream'])), localPaths: tOptional(tArray(tString)), streams: tOptional(tArray(tChannel(['WritableStream']))), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameSetInputFilesResult = tOptional(tObject({})); scheme.FrameTapParams = tObject({ @@ -1827,14 +1834,14 @@ scheme.FrameTapParams = tObject({ force: tOptional(tBoolean), modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.FrameTapResult = tOptional(tObject({})); scheme.FrameTextContentParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.FrameTextContentResult = tObject({ value: tOptional(tString), @@ -1847,8 +1854,8 @@ scheme.FrameTypeParams = tObject({ selector: tString, strict: tOptional(tBoolean), text: tString, - delay: tOptional(tNumber), - timeout: tNumber, + delay: tOptional(tFloat), + timeout: tFloat, }); scheme.FrameTypeResult = tOptional(tObject({})); scheme.FrameUncheckParams = tObject({ @@ -1856,20 +1863,20 @@ scheme.FrameUncheckParams = tObject({ strict: tOptional(tBoolean), force: tOptional(tBoolean), position: tOptional(tType('Point')), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.FrameUncheckResult = tOptional(tObject({})); scheme.FrameWaitForTimeoutParams = tObject({ - waitTimeout: tNumber, + waitTimeout: tFloat, }); scheme.FrameWaitForTimeoutResult = tOptional(tObject({})); scheme.FrameWaitForFunctionParams = tObject({ expression: tString, isFunction: tOptional(tBoolean), arg: tType('SerializedArgument'), - timeout: tNumber, - pollingInterval: tOptional(tNumber), + timeout: tFloat, + pollingInterval: tOptional(tFloat), }); scheme.FrameWaitForFunctionResult = tObject({ handle: tChannel(['ElementHandle', 'JSHandle']), @@ -1877,7 +1884,7 @@ scheme.FrameWaitForFunctionResult = tObject({ scheme.FrameWaitForSelectorParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, state: tOptional(tEnum(['attached', 'detached', 'visible', 'hidden'])), omitReturnValue: tOptional(tBoolean), }); @@ -1889,11 +1896,11 @@ scheme.FrameExpectParams = tObject({ expression: tString, expressionArg: tOptional(tAny), expectedText: tOptional(tArray(tType('ExpectedTextValue'))), - expectedNumber: tOptional(tNumber), + expectedNumber: tOptional(tFloat), expectedValue: tOptional(tType('SerializedArgument')), useInnerText: tOptional(tBoolean), isNot: tBoolean, - timeout: tNumber, + timeout: tFloat, }); scheme.FrameExpectResult = tObject({ matches: tBoolean, @@ -2004,7 +2011,7 @@ scheme.ElementHandleBoundingBoxResult = tObject({ scheme.ElementHandleCheckParams = tObject({ force: tOptional(tBoolean), position: tOptional(tType('Point')), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.ElementHandleCheckResult = tOptional(tObject({})); @@ -2013,10 +2020,10 @@ scheme.ElementHandleClickParams = tObject({ noWaitAfter: tOptional(tBoolean), modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), - delay: tOptional(tNumber), + delay: tOptional(tFloat), button: tOptional(tEnum(['left', 'right', 'middle'])), - clickCount: tOptional(tNumber), - timeout: tNumber, + clickCount: tOptional(tInt), + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.ElementHandleClickResult = tOptional(tObject({})); @@ -2028,9 +2035,9 @@ scheme.ElementHandleDblclickParams = tObject({ force: tOptional(tBoolean), modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), - delay: tOptional(tNumber), + delay: tOptional(tFloat), button: tOptional(tEnum(['left', 'right', 'middle'])), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.ElementHandleDblclickResult = tOptional(tObject({})); @@ -2042,7 +2049,7 @@ scheme.ElementHandleDispatchEventResult = tOptional(tObject({})); scheme.ElementHandleFillParams = tObject({ value: tString, force: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.ElementHandleFillResult = tOptional(tObject({})); scheme.ElementHandleFocusParams = tOptional(tObject({})); @@ -2057,7 +2064,7 @@ scheme.ElementHandleHoverParams = tObject({ force: tOptional(tBoolean), modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.ElementHandleHoverResult = tOptional(tObject({})); @@ -2103,8 +2110,8 @@ scheme.ElementHandleOwnerFrameResult = tObject({ }); scheme.ElementHandlePressParams = tObject({ key: tString, - delay: tOptional(tNumber), - timeout: tNumber, + delay: tOptional(tFloat), + timeout: tFloat, noWaitAfter: tOptional(tBoolean), }); scheme.ElementHandlePressResult = tOptional(tObject({})); @@ -2122,9 +2129,9 @@ scheme.ElementHandleQuerySelectorAllResult = tObject({ elements: tArray(tChannel(['ElementHandle'])), }); scheme.ElementHandleScreenshotParams = tObject({ - timeout: tNumber, + timeout: tFloat, type: tOptional(tEnum(['png', 'jpeg'])), - quality: tOptional(tNumber), + quality: tOptional(tInt), omitBackground: tOptional(tBoolean), caret: tOptional(tEnum(['hide', 'initial'])), animations: tOptional(tEnum(['disabled', 'allow'])), @@ -2140,7 +2147,7 @@ scheme.ElementHandleScreenshotResult = tObject({ binary: tBinary, }); scheme.ElementHandleScrollIntoViewIfNeededParams = tObject({ - timeout: tNumber, + timeout: tFloat, }); scheme.ElementHandleScrollIntoViewIfNeededResult = tOptional(tObject({})); scheme.ElementHandleSelectOptionParams = tObject({ @@ -2149,17 +2156,17 @@ scheme.ElementHandleSelectOptionParams = tObject({ valueOrLabel: tOptional(tString), value: tOptional(tString), label: tOptional(tString), - index: tOptional(tNumber), + index: tOptional(tInt), }))), force: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.ElementHandleSelectOptionResult = tObject({ values: tArray(tString), }); scheme.ElementHandleSelectTextParams = tObject({ force: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, }); scheme.ElementHandleSelectTextResult = tOptional(tObject({})); scheme.ElementHandleSetInputFilesParams = tObject({ @@ -2172,14 +2179,14 @@ scheme.ElementHandleSetInputFilesParams = tObject({ directoryStream: tOptional(tChannel(['WritableStream'])), localPaths: tOptional(tArray(tString)), streams: tOptional(tArray(tChannel(['WritableStream']))), - timeout: tNumber, + timeout: tFloat, }); scheme.ElementHandleSetInputFilesResult = tOptional(tObject({})); scheme.ElementHandleTapParams = tObject({ force: tOptional(tBoolean), modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.ElementHandleTapResult = tOptional(tObject({})); @@ -2189,26 +2196,26 @@ scheme.ElementHandleTextContentResult = tObject({ }); scheme.ElementHandleTypeParams = tObject({ text: tString, - delay: tOptional(tNumber), - timeout: tNumber, + delay: tOptional(tFloat), + timeout: tFloat, }); scheme.ElementHandleTypeResult = tOptional(tObject({})); scheme.ElementHandleUncheckParams = tObject({ force: tOptional(tBoolean), position: tOptional(tType('Point')), - timeout: tNumber, + timeout: tFloat, trial: tOptional(tBoolean), }); scheme.ElementHandleUncheckResult = tOptional(tObject({})); scheme.ElementHandleWaitForElementStateParams = tObject({ state: tEnum(['visible', 'hidden', 'stable', 'enabled', 'disabled', 'editable']), - timeout: tNumber, + timeout: tFloat, }); scheme.ElementHandleWaitForElementStateResult = tOptional(tObject({})); scheme.ElementHandleWaitForSelectorParams = tObject({ selector: tString, strict: tOptional(tBoolean), - timeout: tNumber, + timeout: tFloat, state: tOptional(tEnum(['attached', 'detached', 'visible', 'hidden'])), }); scheme.ElementHandleWaitForSelectorResult = tObject({ @@ -2253,7 +2260,7 @@ scheme.RouteContinueParams = tObject({ }); scheme.RouteContinueResult = tOptional(tObject({})); scheme.RouteFulfillParams = tObject({ - status: tOptional(tNumber), + status: tOptional(tInt), headers: tOptional(tArray(tType('NameValue'))), body: tOptional(tString), isBase64: tOptional(tBoolean), @@ -2272,12 +2279,12 @@ scheme.WebSocketRouteMessageFromServerEvent = tObject({ isBase64: tBoolean, }); scheme.WebSocketRouteClosePageEvent = tObject({ - code: tOptional(tNumber), + code: tOptional(tInt), reason: tOptional(tString), wasClean: tBoolean, }); scheme.WebSocketRouteCloseServerEvent = tObject({ - code: tOptional(tNumber), + code: tOptional(tInt), reason: tOptional(tString), wasClean: tBoolean, }); @@ -2296,31 +2303,31 @@ scheme.WebSocketRouteSendToServerParams = tObject({ }); scheme.WebSocketRouteSendToServerResult = tOptional(tObject({})); scheme.WebSocketRouteClosePageParams = tObject({ - code: tOptional(tNumber), + code: tOptional(tInt), reason: tOptional(tString), wasClean: tBoolean, }); scheme.WebSocketRouteClosePageResult = tOptional(tObject({})); scheme.WebSocketRouteCloseServerParams = tObject({ - code: tOptional(tNumber), + code: tOptional(tInt), reason: tOptional(tString), wasClean: tBoolean, }); scheme.WebSocketRouteCloseServerResult = tOptional(tObject({})); scheme.ResourceTiming = tObject({ - startTime: tNumber, - domainLookupStart: tNumber, - domainLookupEnd: tNumber, - connectStart: tNumber, - secureConnectionStart: tNumber, - connectEnd: tNumber, - requestStart: tNumber, - responseStart: tNumber, + startTime: tFloat, + domainLookupStart: tFloat, + domainLookupEnd: tFloat, + connectStart: tFloat, + secureConnectionStart: tFloat, + connectEnd: tFloat, + requestStart: tFloat, + responseStart: tFloat, }); scheme.ResponseInitializer = tObject({ request: tChannel(['Request']), url: tString, - status: tNumber, + status: tInt, statusText: tString, headers: tArray(tType('NameValue')), timing: tType('ResourceTiming'), @@ -2350,29 +2357,29 @@ scheme.SecurityDetails = tObject({ issuer: tOptional(tString), protocol: tOptional(tString), subjectName: tOptional(tString), - validFrom: tOptional(tNumber), - validTo: tOptional(tNumber), + validFrom: tOptional(tFloat), + validTo: tOptional(tFloat), }); scheme.RequestSizes = tObject({ - requestBodySize: tNumber, - requestHeadersSize: tNumber, - responseBodySize: tNumber, - responseHeadersSize: tNumber, + requestBodySize: tInt, + requestHeadersSize: tInt, + responseBodySize: tInt, + responseHeadersSize: tInt, }); scheme.RemoteAddr = tObject({ ipAddress: tString, - port: tNumber, + port: tInt, }); scheme.WebSocketInitializer = tObject({ url: tString, }); scheme.WebSocketOpenEvent = tOptional(tObject({})); scheme.WebSocketFrameSentEvent = tObject({ - opcode: tNumber, + opcode: tInt, data: tString, }); scheme.WebSocketFrameReceivedEvent = tObject({ - opcode: tNumber, + opcode: tInt, data: tString, }); scheme.WebSocketSocketErrorEvent = tObject({ @@ -2424,8 +2431,8 @@ scheme.TracingTracingGroupParams = tObject({ name: tString, location: tOptional(tObject({ file: tString, - line: tOptional(tNumber), - column: tOptional(tNumber), + line: tOptional(tInt), + column: tOptional(tInt), })), }); scheme.TracingTracingGroupResult = tOptional(tObject({})); @@ -2469,7 +2476,7 @@ scheme.ArtifactDeleteParams = tOptional(tObject({})); scheme.ArtifactDeleteResult = tOptional(tObject({})); scheme.StreamInitializer = tOptional(tObject({})); scheme.StreamReadParams = tObject({ - size: tOptional(tNumber), + size: tOptional(tInt), }); scheme.StreamReadResult = tObject({ binary: tBinary, @@ -2503,15 +2510,15 @@ scheme.ElectronLaunchParams = tObject({ args: tOptional(tArray(tString)), cwd: tOptional(tString), env: tOptional(tArray(tType('NameValue'))), - timeout: tNumber, + timeout: tFloat, acceptDownloads: tOptional(tEnum(['accept', 'deny', 'internal-browser-default'])), bypassCSP: tOptional(tBoolean), colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference', 'no-override'])), extraHTTPHeaders: tOptional(tArray(tType('NameValue'))), geolocation: tOptional(tObject({ - longitude: tNumber, - latitude: tNumber, - accuracy: tOptional(tNumber), + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat), })), httpCredentials: tOptional(tObject({ username: tString, @@ -2524,8 +2531,8 @@ scheme.ElectronLaunchParams = tObject({ recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), })), strictSelectors: tOptional(tBoolean), @@ -2547,8 +2554,8 @@ scheme.ElectronApplicationConsoleEvent = tObject({ args: tArray(tChannel(['ElementHandle', 'JSHandle'])), location: tObject({ url: tString, - lineNumber: tNumber, - columnNumber: tNumber, + lineNumber: tInt, + columnNumber: tInt, }), }); scheme.ElectronApplicationBrowserWindowParams = tObject({ @@ -2581,7 +2588,7 @@ scheme.ElectronApplicationUpdateSubscriptionResult = tOptional(tObject({})); scheme.AndroidInitializer = tOptional(tObject({})); scheme.AndroidDevicesParams = tObject({ host: tOptional(tString), - port: tOptional(tNumber), + port: tOptional(tInt), omitDriverInstall: tOptional(tBoolean), }); scheme.AndroidDevicesResult = tObject({ @@ -2612,68 +2619,68 @@ scheme.AndroidDeviceWebViewRemovedEvent = tObject({ scheme.AndroidDeviceWaitParams = tObject({ androidSelector: tType('AndroidSelector'), state: tOptional(tEnum(['gone'])), - timeout: tNumber, + timeout: tFloat, }); scheme.AndroidDeviceWaitResult = tOptional(tObject({})); scheme.AndroidDeviceFillParams = tObject({ androidSelector: tType('AndroidSelector'), text: tString, - timeout: tNumber, + timeout: tFloat, }); scheme.AndroidDeviceFillResult = tOptional(tObject({})); scheme.AndroidDeviceTapParams = tObject({ androidSelector: tType('AndroidSelector'), - duration: tOptional(tNumber), - timeout: tNumber, + duration: tOptional(tFloat), + timeout: tFloat, }); scheme.AndroidDeviceTapResult = tOptional(tObject({})); scheme.AndroidDeviceDragParams = tObject({ androidSelector: tType('AndroidSelector'), dest: tType('Point'), - speed: tOptional(tNumber), - timeout: tNumber, + speed: tOptional(tFloat), + timeout: tFloat, }); scheme.AndroidDeviceDragResult = tOptional(tObject({})); scheme.AndroidDeviceFlingParams = tObject({ androidSelector: tType('AndroidSelector'), direction: tEnum(['up', 'down', 'left', 'right']), - speed: tOptional(tNumber), - timeout: tNumber, + speed: tOptional(tFloat), + timeout: tFloat, }); scheme.AndroidDeviceFlingResult = tOptional(tObject({})); scheme.AndroidDeviceLongTapParams = tObject({ androidSelector: tType('AndroidSelector'), - timeout: tNumber, + timeout: tFloat, }); scheme.AndroidDeviceLongTapResult = tOptional(tObject({})); scheme.AndroidDevicePinchCloseParams = tObject({ androidSelector: tType('AndroidSelector'), - percent: tNumber, - speed: tOptional(tNumber), - timeout: tNumber, + percent: tFloat, + speed: tOptional(tFloat), + timeout: tFloat, }); scheme.AndroidDevicePinchCloseResult = tOptional(tObject({})); scheme.AndroidDevicePinchOpenParams = tObject({ androidSelector: tType('AndroidSelector'), - percent: tNumber, - speed: tOptional(tNumber), - timeout: tNumber, + percent: tFloat, + speed: tOptional(tFloat), + timeout: tFloat, }); scheme.AndroidDevicePinchOpenResult = tOptional(tObject({})); scheme.AndroidDeviceScrollParams = tObject({ androidSelector: tType('AndroidSelector'), direction: tEnum(['up', 'down', 'left', 'right']), - percent: tNumber, - speed: tOptional(tNumber), - timeout: tNumber, + percent: tFloat, + speed: tOptional(tFloat), + timeout: tFloat, }); scheme.AndroidDeviceScrollResult = tOptional(tObject({})); scheme.AndroidDeviceSwipeParams = tObject({ androidSelector: tType('AndroidSelector'), direction: tEnum(['up', 'down', 'left', 'right']), - percent: tNumber, - speed: tOptional(tNumber), - timeout: tNumber, + percent: tFloat, + speed: tOptional(tFloat), + timeout: tFloat, }); scheme.AndroidDeviceSwipeResult = tOptional(tObject({})); scheme.AndroidDeviceInfoParams = tObject({ @@ -2700,24 +2707,24 @@ scheme.AndroidDeviceInputTapParams = tObject({ scheme.AndroidDeviceInputTapResult = tOptional(tObject({})); scheme.AndroidDeviceInputSwipeParams = tObject({ segments: tArray(tType('Point')), - steps: tNumber, + steps: tInt, }); scheme.AndroidDeviceInputSwipeResult = tOptional(tObject({})); scheme.AndroidDeviceInputDragParams = tObject({ from: tType('Point'), to: tType('Point'), - steps: tNumber, + steps: tInt, }); scheme.AndroidDeviceInputDragResult = tOptional(tObject({})); scheme.AndroidDeviceLaunchBrowserParams = tObject({ noDefaultViewport: tOptional(tBoolean), viewport: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), screen: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), ignoreHTTPSErrors: tOptional(tBoolean), clientCertificates: tOptional(tArray(tObject({ @@ -2733,9 +2740,9 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({ locale: tOptional(tString), timezoneId: tOptional(tString), geolocation: tOptional(tObject({ - longitude: tNumber, - latitude: tNumber, - accuracy: tOptional(tNumber), + longitude: tFloat, + latitude: tFloat, + accuracy: tOptional(tFloat), })), permissions: tOptional(tArray(tString)), extraHTTPHeaders: tOptional(tArray(tType('NameValue'))), @@ -2746,7 +2753,7 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({ origin: tOptional(tString), send: tOptional(tEnum(['always', 'unauthorized'])), })), - deviceScaleFactor: tOptional(tNumber), + deviceScaleFactor: tOptional(tFloat), isMobile: tOptional(tBoolean), hasTouch: tOptional(tBoolean), colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference', 'no-override'])), @@ -2758,8 +2765,8 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({ recordVideo: tOptional(tObject({ dir: tString, size: tOptional(tObject({ - width: tNumber, - height: tNumber, + width: tInt, + height: tInt, })), })), strictSelectors: tOptional(tBoolean), @@ -2798,7 +2805,7 @@ scheme.AndroidDeviceInstallApkResult = tOptional(tObject({})); scheme.AndroidDevicePushParams = tObject({ file: tBinary, path: tString, - mode: tOptional(tNumber), + mode: tOptional(tInt), }); scheme.AndroidDevicePushResult = tOptional(tObject({})); scheme.AndroidDeviceConnectToWebViewParams = tObject({ @@ -2810,7 +2817,7 @@ scheme.AndroidDeviceConnectToWebViewResult = tObject({ scheme.AndroidDeviceCloseParams = tOptional(tObject({})); scheme.AndroidDeviceCloseResult = tOptional(tObject({})); scheme.AndroidWebView = tObject({ - pid: tNumber, + pid: tInt, pkg: tString, socketName: tString, }); @@ -2819,7 +2826,7 @@ scheme.AndroidSelector = tObject({ checked: tOptional(tBoolean), clazz: tOptional(tString), clickable: tOptional(tBoolean), - depth: tOptional(tNumber), + depth: tOptional(tInt), desc: tOptional(tString), enabled: tOptional(tBoolean), focusable: tOptional(tBoolean), @@ -2829,7 +2836,7 @@ scheme.AndroidSelector = tObject({ })), hasDescendant: tOptional(tObject({ androidSelector: tType('AndroidSelector'), - maxDepth: tOptional(tNumber), + maxDepth: tOptional(tInt), })), longClickable: tOptional(tBoolean), pkg: tOptional(tString), diff --git a/packages/playwright-core/src/protocol/validatorPrimitives.ts b/packages/playwright-core/src/protocol/validatorPrimitives.ts index eadd2e014e67d..3c96c0748f8de 100644 --- a/packages/playwright-core/src/protocol/validatorPrimitives.ts +++ b/packages/playwright-core/src/protocol/validatorPrimitives.ts @@ -37,12 +37,24 @@ export function createMetadataValidator(): Validator { return tOptional(scheme['Metadata']); } -export const tNumber: Validator = (arg: any, path: string, context: ValidatorContext) => { +export const tFloat: Validator = (arg: any, path: string, context: ValidatorContext) => { if (arg instanceof Number) return arg.valueOf(); if (typeof arg === 'number') return arg; - throw new ValidationError(`${path}: expected number, got ${typeof arg}`); + throw new ValidationError(`${path}: expected float, got ${typeof arg}`); +}; +export const tInt: Validator = (arg: any, path: string, context: ValidatorContext) => { + let value: number; + if (arg instanceof Number) + value = arg.valueOf(); + else if (typeof arg === 'number') + value = arg; + else + throw new ValidationError(`${path}: expected integer, got ${typeof arg}`); + if (!Number.isInteger(value)) + throw new ValidationError(`${path}: expected integer, got float ${value}`); + return value; }; export const tBoolean: Validator = (arg: any, path: string, context: ValidatorContext) => { if (arg instanceof Boolean) diff --git a/packages/playwright-core/src/server/bidi/bidiBrowser.ts b/packages/playwright-core/src/server/bidi/bidiBrowser.ts index 5235b74a88dc8..76847bd5d36f1 100644 --- a/packages/playwright-core/src/server/bidi/bidiBrowser.ts +++ b/packages/playwright-core/src/server/bidi/bidiBrowser.ts @@ -199,6 +199,12 @@ export class BidiBrowserContext extends BrowserContext { promises.push(this.doUpdateDefaultViewport()); if (this._options.geolocation) promises.push(this.setGeolocation(this._options.geolocation)); + if (this._options.locale) { + promises.push(this._browser._browserSession.send('emulation.setLocaleOverride', { + locale: this._options.locale, + userContexts: [this._userContextId()], + })); + } await Promise.all(promises); } @@ -295,7 +301,7 @@ export class BidiBrowserContext extends BrowserContext { }, state, origin, - userContext: this._browserContextId || 'default', + userContext: this._userContextId(), }); } @@ -310,7 +316,7 @@ export class BidiBrowserContext extends BrowserContext { longitude: geolocation.longitude, accuracy: geolocation.accuracy, } : null, - userContexts: [this._browserContextId || 'default'], + userContexts: [this._userContextId()], }); } @@ -333,7 +339,7 @@ export class BidiBrowserContext extends BrowserContext { const { script } = await this._browser._browserSession.send('script.addPreloadScript', { // TODO: remove function call from the source. functionDeclaration: `() => { return ${initScript.source} }`, - userContexts: [this._browserContextId || 'default'], + userContexts: [this._userContextId()], }); this._initScriptIds.set(initScript, script); } diff --git a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts index 8456787030898..f4e4a801d6428 100644 --- a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts +++ b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts @@ -31,17 +31,15 @@ export class BidiNetworkManager { private readonly _requests: Map; private readonly _page: Page; private readonly _eventListeners: RegisteredListener[]; - private readonly _onNavigationResponseStarted: (params: bidi.Network.ResponseStartedParameters) => void; private _userRequestInterceptionEnabled: boolean = false; private _protocolRequestInterceptionEnabled: boolean = false; private _credentials: types.Credentials | undefined; private _intercepId: bidi.Network.Intercept | undefined; - constructor(bidiSession: BidiSession, page: Page, onNavigationResponseStarted: (params: bidi.Network.ResponseStartedParameters) => void) { + constructor(bidiSession: BidiSession, page: Page) { this._session = bidiSession; this._requests = new Map(); this._page = page; - this._onNavigationResponseStarted = onNavigationResponseStarted; this._eventListeners = [ eventsHelper.addEventListener(bidiSession, 'network.beforeRequestSent', this._onBeforeRequestSent.bind(this)), eventsHelper.addEventListener(bidiSession, 'network.responseStarted', this._onResponseStarted.bind(this)), @@ -116,8 +114,6 @@ export class BidiNetworkManager { response.setRawResponseHeaders(null); response.setResponseHeadersSize(params.response.headersSize); this._page.frameManager.requestReceivedResponse(response); - if (params.navigation) - this._onNavigationResponseStarted(params); } private _onResponseCompleted(params: bidi.Network.ResponseCompletedParameters) { diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index f1cdc14dfe256..97d07698f1dd5 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { assert } from '../../utils'; import { eventsHelper } from '../utils/eventsHelper'; import * as dialog from '../dialog'; import * as dom from '../dom'; @@ -61,7 +60,7 @@ export class BidiPage implements PageDelegate { this._realmToContext = new Map(); this._page = new Page(this, browserContext); this._browserContext = browserContext; - this._networkManager = new BidiNetworkManager(this._session, this._page, this._onNavigationResponseStarted.bind(this)); + this._networkManager = new BidiNetworkManager(this._session, this._page); this._pdf = new BidiPDF(this._session); this._page.on(Page.Events.FrameDetached, (frame: frames.Frame) => this._removeContextsForFrame(frame, false)); this._sessionListeners = [ @@ -69,6 +68,7 @@ export class BidiPage implements PageDelegate { eventsHelper.addEventListener(bidiSession, 'script.message', this._onScriptMessage.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.contextDestroyed', this._onBrowsingContextDestroyed.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationStarted', this._onNavigationStarted.bind(this)), + eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationCommitted', this._onNavigationCommitted.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationAborted', this._onNavigationAborted.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationFailed', this._onNavigationFailed.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.fragmentNavigated', this._onFragmentNavigated.bind(this)), @@ -178,25 +178,11 @@ export class BidiPage implements PageDelegate { private _onNavigationStarted(params: bidi.BrowsingContext.NavigationInfo) { const frameId = params.context; this._page.frameManager.frameRequestedNavigation(frameId, params.navigation!); - - const url = params.url.toLowerCase(); - if (url.startsWith('file:') || url.startsWith('data:') || url === 'about:blank') { - // Navigation to file urls doesn't emit network events, so we fire 'commit' event right when navigation is started. - // Doing it in domcontentload would be too late as we'd clear frame tree. - const frame = this._page.frameManager.frame(frameId)!; - if (frame) - this._page.frameManager.frameCommittedNewDocumentNavigation(frameId, params.url, '', params.navigation!, /* initial */ false); - } } - // TODO: there is no separate event for committed navigation, so we approximate it with responseStarted. - private _onNavigationResponseStarted(params: bidi.Network.ResponseStartedParameters) { - const frameId = params.context!; - const frame = this._page.frameManager.frame(frameId); - assert(frame); - this._page.frameManager.frameCommittedNewDocumentNavigation(frameId, params.response.url, '', params.navigation!, /* initial */ false); - // if (!initial) - // this._firstNonInitialNavigationCommittedFulfill(); + private _onNavigationCommitted(params: bidi.BrowsingContext.NavigationInfo) { + const frameId = params.context; + this._page.frameManager.frameCommittedNewDocumentNavigation(frameId, params.url, '', params.navigation!, /* initial */ false); } private _onDomContentLoaded(params: bidi.BrowsingContext.NavigationInfo) { diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts b/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts index 5463cbaf7791b..5be629678aeaf 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts @@ -118,6 +118,11 @@ export interface Commands { returnType: Bidi.EmptyResult; }; + 'emulation.setLocaleOverride': { + params: Bidi.Emulation.SetLocaleOverrideParameters; + returnType: Bidi.EmptyResult; + }; + 'permissions.setPermission': { params: Bidi.Permissions.SetPermissionParameters; returnType: Bidi.EmptyResult; diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts b/packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts index 81e9e8b713009..5d9bedd66261e 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts @@ -1,8 +1,18 @@ /** - * @license - * Copyright 2024 Google Inc. - * Modifications copyright (c) Microsoft Corporation. - * SPDX-License-Identifier: Apache-2.0 + * Copyright 2024 Google LLC. + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ // Copied from upstream: https://github.com/GoogleChromeLabs/chromium-bidi/blob/main/src/protocol/generated/webdriver-bidi.ts @@ -80,11 +90,13 @@ export const enum ErrorCode { InvalidWebExtension = 'invalid web extension', MoveTargetOutOfBounds = 'move target out of bounds', NoSuchAlert = 'no such alert', + NoSuchNetworkCollector = 'no such network collector', NoSuchElement = 'no such element', NoSuchFrame = 'no such frame', NoSuchHandle = 'no such handle', NoSuchHistoryEntry = 'no such history entry', NoSuchIntercept = 'no such intercept', + NoSuchNetworkData = 'no such network data', NoSuchNode = 'no such node', NoSuchRequest = 'no such request', NoSuchScript = 'no such script', @@ -96,6 +108,7 @@ export const enum ErrorCode { UnableToCloseBrowser = 'unable to close browser', UnableToSetCookie = 'unable to set cookie', UnableToSetFileInput = 'unable to set file input', + UnavailableNetworkData = 'unavailable network data', UnderspecifiedStoragePartition = 'underspecified storage partition', UnknownCommand = 'unknown command', UnknownError = 'unknown error', @@ -148,7 +161,6 @@ export namespace Session { export namespace Session { export type ManualProxyConfiguration = { proxyType: 'manual'; - ftpProxy?: string; httpProxy?: string; sslProxy?: string; } & ({} | Session.SocksProxyConfiguration) & { @@ -334,6 +346,7 @@ export namespace Browser { export type CreateUserContextParameters = { acceptInsecureCerts?: boolean; proxy?: Session.ProxyConfiguration; + unhandledPromptBehavior?: Session.UserPromptHandler; }; } export namespace Browser { @@ -414,6 +427,7 @@ export type BrowsingContextEvent = | BrowsingContext.ContextCreated | BrowsingContext.ContextDestroyed | BrowsingContext.DomContentLoaded + | BrowsingContext.DownloadEnd | BrowsingContext.DownloadWillBegin | BrowsingContext.FragmentNavigated | BrowsingContext.HistoryUpdated @@ -866,6 +880,7 @@ export namespace BrowsingContext { export namespace BrowsingContext { export type HistoryUpdatedParameters = { context: BrowsingContext.BrowsingContext; + timestamp: JsUint; url: string; }; } @@ -892,6 +907,28 @@ export namespace BrowsingContext { suggestedFilename: string; } & BrowsingContext.BaseNavigationInfo; } +export namespace BrowsingContext { + export type DownloadEnd = { + method: 'browsingContext.downloadEnd'; + params: BrowsingContext.DownloadEndParams; + }; +} +export namespace BrowsingContext { + export type DownloadEndParams = + | BrowsingContext.DownloadCanceledParams + | BrowsingContext.DownloadCompleteParams; +} +export namespace BrowsingContext { + export type DownloadCanceledParams = { + status: 'canceled'; + } & BrowsingContext.BaseNavigationInfo; +} +export namespace BrowsingContext { + export type DownloadCompleteParams = { + status: 'complete'; + filepath: string | null; + } & BrowsingContext.BaseNavigationInfo; +} export namespace BrowsingContext { export type NavigationAborted = { method: 'browsingContext.navigationAborted'; @@ -939,7 +976,34 @@ export namespace BrowsingContext { defaultValue?: string; }; } -export type EmulationCommand = Emulation.SetGeolocationOverride; +export type EmulationCommand = + | Emulation.SetForcedColorsModeThemeOverride + | Emulation.SetGeolocationOverride + | Emulation.SetLocaleOverride + | Emulation.SetScreenOrientationOverride + | Emulation.SetTimezoneOverride; +export namespace Emulation { + export type SetForcedColorsModeThemeOverride = { + method: 'emulation.setForcedColorsModeThemeOverride'; + params: Emulation.SetForcedColorsModeThemeOverrideParameters; + }; +} +export namespace Emulation { + export type SetForcedColorsModeThemeOverrideParameters = { + theme: Emulation.ForcedColorsModeTheme | null; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} +export namespace Emulation { + export const enum ForcedColorsModeTheme { + Light = 'light', + Dark = 'dark', + } +} export namespace Emulation { export type SetGeolocationOverride = { method: 'emulation.setGeolocationOverride'; @@ -1007,15 +1071,87 @@ export namespace Emulation { type: 'positionUnavailable'; }; } +export namespace Emulation { + export type SetLocaleOverride = { + method: 'emulation.setLocaleOverride'; + params: Emulation.SetLocaleOverrideParameters; + }; +} +export namespace Emulation { + export type SetLocaleOverrideParameters = { + locale: string | null; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} +export namespace Emulation { + export type SetScreenOrientationOverride = { + method: 'emulation.setScreenOrientationOverride'; + params: Emulation.SetScreenOrientationOverrideParameters; + }; +} +export namespace Emulation { + export const enum ScreenOrientationNatural { + Portrait = 'portrait', + Landscape = 'landscape', + } +} +export namespace Emulation { + export type ScreenOrientationType = + | 'portrait-primary' + | 'portrait-secondary' + | 'landscape-primary' + | 'landscape-secondary'; +} +export namespace Emulation { + export type ScreenOrientation = { + natural: Emulation.ScreenOrientationNatural; + type: Emulation.ScreenOrientationType; + }; +} +export namespace Emulation { + export type SetScreenOrientationOverrideParameters = { + screenOrientation: Emulation.ScreenOrientation | null; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} +export namespace Emulation { + export type SetTimezoneOverride = { + method: 'emulation.setTimezoneOverride'; + params: Emulation.SetTimezoneOverrideParameters; + }; +} +export namespace Emulation { + export type SetTimezoneOverrideParameters = { + timezone: string | null; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} export type NetworkCommand = + | Network.AddDataCollector | Network.AddIntercept | Network.ContinueRequest | Network.ContinueResponse | Network.ContinueWithAuth + | Network.DisownData | Network.FailRequest + | Network.GetData | Network.ProvideResponse + | Network.RemoveDataCollector | Network.RemoveIntercept - | Network.SetCacheBehavior; + | Network.SetCacheBehavior + | Network.SetExtraHeaders; export type NetworkEvent = | Network.AuthRequired | Network.BeforeRequestSent @@ -1062,11 +1198,20 @@ export namespace Network { value: string; }; } +export namespace Network { + export type Collector = string; +} +export namespace Network { + export const enum CollectorType { + Blob = 'blob', + } +} export namespace Network { export const enum SameSite { Strict = 'strict', Lax = 'lax', None = 'none', + Default = 'default', } } export namespace Network { @@ -1088,6 +1233,11 @@ export namespace Network { value: Network.BytesValue; }; } +export namespace Network { + export const enum DataType { + Response = 'response', + } +} export namespace Network { export type FetchTimingInfo = { timeOrigin: number; @@ -1193,6 +1343,32 @@ export namespace Network { pattern: string; }; } +export namespace Network { + export type AddDataCollector = { + method: 'network.addDataCollector'; + params: Network.AddDataCollectorParameters; + }; +} +export namespace Network { + export type AddDataCollectorParameters = { + dataTypes: [Network.DataType, ...Network.DataType[]]; + maxEncodedDataSize: JsUint; + /** + * @defaultValue `"blob"` + */ + collectorType?: Network.CollectorType; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} +export namespace Network { + export type AddDataCollectorResult = { + collector: Network.Collector; + }; +} export namespace Network { export type AddInterceptParameters = { phases: [Network.InterceptPhase, ...Network.InterceptPhase[]]; @@ -1278,6 +1454,19 @@ export namespace Network { action: 'default' | 'cancel'; }; } +export namespace Network { + export type DisownData = { + method: 'network.disownData'; + params: Network.DisownDataParameters; + }; +} +export namespace Network { + export type DisownDataParameters = { + dataType: Network.DataType; + collector: Network.Collector; + request: Network.Request; + }; +} export namespace Network { export type FailRequest = { method: 'network.failRequest'; @@ -1289,6 +1478,28 @@ export namespace Network { request: Network.Request; }; } +export namespace Network { + export type GetData = { + method: 'network.getData'; + params: Network.GetDataParameters; + }; +} +export namespace Network { + export type GetDataParameters = { + dataType: Network.DataType; + collector?: Network.Collector; + /** + * @defaultValue `false` + */ + disown?: boolean; + request: Network.Request; + }; +} +export namespace Network { + export type GetDataResult = { + bytes: Network.BytesValue; + }; +} export namespace Network { export type ProvideResponse = { method: 'network.provideResponse'; @@ -1305,6 +1516,17 @@ export namespace Network { statusCode?: JsUint; }; } +export namespace Network { + export type RemoveDataCollector = { + method: 'network.removeDataCollector'; + params: Network.RemoveDataCollectorParameters; + }; +} +export namespace Network { + export type RemoveDataCollectorParameters = { + collector: Network.Collector; + }; +} export namespace Network { export type RemoveIntercept = { method: 'network.removeIntercept'; @@ -1331,6 +1553,22 @@ export namespace Network { ]; }; } +export namespace Network { + export type SetExtraHeaders = { + method: 'network.setExtraHeaders'; + params: Network.SetExtraHeadersParameters; + }; +} +export namespace Network { + export type SetExtraHeadersParameters = { + headers: [Network.Header, ...Network.Header[]]; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} export type ScriptEvent = | Script.Message | Script.RealmCreated diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index 3ae466f74ba90..d91f2f9668c67 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -213,7 +213,7 @@ export abstract class BrowserContext extends SdkObject { await progress.race(this.setUserAgent(this._options.userAgent)); await progress.race(this.doUpdateDefaultEmulatedMedia()); await progress.race(this.doUpdateDefaultViewport()); - await this.setStorageState(progress, this._options.storageState, 'reset'); + await this.setStorageState(progress, this._options.storageState, 'resetForReuse'); await page?.resetForReuse(progress); } @@ -612,11 +612,11 @@ export abstract class BrowserContext extends SdkObject { return this._creatingStorageStatePage; } - async setStorageState(progress: Progress, state: channels.BrowserNewContextParams['storageState'], mode: 'initial' | 'reset') { + async setStorageState(progress: Progress, state: channels.BrowserNewContextParams['storageState'], mode: 'initial' | 'resetForReuse' | 'update') { let page: Page | undefined; let interceptor: network.RouteHandler | undefined; try { - if (mode === 'reset') { + if (mode !== 'initial') { await progress.race(this.clearCache()); await progress.race(this.doClearCookies()); } @@ -627,11 +627,11 @@ export abstract class BrowserContext extends SdkObject { const newOrigins = new Map(state?.origins?.map(p => [p.origin, p]) || []); const allOrigins = new Set([...this._origins, ...newOrigins.keys()]); if (allOrigins.size) { - if (mode === 'reset') + if (mode === 'resetForReuse') page = this.pages()[0]; if (!page) { try { - this._creatingStorageStatePage = mode === 'initial'; + this._creatingStorageStatePage = mode !== 'resetForReuse'; page = await this.newPage(progress, this._creatingStorageStatePage); } finally { this._creatingStorageStatePage = false; @@ -660,7 +660,7 @@ export abstract class BrowserContext extends SdkObject { rewriteErrorMessage(error, `Error setting storage state:\n` + error.message); throw error; } finally { - if (mode === 'initial') + if (mode !== 'resetForReuse') await page?.close(); else if (interceptor) await page?.removeRequestInterceptor(interceptor); diff --git a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts index f490971ba3913..cedf70082c0f8 100644 --- a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts +++ b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts @@ -38,6 +38,8 @@ const disabledFeatures = (assistantMode?: boolean) => [ 'ThirdPartyStoragePartitioning', // See https://github.com/microsoft/playwright/issues/16126 'Translate', + // See https://issues.chromium.org/u/1/issues/435410220 + 'AutoDeElevate', assistantMode ? 'AutomationControlled' : '', ].filter(Boolean); diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index 15c8f84fd0054..6803d00e52429 100644 --- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -330,6 +330,10 @@ export class BrowserContextDispatcher extends Dispatcher { + await this._context.setStorageState(progress, params.storageState, 'update'); + } + async close(params: channels.BrowserContextCloseParams, progress: Progress): Promise { progress.metadata.potentiallyClosesScope = true; await this._context.close(params); diff --git a/packages/playwright-core/src/server/instrumentation.ts b/packages/playwright-core/src/server/instrumentation.ts index 9dc29950c86fc..947c929390b66 100644 --- a/packages/playwright-core/src/server/instrumentation.ts +++ b/packages/playwright-core/src/server/instrumentation.ts @@ -121,6 +121,5 @@ export function serverSideCallMetadata(): CallMetadata { method: '', params: {}, log: [], - isServerSide: true, }; } diff --git a/packages/playwright-core/src/server/launchApp.ts b/packages/playwright-core/src/server/launchApp.ts index 8eee438d84022..1dc29e983f54b 100644 --- a/packages/playwright-core/src/server/launchApp.ts +++ b/packages/playwright-core/src/server/launchApp.ts @@ -17,9 +17,9 @@ import fs from 'fs'; import path from 'path'; -import { isUnderTest } from '../utils'; +import { isUnderTest, rewriteErrorMessage, wrapInASCIIBox } from '../utils'; import { serverSideCallMetadata } from './instrumentation'; -import { findChromiumChannel } from './registry'; +import { buildPlaywrightCLICommand, findChromiumChannelBestEffort } from './registry'; import { registryDirectory } from './registry'; import { ProgressController } from './progress'; @@ -46,19 +46,32 @@ export async function launchApp(browserType: BrowserType, options: { '--test-type=', ); if (!channel && !options.persistentContextOptions?.executablePath) - channel = findChromiumChannel(options.sdkLanguage); + channel = findChromiumChannelBestEffort(options.sdkLanguage); } const controller = new ProgressController(serverSideCallMetadata(), browserType); - const context = await controller.run(progress => browserType.launchPersistentContext(progress, '', { - ignoreDefaultArgs: ['--enable-automation'], - ...options?.persistentContextOptions, - channel, - noDefaultViewport: options.persistentContextOptions?.noDefaultViewport ?? true, - acceptDownloads: options?.persistentContextOptions?.acceptDownloads ?? (isUnderTest() ? 'accept' : 'internal-browser-default'), - colorScheme: options?.persistentContextOptions?.colorScheme ?? 'no-override', - args, - }), 0); // Deliberately no timeout for our apps. + let context; + try { + context = await controller.run(progress => browserType.launchPersistentContext(progress, '', { + ignoreDefaultArgs: ['--enable-automation'], + ...options?.persistentContextOptions, + channel, + noDefaultViewport: options.persistentContextOptions?.noDefaultViewport ?? true, + acceptDownloads: options?.persistentContextOptions?.acceptDownloads ?? (isUnderTest() ? 'accept' : 'internal-browser-default'), + colorScheme: options?.persistentContextOptions?.colorScheme ?? 'no-override', + args, + }), 0); // Deliberately no timeout for our apps. + } catch (error) { + if (channel) { + error = rewriteErrorMessage(error, [ + `Failed to launch "${channel}" channel.`, + 'Using custom channels could lead to unexpected behavior due to Enterprise policies (chrome://policy).', + 'Install the default browser instead:', + wrapInASCIIBox(`${buildPlaywrightCLICommand(options.sdkLanguage, 'install')}`, 2), + ].join('\n')); + } + throw error; + } const [page] = context.pages(); // Chromium on macOS opens a new tab when clicking on the dock icon. // See https://github.com/microsoft/playwright/issues/9434 diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index fde6b2f1017de..36ee1d92d5e38 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -1363,7 +1363,8 @@ export async function installBrowsersForNpmInstall(browsers: string[]) { await registry.install(executables, false /* forceReinstall */); } -export function findChromiumChannel(sdkLanguage: string): string | undefined { +// for launchApp -> UI Mode / Trace Viewer +export function findChromiumChannelBestEffort(sdkLanguage: string): string | undefined { // Fall back to the stable channels of popular vendors to work out of the box. // Null means no installation and no channels found. let channel = null; diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index 37148ddb448cf..219438ef0b14a 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -459,7 +459,9 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps } onCallLog(sdkObject: SdkObject, metadata: CallMetadata, logName: string, message: string) { - if (metadata.isServerSide || metadata.internal) + if (!this._state?.callIds.has(metadata.id)) + return; + if (metadata.internal) return; if (logName !== 'api') return; diff --git a/packages/playwright-core/src/utils.ts b/packages/playwright-core/src/utils.ts index 4037c44c14c2c..db049937ce068 100644 --- a/packages/playwright-core/src/utils.ts +++ b/packages/playwright-core/src/utils.ts @@ -14,8 +14,9 @@ * limitations under the License. */ -export * from './utils/isomorphic/colors'; +export * from './utils/isomorphic/ariaSnapshot'; export * from './utils/isomorphic/assert'; +export * from './utils/isomorphic/colors'; export * from './utils/isomorphic/headers'; export * from './utils/isomorphic/locatorGenerators'; export * from './utils/isomorphic/manualPromise'; diff --git a/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts b/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts index 87121b0bed5bb..c85f04c599144 100644 --- a/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts +++ b/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts @@ -82,6 +82,7 @@ export const methodMetainfo = new Map; + /** + * Resets storage state in the context by clearing cookies, cache and storage, and then applying the new storage + * state. + * @param storageState Learn more about [storage state and auth](https://playwright.dev/docs/auth). + * + * Populates context with given storage state. This option can be used to initialize context with logged-in + * information obtained via + * [browserContext.storageState([options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state). + */ + setStorageState(storageState: string|{ + /** + * Cookies to set for context + */ + cookies: Array<{ + name: string; + + value: string; + + /** + * Domain and path are required. For the cookie to apply to all subdomains as well, prefix domain with a dot, like + * this: ".example.com" + */ + domain: string; + + /** + * Domain and path are required + */ + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + /** + * sameSite flag + */ + sameSite: "Strict"|"Lax"|"None"; + }>; + + origins: Array<{ + origin: string; + + /** + * localStorage to set for context + */ + localStorage: Array<{ + name: string; + + value: string; + }>; + }>; + }): Promise; + /** * Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB * snapshot. diff --git a/packages/protocol/src/callMetadata.d.ts b/packages/protocol/src/callMetadata.d.ts index 076abd2dc656a..f0a26b47ad524 100644 --- a/packages/protocol/src/callMetadata.d.ts +++ b/packages/protocol/src/callMetadata.d.ts @@ -29,9 +29,6 @@ export type CallMetadata = { // Client is making an internal call that should not show up in // the inspector or trace. internal?: boolean; - // Service-side is making a call to itself, this metadata does not go - // through the dispatcher, so is always excluded from inspector / tracing. - isServerSide?: boolean; // Test runner step id. stepId?: string; location?: { file: string, line?: number, column?: number }; diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 33fce000bfee0..f753e2825535f 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -1612,6 +1612,7 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT setWebSocketInterceptionPatterns(params: BrowserContextSetWebSocketInterceptionPatternsParams, progress?: Progress): Promise; setOffline(params: BrowserContextSetOfflineParams, progress?: Progress): Promise; storageState(params: BrowserContextStorageStateParams, progress?: Progress): Promise; + setStorageState(params: BrowserContextSetStorageStateParams, progress?: Progress): Promise; pause(params?: BrowserContextPauseParams, progress?: Progress): Promise; enableRecorder(params: BrowserContextEnableRecorderParams, progress?: Progress): Promise; disableRecorder(params?: BrowserContextDisableRecorderParams, progress?: Progress): Promise; @@ -1862,6 +1863,16 @@ export type BrowserContextStorageStateResult = { cookies: NetworkCookie[], origins: OriginStorage[], }; +export type BrowserContextSetStorageStateParams = { + storageState: { + cookies?: SetNetworkCookie[], + origins?: SetOriginStorage[], + }, +}; +export type BrowserContextSetStorageStateOptions = { + +}; +export type BrowserContextSetStorageStateResult = void; export type BrowserContextPauseParams = {}; export type BrowserContextPauseOptions = {}; export type BrowserContextPauseResult = void; diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 5bd36be29a415..ab25ad60502e5 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -16,8 +16,8 @@ StackFrame: type: object properties: file: string - line: number - column: number + line: int + column: int function: string? # This object can be send with any rpc call in the "metadata" field. @@ -29,8 +29,8 @@ Metadata: type: object? properties: file: string - line: number? - column: number? + line: int? + column: int? title: string? internal: boolean? # Test runner step id. @@ -39,7 +39,7 @@ Metadata: ClientSideCallMetadata: type: object properties: - id: number + id: int stack: type: array? items: StackFrame @@ -47,24 +47,24 @@ ClientSideCallMetadata: Point: type: object properties: - x: number - y: number + x: float + y: float Rect: type: object properties: - x: number - y: number - width: number - height: number + x: float + y: float + width: float + height: float SerializedValue: type: object # Exactly one of the properties must be present. properties: - n: number? + n: float? b: boolean? s: string? v: @@ -126,11 +126,11 @@ SerializedValue: k: string v: SerializedValue # An index in the handles array from SerializedArgument. - h: number? + h: int? # Index of the object in value-type for circular reference resolution. - id: number? + id: int? # Ref to the object in value-type for circular reference resolution. - ref: number? + ref: int? # Represents a value with handle references. @@ -168,7 +168,7 @@ AXNode: role: string name: string valueString: string? - valueNumber: number? + valueNumber: float? description: string? keyshortcuts: string? roledescription: string? @@ -194,9 +194,9 @@ AXNode: - pressed - released - mixed - level: number? - valuemin: number? - valuemax: number? + level: int? + valuemin: float? + valuemax: float? autocomplete: string? haspopup: string? invalid: string? @@ -214,7 +214,7 @@ SetNetworkCookie: url: string? domain: string? path: string? - expires: number? + expires: float? httpOnly: boolean? secure: boolean? sameSite: @@ -234,7 +234,7 @@ NetworkCookie: value: string domain: string path: string - expires: number + expires: float httpOnly: boolean secure: boolean sameSite: @@ -257,7 +257,7 @@ IndexedDBDatabase: type: object properties: name: string - version: number + version: int stores: type: array items: @@ -393,11 +393,11 @@ APIRequestContext: multipartData: type: array? items: FormField - timeout: number + timeout: float failOnStatusCode: boolean? ignoreHTTPSErrors: boolean? - maxRedirects: number? - maxRetries: number? + maxRedirects: int? + maxRetries: int? returns: response: APIResponse @@ -445,7 +445,7 @@ APIResponse: properties: fetchUid: string url: string - status: number + status: int statusText: string headers: type: array @@ -505,7 +505,7 @@ LaunchOptions: handleSIGINT: boolean? handleSIGTERM: boolean? handleSIGHUP: boolean? - timeout: number + timeout: float env: type: array? items: NameValue @@ -522,7 +522,7 @@ LaunchOptions: tracesDir: string? chromiumSandbox: boolean? firefoxUserPrefs: json? - cdpPort: number? + cdpPort: int? ContextOptions: @@ -532,13 +532,13 @@ ContextOptions: viewport: type: object? properties: - width: number - height: number + width: int + height: int screen: type: object? properties: - width: number - height: number + width: int + height: int ignoreHTTPSErrors: boolean? clientCertificates: type: array? @@ -558,9 +558,9 @@ ContextOptions: geolocation: type: object? properties: - longitude: number - latitude: number - accuracy: number? + longitude: float + latitude: float + accuracy: float? permissions: type: array? items: string @@ -579,7 +579,7 @@ ContextOptions: literals: - always - unauthorized - deviceScaleFactor: number? + deviceScaleFactor: float? isMobile: boolean? hasTouch: boolean? colorScheme: @@ -621,8 +621,8 @@ ContextOptions: size: type: object? properties: - width: number - height: number + width: int + height: int strictSelectors: boolean? serviceWorkers: type: enum? @@ -652,14 +652,14 @@ LocalUtils: viewport: type: object properties: - width: number - height: number + width: int + height: int screen: type: object? properties: - width: number - height: number - deviceScaleFactor: number + width: int + height: int + deviceScaleFactor: float isMobile: boolean hasTouch: boolean defaultBrowserType: @@ -715,7 +715,7 @@ LocalUtils: - noentry message: string? redirectURL: string? - status: number? + status: int? headers: type: array? items: NameValue @@ -738,9 +738,9 @@ LocalUtils: wsEndpoint: string headers: json? exposeNetwork: string? - slowMo: number? - timeout: number - socksProxyRedirectPortForTest: number? + slowMo: float? + timeout: float + socksProxyRedirectPortForTest: int? returns: pipe: JsonPipe headers: @@ -818,7 +818,7 @@ Playwright: key: binary? passphrase: string? pfx: binary? - maxRedirects: number? + maxRedirects: int? httpCredentials: type: object? properties: @@ -864,9 +864,9 @@ RecorderSource: items: type: object properties: - line: number + line: int type: string - revealLine: number? + revealLine: int? group: string? DebugController: @@ -923,7 +923,7 @@ DebugController: stateChanged: parameters: - pageCount: number + pageCount: int sourceChanged: parameters: @@ -947,7 +947,7 @@ SocksSupport: parameters: uid: string host: string - port: number + port: int socksFailed: internal: true @@ -977,7 +977,7 @@ SocksSupport: parameters: uid: string host: string - port: number + port: int socksData: parameters: @@ -1002,7 +1002,7 @@ BrowserType: title: Launch browser parameters: $mixin: LaunchOptions - slowMo: number? + slowMo: float? returns: browser: Browser @@ -1012,7 +1012,7 @@ BrowserType: $mixin1: LaunchOptions $mixin2: ContextOptions userDataDir: string - slowMo: number? + slowMo: float? returns: browser: Browser context: BrowserContext @@ -1024,8 +1024,8 @@ BrowserType: headers: type: array? items: NameValue - slowMo: number? - timeout: number + slowMo: float? + timeout: float returns: browser: Browser defaultContext: BrowserContext? @@ -1143,8 +1143,8 @@ ConsoleMessage: type: object properties: url: string - lineNumber: number - columnNumber: number + lineNumber: int + columnNumber: int EventTarget: @@ -1272,9 +1272,9 @@ BrowserContext: geolocation: type: object? properties: - longitude: number - latitude: number - accuracy: number? + longitude: float + latitude: float + accuracy: float? setHTTPCredentials: title: Set HTTP credentials @@ -1327,6 +1327,19 @@ BrowserContext: type: array items: OriginStorage + setStorageState: + title: Set storage state + parameters: + storageState: + type: object + properties: + cookies: + type: array? + items: SetNetworkCookie + origins: + type: array? + items: SetOriginStorage + pause: title: Pause @@ -1390,7 +1403,7 @@ BrowserContext: type: object properties: name: string - lastModifiedMs: number? + lastModifiedMs: float? returns: rootDir: WritableStream? writableStreams: @@ -1414,19 +1427,19 @@ BrowserContext: clockFastForward: title: Fast forward clock "{ticksNumber}{ticksString}" parameters: - ticksNumber: number? + ticksNumber: float? ticksString: string? clockInstall: title: Install clock "{timeNumber}{timeString}" parameters: - timeNumber: number? + timeNumber: float? timeString: string? clockPauseAt: title: Pause clock "{timeNumber}{timeString}" parameters: - timeNumber: number? + timeNumber: float? timeString: string? clockResume: @@ -1435,19 +1448,19 @@ BrowserContext: clockRunFor: title: Run clock "{ticksNumber}{ticksString}" parameters: - ticksNumber: number? + ticksNumber: float? ticksString: string? clockSetFixedTime: title: Set fixed time "{timeNumber}{timeString}" parameters: - timeNumber: number? + timeNumber: float? timeString: string? clockSetSystemTime: title: Set system time "{timeNumber}{timeString}" parameters: - timeNumber: number? + timeNumber: float? timeString: string? clockUninstall: @@ -1508,14 +1521,14 @@ BrowserContext: parameters: request: Request failureText: string? - responseEndTiming: number + responseEndTiming: float page: Page? requestFinished: parameters: request: Request response: Response? - responseEndTiming: number + responseEndTiming: float page: Page? response: @@ -1545,8 +1558,8 @@ Page: viewportSize: type: object? properties: - width: number - height: number + width: int + height: int isClosed: boolean opener: Page? @@ -1608,7 +1621,7 @@ Page: goBack: title: Go back parameters: - timeout: number + timeout: float waitUntil: LifecycleEvent? returns: response: Response? @@ -1619,7 +1632,7 @@ Page: goForward: title: Go forward parameters: - timeout: number + timeout: float waitUntil: LifecycleEvent? returns: response: Response? @@ -1636,23 +1649,23 @@ Page: selector: string noWaitAfter: boolean? returns: - uid: number + uid: int resolveLocatorHandlerNoReply: internal: true parameters: - uid: number + uid: int remove: boolean? unregisterLocatorHandler: title: Unregister locator handler parameters: - uid: number + uid: int reload: title: Reload parameters: - timeout: number + timeout: float waitUntil: LifecycleEvent? returns: response: Response? @@ -1664,7 +1677,7 @@ Page: title: Expect screenshot parameters: expected: binary? - timeout: number + timeout: float isNot: boolean locator: type: object? @@ -1672,9 +1685,9 @@ Page: frame: Frame selector: string comparator: string? - maxDiffPixels: number? - maxDiffPixelRatio: number? - threshold: number? + maxDiffPixels: int? + maxDiffPixelRatio: float? + threshold: float? fullPage: boolean? clip: Rect? $mixin: CommonScreenshotOptions @@ -1693,13 +1706,13 @@ Page: screenshot: title: Screenshot parameters: - timeout: number + timeout: float type: type: enum? literals: - png - jpeg - quality: number? + quality: int? fullPage: boolean? clip: Rect? $mixin: CommonScreenshotOptions @@ -1745,8 +1758,8 @@ Page: viewportSize: type: object properties: - width: number - height: number + width: int + height: int flags: snapshot: true @@ -1778,7 +1791,7 @@ Page: title: Type "{text}" parameters: text: string - delay: number? + delay: float? flags: slowMo: true snapshot: true @@ -1787,7 +1800,7 @@ Page: title: Press "{key}" parameters: key: string - delay: number? + delay: float? flags: slowMo: true snapshot: true @@ -1795,9 +1808,9 @@ Page: mouseMove: title: Mouse move parameters: - x: number - y: number - steps: number? + x: float + y: float + steps: int? flags: slowMo: true snapshot: true @@ -1811,7 +1824,7 @@ Page: - left - right - middle - clickCount: number? + clickCount: int? flags: slowMo: true snapshot: true @@ -1825,7 +1838,7 @@ Page: - left - right - middle - clickCount: number? + clickCount: int? flags: slowMo: true snapshot: true @@ -1833,16 +1846,16 @@ Page: mouseClick: title: Click parameters: - x: number - y: number - delay: number? + x: float + y: float + delay: float? button: type: enum? literals: - left - right - middle - clickCount: number? + clickCount: int? flags: slowMo: true snapshot: true @@ -1850,8 +1863,8 @@ Page: mouseWheel: title: Mouse wheel parameters: - deltaX: number - deltaY: number + deltaX: float + deltaY: float flags: slowMo: true snapshot: true @@ -1859,8 +1872,8 @@ Page: touchscreenTap: title: Tap parameters: - x: number - y: number + x: float + y: float flags: slowMo: true snapshot: true @@ -1878,7 +1891,7 @@ Page: pdf: title: PDF parameters: - scale: number? + scale: float? displayHeaderFooter: boolean? headerTemplate: string? footerTemplate: string? @@ -1904,7 +1917,7 @@ Page: snapshotForAI: internal: true parameters: - timeout: number + timeout: float returns: snapshot: string flags: @@ -1939,9 +1952,9 @@ Page: items: type: object properties: - startOffset: number - endOffset: number - count: number + startOffset: int + endOffset: int + count: int startCSSCoverage: internal: true @@ -1963,8 +1976,8 @@ Page: items: type: object properties: - start: number - end: number + start: int + end: int bringToFront: title: Bring to front @@ -2005,8 +2018,8 @@ Page: viewportSize: type: object? properties: - width: number - height: number + width: int + height: int fileChooser: parameters: @@ -2023,7 +2036,7 @@ Page: locatorHandlerTriggered: parameters: - uid: number + uid: int route: parameters: @@ -2110,7 +2123,7 @@ Frame: title: Aria snapshot parameters: selector: string - timeout: number + timeout: float returns: snapshot: string flags: @@ -2121,7 +2134,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -2133,7 +2146,7 @@ Frame: strict: boolean? force: boolean? position: Point? - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -2158,15 +2171,15 @@ Frame: - Meta - Shift position: Point? - delay: number? + delay: float? button: type: enum? literals: - left - right - middle - clickCount: number? - timeout: number + clickCount: int? + timeout: float trial: boolean? flags: slowMo: true @@ -2186,7 +2199,7 @@ Frame: source: string target: string force: boolean? - timeout: number + timeout: float trial: boolean? sourcePosition: Point? targetPosition: Point? @@ -2213,14 +2226,14 @@ Frame: - Meta - Shift position: Point? - delay: number? + delay: float? button: type: enum? literals: - left - right - middle - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -2234,7 +2247,7 @@ Frame: strict: boolean? type: string eventInit: SerializedArgument - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -2268,7 +2281,7 @@ Frame: strict: boolean? value: string force: boolean? - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -2279,7 +2292,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -2307,7 +2320,7 @@ Frame: selector: string strict: boolean? name: string - timeout: number + timeout: float returns: value: string? flags: @@ -2317,7 +2330,7 @@ Frame: title: Navigate to "{url}" parameters: url: string - timeout: number + timeout: float waitUntil: LifecycleEvent? referer: string? returns: @@ -2343,7 +2356,7 @@ Frame: - Meta - Shift position: Point? - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -2355,7 +2368,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float returns: value: string flags: @@ -2366,7 +2379,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float returns: value: string flags: @@ -2377,7 +2390,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float returns: value: string flags: @@ -2388,7 +2401,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float returns: value: boolean flags: @@ -2399,7 +2412,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float returns: value: boolean flags: @@ -2410,7 +2423,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float returns: value: boolean flags: @@ -2443,7 +2456,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float returns: value: boolean flags: @@ -2455,9 +2468,9 @@ Frame: selector: string strict: boolean? key: string - delay: number? + delay: float? noWaitAfter: boolean? - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -2489,7 +2502,7 @@ Frame: parameters: selector: string returns: - value: number + value: int flags: snapshot: true @@ -2509,9 +2522,9 @@ Frame: valueOrLabel: string? value: string? label: string? - index: number? + index: int? force: boolean? - timeout: number + timeout: float returns: values: type: array @@ -2525,7 +2538,7 @@ Frame: title: Set content parameters: html: string - timeout: number + timeout: float waitUntil: LifecycleEvent? flags: snapshot: true @@ -2552,7 +2565,7 @@ Frame: streams: type: array? items: WritableStream - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -2575,7 +2588,7 @@ Frame: - Meta - Shift position: Point? - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -2587,7 +2600,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float returns: value: string? flags: @@ -2604,8 +2617,8 @@ Frame: selector: string strict: boolean? text: string - delay: number? - timeout: number + delay: float? + timeout: float flags: slowMo: true snapshot: true @@ -2618,7 +2631,7 @@ Frame: strict: boolean? force: boolean? position: Point? - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -2628,7 +2641,7 @@ Frame: waitForTimeout: title: Wait for timeout parameters: - waitTimeout: number + waitTimeout: float flags: snapshot: true @@ -2638,9 +2651,9 @@ Frame: expression: string isFunction: boolean? arg: SerializedArgument - timeout: number + timeout: float # When present, polls on interval. Otherwise, polls on raf. - pollingInterval: number? + pollingInterval: float? returns: handle: JSHandle flags: @@ -2651,7 +2664,7 @@ Frame: parameters: selector: string strict: boolean? - timeout: number + timeout: float state: type: enum? literals: @@ -2674,11 +2687,11 @@ Frame: expectedText: type: array? items: ExpectedTextValue - expectedNumber: number? + expectedNumber: float? expectedValue: SerializedArgument? useInnerText: boolean? isNot: boolean - timeout: number + timeout: float returns: matches: boolean received: SerializedValue? @@ -2846,7 +2859,7 @@ ElementHandle: parameters: force: boolean? position: Point? - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -2869,15 +2882,15 @@ ElementHandle: - Meta - Shift position: Point? - delay: number? + delay: float? button: type: enum? literals: - left - right - middle - clickCount: number? - timeout: number + clickCount: int? + timeout: float trial: boolean? flags: slowMo: true @@ -2906,14 +2919,14 @@ ElementHandle: - Meta - Shift position: Point? - delay: number? + delay: float? button: type: enum? literals: - left - right - middle - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -2934,7 +2947,7 @@ ElementHandle: parameters: value: string force: boolean? - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -2968,7 +2981,7 @@ ElementHandle: - Meta - Shift position: Point? - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -3047,8 +3060,8 @@ ElementHandle: title: Press "{key}" parameters: key: string - delay: number? - timeout: number + delay: float? + timeout: float noWaitAfter: boolean? flags: slowMo: true @@ -3079,13 +3092,13 @@ ElementHandle: screenshot: title: Screenshot parameters: - timeout: number + timeout: float type: type: enum? literals: - png - jpeg - quality: number? + quality: int? $mixin: CommonScreenshotOptions returns: binary: binary @@ -3095,7 +3108,7 @@ ElementHandle: scrollIntoViewIfNeeded: title: Scroll into view parameters: - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -3114,9 +3127,9 @@ ElementHandle: valueOrLabel: string? value: string? label: string? - index: number? + index: int? force: boolean? - timeout: number + timeout: float returns: values: type: array @@ -3130,7 +3143,7 @@ ElementHandle: title: Select text parameters: force: boolean? - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -3155,7 +3168,7 @@ ElementHandle: streams: type: array? items: WritableStream - timeout: number + timeout: float flags: slowMo: true snapshot: true @@ -3176,7 +3189,7 @@ ElementHandle: - Meta - Shift position: Point? - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -3194,8 +3207,8 @@ ElementHandle: title: Type parameters: text: string - delay: number? - timeout: number + delay: float? + timeout: float flags: slowMo: true snapshot: true @@ -3206,7 +3219,7 @@ ElementHandle: parameters: force: boolean? position: Point? - timeout: number + timeout: float trial: boolean? flags: slowMo: true @@ -3225,7 +3238,7 @@ ElementHandle: - enabled - disabled - editable - timeout: number + timeout: float flags: snapshot: true @@ -3234,7 +3247,7 @@ ElementHandle: parameters: selector: string strict: boolean? - timeout: number + timeout: float state: type: enum? literals: @@ -3311,7 +3324,7 @@ Route: internal: true parameters: # default is 200 - status: number? + status: int? headers: type: array? items: NameValue @@ -3349,14 +3362,14 @@ WebSocketRoute: closePage: internal: true parameters: - code: number? + code: int? reason: string? wasClean: boolean closeServer: internal: true parameters: - code: number? + code: int? reason: string? wasClean: boolean @@ -3374,13 +3387,13 @@ WebSocketRoute: closePage: parameters: - code: number? + code: int? reason: string? wasClean: boolean closeServer: parameters: - code: number? + code: int? reason: string? wasClean: boolean @@ -3388,14 +3401,14 @@ WebSocketRoute: ResourceTiming: type: object properties: - startTime: number - domainLookupStart: number - domainLookupEnd: number - connectStart: number - secureConnectionStart: number - connectEnd: number - requestStart: number - responseStart: number + startTime: float + domainLookupStart: float + domainLookupEnd: float + connectStart: float + secureConnectionStart: float + connectEnd: float + requestStart: float + responseStart: float Response: type: interface @@ -3403,7 +3416,7 @@ Response: initializer: request: Request url: string - status: number + status: int statusText: string headers: type: array @@ -3448,23 +3461,23 @@ SecurityDetails: issuer: string? protocol: string? subjectName: string? - validFrom: number? - validTo: number? + validFrom: float? + validTo: float? RequestSizes: type: object properties: - requestBodySize: number - requestHeadersSize: number - responseBodySize: number - responseHeadersSize: number + requestBodySize: int + requestHeadersSize: int + responseBodySize: int + responseHeadersSize: int RemoteAddr: type: object properties: ipAddress: string - port: number + port: int WebSocket: @@ -3480,12 +3493,12 @@ WebSocket: frameSent: parameters: - opcode: number + opcode: int data: string frameReceived: parameters: - opcode: number + opcode: int data: string socketError: @@ -3568,8 +3581,8 @@ Tracing: type: object? properties: file: string - line: number? - column: number? + line: int? + column: int? tracingGroupEnd: title: Group end @@ -3646,7 +3659,7 @@ Stream: read: internal: true parameters: - size: number? + size: int? returns: binary: binary @@ -3708,7 +3721,7 @@ Electron: env: type: array? items: NameValue - timeout: number + timeout: float acceptDownloads: type: enum? literals: @@ -3729,9 +3742,9 @@ Electron: geolocation: type: object? properties: - longitude: number - latitude: number - accuracy: number? + longitude: float + latitude: float + accuracy: float? httpCredentials: type: object? properties: @@ -3748,8 +3761,8 @@ Electron: size: type: object? properties: - width: number - height: number + width: int + height: int strictSelectors: boolean? timezoneId: string? tracesDir: string? @@ -3821,7 +3834,7 @@ Android: internal: true parameters: host: string? - port: number? + port: int? omitDriverInstall: boolean? returns: devices: @@ -3864,29 +3877,29 @@ AndroidDevice: type: enum? literals: - gone - timeout: number + timeout: float fill: title: Fill "{text}" parameters: androidSelector: AndroidSelector text: string - timeout: number + timeout: float tap: title: Tap parameters: androidSelector: AndroidSelector - duration: number? - timeout: number + duration: float? + timeout: float drag: title: Drag parameters: androidSelector: AndroidSelector dest: Point - speed: number? - timeout: number + speed: float? + timeout: float fling: title: Fling @@ -3899,30 +3912,30 @@ AndroidDevice: - down - left - right - speed: number? - timeout: number + speed: float? + timeout: float longTap: title: Long tap parameters: androidSelector: AndroidSelector - timeout: number + timeout: float pinchClose: title: Pinch close parameters: androidSelector: AndroidSelector - percent: number - speed: number? - timeout: number + percent: float + speed: float? + timeout: float pinchOpen: title: Pinch open parameters: androidSelector: AndroidSelector - percent: number - speed: number? - timeout: number + percent: float + speed: float? + timeout: float scroll: title: Scroll @@ -3935,9 +3948,9 @@ AndroidDevice: - down - left - right - percent: number - speed: number? - timeout: number + percent: float + speed: float? + timeout: float swipe: title: Swipe @@ -3950,9 +3963,9 @@ AndroidDevice: - down - left - right - percent: number - speed: number? - timeout: number + percent: float + speed: float? + timeout: float info: internal: true @@ -3987,14 +4000,14 @@ AndroidDevice: segments: type: array items: Point - steps: number + steps: int inputDrag: title: Drag parameters: from: Point to: Point - steps: number + steps: int launchBrowser: title: Launch browser @@ -4042,7 +4055,7 @@ AndroidDevice: parameters: file: binary path: string - mode: number? + mode: int? connectToWebView: internal: true @@ -4069,7 +4082,7 @@ AndroidDevice: AndroidWebView: type: object properties: - pid: number + pid: int pkg: string socketName: string @@ -4081,7 +4094,7 @@ AndroidSelector: checked: boolean? clazz: string? clickable: boolean? - depth: number? + depth: int? desc: string? enabled: boolean? focusable: boolean? @@ -4094,7 +4107,7 @@ AndroidSelector: type: object? properties: androidSelector: AndroidSelector - maxDepth: number? + maxDepth: int? longClickable: boolean? pkg: string? res: string? diff --git a/tests/library/browsercontext-storage-state.spec.ts b/tests/library/browsercontext-storage-state.spec.ts index 4f4fca6cee201..8c11a086958f7 100644 --- a/tests/library/browsercontext-storage-state.spec.ts +++ b/tests/library/browsercontext-storage-state.spec.ts @@ -500,3 +500,74 @@ it('should support empty indexedDB', { annotation: { type: 'issue', description: const context = await contextFactory({ storageState }); expect(await context.storageState({ indexedDB: true })).toEqual(storageState); }); + +it('should cleanup existing and set new storage state', async ({ contextFactory }, testInfo) => { + const context = await contextFactory(); + const page = await context.newPage(); + await page.route('**/*', route => { + route.fulfill({ body: '' }).catch(() => {}); + }); + await page.goto('https://www.example.com'); + await page.evaluate(async () => { + localStorage['name1'] = 'value1'; + document.cookie = 'username=John Doe'; + return document.cookie; + }); + + const state = await context.storageState(); + expect(state).toEqual({ + cookies: [ + expect.objectContaining({ + domain: 'www.example.com', + name: 'username', + value: 'John Doe', + }), + ], + origins: [ + { + origin: 'https://www.example.com', + localStorage: [{ + name: 'name1', + value: 'value1', + }], + }, + ], + }); + + await context.setStorageState({ + cookies: [ + { + domain: 'www.example.com', + name: 'foo', + value: 'bar', + path: '/', + expires: -1, + httpOnly: false, + secure: false, + sameSite: 'Lax', + }, + ], + origins: [ + { + origin: 'https://www.another.com', + localStorage: [{ + name: 'name2', + value: 'value2', + }], + }, + ], + }); + + expect(context.pages()).toHaveLength(1); + await expect(page).toHaveURL('https://www.example.com/'); + + await page.goto('https://www.another.com'); + expect(await page.evaluate(() => document.cookie)).toBe(''); + expect(await page.evaluate(() => window.localStorage['name1'])).toBe(undefined); + expect(await page.evaluate(() => window.localStorage['name2'])).toBe('value2'); + + await page.goto('https://www.example.com'); + expect(await page.evaluate(() => document.cookie)).toBe('foo=bar'); + expect(await page.evaluate(() => window.localStorage['name1'])).toBe(undefined); + expect(await page.evaluate(() => window.localStorage['name2'])).toBe(undefined); +}); diff --git a/tests/library/chromium/tracing.spec.ts b/tests/library/chromium/tracing.spec.ts index f61072fa32a08..f933ebbe2ce06 100644 --- a/tests/library/chromium/tracing.spec.ts +++ b/tests/library/chromium/tracing.spec.ts @@ -100,6 +100,7 @@ it('should support a buffer without a path', async ({ browser, server }) => { const page = await browser.newPage(); await browser.startTracing(page, { screenshots: true }); await page.goto(server.PREFIX + '/grid.html'); + await rafraf(page); const trace = await browser.stopTracing(); expect(trace.toString()).toContain('screenshot'); await page.close(); diff --git a/tests/library/geolocation.spec.ts b/tests/library/geolocation.spec.ts index 30ea11781d99c..525e5cd3092ce 100644 --- a/tests/library/geolocation.spec.ts +++ b/tests/library/geolocation.spec.ts @@ -85,7 +85,7 @@ it('should throw with missing latitude', async ({ contextFactory }) => { } catch (e) { error = e; } - expect(error.message).toContain('geolocation.latitude: expected number, got undefined'); + expect(error.message).toContain('geolocation.latitude: expected float, got undefined'); }); it('should not modify passed default options object', async ({ browser }) => { @@ -106,7 +106,7 @@ it('should throw with missing longitude in default options', async ({ browser }) } catch (e) { error = e; } - expect(error.message).toContain('geolocation.longitude: expected number, got undefined'); + expect(error.message).toContain('geolocation.longitude: expected float, got undefined'); }); it('should use context options', async ({ browser, server }) => { diff --git a/tests/page/page-select-option.spec.ts b/tests/page/page-select-option.spec.ts index 0355b4592bbd8..8b6afbd640d61 100644 --- a/tests/page/page-select-option.spec.ts +++ b/tests/page/page-select-option.spec.ts @@ -222,7 +222,7 @@ it('should throw if passed wrong types', async ({ page, server }) => { } catch (e) { error = e; } - expect(error.message).toContain('options[0].index: expected number, got string'); + expect(error.message).toContain('options[0].index: expected integer, got string'); }); // @see https://github.com/GoogleChrome/puppeteer/issues/3327 it('should work when re-defining top-level Event class', async ({ page, server }) => { diff --git a/utils/generate_channels.js b/utils/generate_channels.js index febc92fb40c3c..77ae55d234909 100755 --- a/utils/generate_channels.js +++ b/utils/generate_channels.js @@ -41,8 +41,12 @@ function inlineType(type, indent, wrapEnums = false) { return { ts: 'Binary', scheme: 'tBinary', optional }; if (type === 'json') return { ts: 'any', scheme: 'tAny', optional }; - if (['string', 'boolean', 'number', 'undefined'].includes(type)) + if (['string', 'boolean', 'undefined'].includes(type)) return { ts: type, scheme: `t${titleCase(type)}`, optional }; + if (type === 'number') + throw new Error('Use "int" or "float" instead of "number" in protocol.yml'); + if (type === 'int' || type === 'float') + return { ts: 'number', scheme: `t${titleCase(type)}`, optional }; if (channels.has(type)) { let derived = derivedClasses.get(type) || []; derived = [...derived, type]; @@ -153,7 +157,7 @@ const validator_ts = [ // This file is generated by ${path.basename(__filename)}, do not edit manually. -import { scheme, tOptional, tObject, tBoolean, tNumber, tString, tAny, tEnum, tArray, tBinary, tChannel, tType } from './validatorPrimitives'; +import { scheme, tOptional, tObject, tBoolean, tInt, tFloat, tString, tAny, tEnum, tArray, tBinary, tChannel, tType } from './validatorPrimitives'; export type { Validator, ValidatorContext } from './validatorPrimitives'; export { ValidationError, findValidator, maybeFindValidator, createMetadataValidator } from './validatorPrimitives'; `];