diff --git a/.github/actions/enable-microphone-access/action.yml b/.github/actions/enable-microphone-access/action.yml index b94f480647..76ca24eebf 100644 --- a/.github/actions/enable-microphone-access/action.yml +++ b/.github/actions/enable-microphone-access/action.yml @@ -14,12 +14,12 @@ runs: fi echo "Allowing microphone access to all apps" version=$(sw_vers -productVersion | cut -d. -f1) - if [[ "$version" == "14" ]]; then + if [[ "$version" == "14" || "$version" == "15" ]]; then sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159,NULL,NULL,'UNUSED',1687786159);" elif [[ "$version" == "12" || "$version" == "13" ]]; then sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR REPLACE INTO access VALUES('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159);" else - echo "macOS version is unsupported. Version is $version, exiting" - exit 1 + echo "Skipping unsupported macOS version $version" + exit 0 fi echo "Successfully allowed microphone access" diff --git a/.github/workflows/create_test_report.yml b/.github/workflows/create_test_report.yml index 9d382f1ee6..b20f57573b 100644 --- a/.github/workflows/create_test_report.yml +++ b/.github/workflows/create_test_report.yml @@ -22,6 +22,7 @@ jobs: env: DEBUG: pw:install PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm run build - name: Download blob report artifact @@ -120,21 +121,3 @@ jobs: ]), }); core.info('Posted comment: ' + response.html_url); - - const check = await github.rest.checks.create({ - ...context.repo, - name: 'Merge report (${{ github.event.workflow_run.name }})', - head_sha: '${{ github.event.workflow_run.head_sha }}', - status: 'completed', - conclusion: 'success', - details_url: reportUrl, - output: { - title: 'Test results for "${{ github.event.workflow_run.name }}"', - summary: [ - reportMd, - '', - '---', - `Full [HTML report](${reportUrl}). Merge [workflow run](${mergeWorkflowUrl}).` - ].join('\n'), - } - }); diff --git a/.github/workflows/infra.yml b/.github/workflows/infra.yml index f33c8535f0..905597c8bd 100644 --- a/.github/workflows/infra.yml +++ b/.github/workflows/infra.yml @@ -38,7 +38,7 @@ jobs: run: npm audit --omit dev lint-snippets: name: "Lint snippets" - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -50,6 +50,12 @@ jobs: - uses: actions/setup-dotnet@v4 with: dotnet-version: 8.0.x + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: '21' - run: npm ci - run: pip install -r utils/doclint/linting-code-snippets/python/requirements.txt + - run: mvn package + working-directory: utils/doclint/linting-code-snippets/java - run: node utils/doclint/linting-code-snippets/cli.js diff --git a/.github/workflows/tests_bidi.yml b/.github/workflows/tests_bidi.yml index 8224d24883..46b16aac7b 100644 --- a/.github/workflows/tests_bidi.yml +++ b/.github/workflows/tests_bidi.yml @@ -13,7 +13,6 @@ on: env: FORCE_COLOR: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 jobs: test_bidi: @@ -26,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - channel: [bidi-chromium, bidi-firefox-beta] + channel: [bidi-chromium, bidi-firefox-nightly] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -38,8 +37,8 @@ jobs: - run: npm run build - run: npx playwright install --with-deps chromium if: matrix.channel == 'bidi-chromium' - - run: npx -y @puppeteer/browsers install firefox@beta - if: matrix.channel == 'bidi-firefox-beta' + - run: npx -y @puppeteer/browsers install firefox@nightly + if: matrix.channel == 'bidi-firefox-nightly' - name: Run tests run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run biditest -- --project=${{ matrix.channel }}* env: diff --git a/.github/workflows/tests_others.yml b/.github/workflows/tests_others.yml index 434c5aa9d3..66728e720f 100644 --- a/.github/workflows/tests_others.yml +++ b/.github/workflows/tests_others.yml @@ -24,9 +24,7 @@ jobs: strategy: fail-fast: false matrix: - # Stick with macos-latest-large for now which is Intel-based until - # https://github.com/microsoft/playwright/issues/30705 is fixed. - os: [ubuntu-latest, macos-latest-large, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/tests_secondary.yml b/.github/workflows/tests_secondary.yml index 8d4bbd1c78..8b8555eb86 100644 --- a/.github/workflows/tests_secondary.yml +++ b/.github/workflows/tests_secondary.yml @@ -50,10 +50,15 @@ jobs: strategy: fail-fast: false matrix: - # Intel: macos-13, macos-14-large - # Arm64: macos-13-xlarge, macos-14 - os: [macos-13, macos-13-xlarge, macos-14-large, macos-14] + # Intel: *-large + # Arm64: *-xlarge + os: [macos-13-large, macos-13-xlarge, macos-14-large, macos-14-xlarge] browser: [chromium, firefox, webkit] + include: + - os: macos-15-large + browser: webkit + - os: macos-15-xlarge + browser: webkit runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -118,7 +123,13 @@ jobs: fail-fast: false matrix: browser: [chromium, firefox, webkit] - os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, macos-14, windows-latest] + os: [ubuntu-24.04, macos-14-xlarge, windows-latest] + include: + # We have different binaries per Ubuntu version for WebKit. + - browser: webkit + os: ubuntu-20.04 + - browser: webkit + os: ubuntu-22.04 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -179,356 +190,71 @@ jobs: PWTEST_TRACE: 1 PWTEST_CHANNEL: ${{ matrix.channel }} - chrome_stable_linux: - name: "Chrome Stable (Linux)" + test_chromium_channels: + name: Test ${{ matrix.channel }} on ${{ matrix.runs-on }} environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: ubuntu-20.04 + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + channel: [chrome, chrome-beta, msedge, msedge-beta, msedge-dev] + runs-on: [ubuntu-20.04, macos-latest, windows-latest] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/run-test with: - browsers-to-install: chrome + browsers-to-install: ${{ matrix.channel }} command: npm run ctest - bot-name: "chrome-stable-linux" + bot-name: ${{ matrix.channel }}-${{ matrix.runs-on }} flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} env: - PWTEST_CHANNEL: chrome - - chrome_stable_win: - name: "Chrome Stable (Win)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: chrome - command: npm run ctest - bot-name: "chrome-stable-windows" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: chrome - - chrome_stable_mac: - name: "Chrome Stable (Mac)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: chrome - command: npm run ctest - bot-name: "chrome-stable-mac" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: chrome + PWTEST_CHANNEL: ${{ matrix.channel }} chromium_tot: - name: Chromium tip-of-tree ${{ matrix.os }} + name: Chromium tip-of-tree ${{ matrix.os }}${{ matrix.headed }} environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-20.04, macos-13, windows-latest] + headed: ['--headed', ''] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/run-test with: browsers-to-install: chromium-tip-of-tree - command: npm run ctest - bot-name: "tip-of-tree-${{ matrix.os }}" + command: npm run ctest -- ${{ matrix.headed }} + bot-name: "chromium-tip-of-tree-${{ matrix.os }}${{ matrix.headed }}" flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} env: PWTEST_CHANNEL: chromium-tip-of-tree - chromium_tot_headed: - name: Chromium tip-of-tree headed ${{ matrix.os }} + firefox_beta: + name: Firefox Beta ${{ matrix.os }} environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: chromium-tip-of-tree - command: npm run ctest -- --headed - bot-name: "tip-of-tree-headed-${{ matrix.os }}" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: chromium-tip-of-tree - - firefox_beta_linux: - name: "Firefox Beta (Linux)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: ubuntu-20.04 + os: [ubuntu-20.04, windows-latest, macos-latest] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/run-test with: browsers-to-install: firefox-beta chromium command: npm run ftest - bot-name: "firefox-beta-linux" + bot-name: "firefox-beta-${{ matrix.os }}" flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} env: PWTEST_CHANNEL: firefox-beta - firefox_beta_win: - name: "Firefox Beta (Win)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: firefox-beta chromium - command: npm run ftest -- --workers=1 - bot-name: "firefox-beta-windows" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: firefox-beta - - firefox_beta_mac: - name: "Firefox Beta (Mac)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - # Stick with macos-latest-large for now which is Intel-based until - # https://github.com/microsoft/playwright/issues/30705 is fixed. - runs-on: macos-latest-large - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: firefox-beta chromium - command: npm run ftest - bot-name: "firefox-beta-mac" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: firefox-beta - - edge_stable_mac: - name: "Edge Stable (Mac)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge - command: npm run ctest - bot-name: "edge-stable-mac" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge - - edge_stable_win: - name: "Edge Stable (Win)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge - command: npm run ctest - bot-name: "edge-stable-windows" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge - - edge_stable_linux: - name: "Edge Stable (Linux)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge - command: npm run ctest - bot-name: "edge-stable-linux" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge - - edge_beta_mac: - name: "Edge Beta (Mac)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge-beta - command: npm run ctest - bot-name: "edge-beta-mac" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge-beta - - edge_beta_win: - name: "Edge Beta (Win)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge-beta - command: npm run ctest - bot-name: "edge-beta-windows" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge-beta - - edge_beta_linux: - name: "Edge Beta (Linux)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge-beta - command: npm run ctest - bot-name: "edge-beta-linux" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge-beta - - edge_dev_mac: - name: "Edge Dev (Mac)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge-dev - command: npm run ctest - bot-name: "edge-dev-mac" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge-dev - - edge_dev_win: - name: "Edge Dev (Win)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge-dev - command: npm run ctest - bot-name: "edge-dev-windows" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge-dev - - edge_dev_linux: - name: "Edge Dev (Linux)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: msedge-dev - command: npm run ctest - bot-name: "edge-dev-linux" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: msedge-dev - - chrome_beta_linux: - name: "Chrome Beta (Linux)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: chrome-beta - command: npm run ctest - bot-name: "chrome-beta-linux" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: chrome-beta - - chrome_beta_win: - name: "Chrome Beta (Win)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: chrome-beta - command: npm run ctest - bot-name: "chrome-beta-windows" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: chrome-beta - - chrome_beta_mac: - name: "Chrome Beta (Mac)" - environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/run-test - with: - browsers-to-install: chrome-beta - command: npm run ctest - bot-name: "chrome-beta-mac" - flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} - flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} - flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} - env: - PWTEST_CHANNEL: chrome-beta - build-playwright-driver: name: "build-playwright-driver" runs-on: ubuntu-24.04 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30e89e6bee..b25a131d44 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,32 +29,32 @@ npm i -g npm@latest 1. Clone this repository -```bash -git clone https://github.com/microsoft/playwright -cd playwright -``` + ```bash + git clone https://github.com/microsoft/playwright + cd playwright + ``` 2. Install dependencies -```bash -npm ci -``` + ```bash + npm ci + ``` 3. Build Playwright -```bash -npm run build -``` + ```bash + npm run build + ``` 4. Run tests -This will run a test on line `23` in `page-fill.spec.ts`: + This will run a test on line `23` in `page-fill.spec.ts`: -```bash -npm run ctest -- page-fill:23 -``` + ```bash + npm run ctest -- page-fill:23 + ``` -See [here](#running--writing-tests) for more information about running and writing tests. + See [here](#running--writing-tests) for more information about running and writing tests. ### Code reviews @@ -155,63 +155,65 @@ These are integration tests, making sure public API methods and events work as e - To run all tests: -```bash -npx playwright install -npm run test -``` + ```bash + npx playwright install + npm run test + ``` -Be sure to run `npm run build` or let `npm run watch` run before you re-run the -tests after making your changes to check them. + Be sure to run `npm run build` or let `npm run watch` run before you re-run the + tests after making your changes to check them. - To run tests in Chromium -```bash -npm run ctest # also `ftest` for firefox and `wtest` for WebKit -npm run ctest -- page-fill:23 # runs line 23 of page-fill.spec.ts -``` -To run tests in WebKit / Firefox, use `wtest` or `ftest`. + ```bash + npm run ctest # also `ftest` for firefox and `wtest` for WebKit + npm run ctest -- page-fill:23 # runs line 23 of page-fill.spec.ts + ``` + +- To run tests in WebKit / Firefox, use `wtest` or `ftest`. - To run the Playwright test runner tests -```bash -npm run ttest -npm run ttest -- --grep "specific test" -``` + + ```bash + npm run ttest + npm run ttest -- --grep "specific test" + ``` - To run a specific test, substitute `it` with `it.only`, or use the `--grep 'My test'` CLI parameter: -```js -... -// Using "it.only" to run a specific test -it.only('should work', async ({server, page}) => { - const response = await page.goto(server.EMPTY_PAGE); - expect(response.ok).toBe(true); -}); -// or -playwright test --config=xxx --grep 'should work' -``` + ```js + ... + // Using "it.only" to run a specific test + it.only('should work', async ({server, page}) => { + const response = await page.goto(server.EMPTY_PAGE); + expect(response.ok).toBe(true); + }); + // or + playwright test --config=xxx --grep 'should work' + ``` - To disable a specific test, substitute `it` with `it.skip`: -```js -... -// Using "it.skip" to skip a specific test -it.skip('should work', async ({server, page}) => { - const response = await page.goto(server.EMPTY_PAGE); - expect(response.ok).toBe(true); -}); -``` + ```js + ... + // Using "it.skip" to skip a specific test + it.skip('should work', async ({server, page}) => { + const response = await page.goto(server.EMPTY_PAGE); + expect(response.ok).toBe(true); + }); + ``` - To run tests in non-headless (headed) mode: -```bash -npm run ctest -- --headed -``` + ```bash + npm run ctest -- --headed + ``` - To run tests with custom browser executable, specify `CRPATH`, `WKPATH` or `FFPATH` env variable that points to browser executable: -```bash -CRPATH= npm run ctest -``` + ```bash + CRPATH= npm run ctest + ``` - When should a test be marked with `skip` or `fixme`? diff --git a/README.md b/README.md index d0d8e53dd0..6df15c2024 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🎭 Playwright -[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-129.0.6668.42-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-130.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-18.0-blue.svg?logo=safari)](https://webkit.org/) [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord) +[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-130.0.6723.31-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-131.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-18.0-blue.svg?logo=safari)](https://webkit.org/) [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord) ## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright) @@ -8,9 +8,9 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 129.0.6668.42 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 130.0.6723.31 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 18.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| Firefox 130.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Firefox 131.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details. diff --git a/browser_patches/firefox/UPSTREAM_CONFIG.sh b/browser_patches/firefox/UPSTREAM_CONFIG.sh index ff33363424..da588ee687 100644 --- a/browser_patches/firefox/UPSTREAM_CONFIG.sh +++ b/browser_patches/firefox/UPSTREAM_CONFIG.sh @@ -1,3 +1,3 @@ REMOTE_URL="https://github.com/mozilla/gecko-dev" BASE_BRANCH="release" -BASE_REVISION="cf0397e3ba298868fdca53f894da5b0d239dc09e" +BASE_REVISION="47bcb6d7d2013f9a3d864678675100e0b3d73c5e" diff --git a/browser_patches/firefox/juggler/NetworkObserver.js b/browser_patches/firefox/juggler/NetworkObserver.js index aa6f866277..2ee5996068 100644 --- a/browser_patches/firefox/juggler/NetworkObserver.js +++ b/browser_patches/firefox/juggler/NetworkObserver.js @@ -145,10 +145,13 @@ class NetworkRequest { } this._expectingInterception = false; this._expectingResumedRequest = undefined; // { method, headers, postData } + this._overriddenHeadersForRedirect = redirectedFrom?._overriddenHeadersForRedirect; this._sentOnResponse = false; this._fulfilled = false; - if (this._pageNetwork) + if (this._overriddenHeadersForRedirect) + overrideRequestHeaders(httpChannel, this._overriddenHeadersForRedirect); + else if (this._pageNetwork) appendExtraHTTPHeaders(httpChannel, this._pageNetwork.combinedExtraHTTPHeaders()); this._responseBodyChunks = []; @@ -230,20 +233,13 @@ class NetworkRequest { if (!this._expectingResumedRequest) return; const { method, headers, postData } = this._expectingResumedRequest; + this._overriddenHeadersForRedirect = headers; this._expectingResumedRequest = undefined; - if (headers) { - for (const header of requestHeaders(this.httpChannel)) { - // We cannot remove the "host" header. - if (header.name.toLowerCase() === 'host') - continue; - this.httpChannel.setRequestHeader(header.name, '', false /* merge */); - } - for (const header of headers) - this.httpChannel.setRequestHeader(header.name, header.value, false /* merge */); - } else if (this._pageNetwork) { + if (headers) + overrideRequestHeaders(this.httpChannel, headers); + else if (this._pageNetwork) appendExtraHTTPHeaders(this.httpChannel, this._pageNetwork.combinedExtraHTTPHeaders()); - } if (method) this.httpChannel.requestMethod = method; if (postData !== undefined) @@ -773,6 +769,20 @@ function requestHeaders(httpChannel) { return headers; } +function clearRequestHeaders(httpChannel) { + for (const header of requestHeaders(httpChannel)) { + // We cannot remove the "host" header. + if (header.name.toLowerCase() === 'host') + continue; + httpChannel.setRequestHeader(header.name, '', false /* merge */); + } +} + +function overrideRequestHeaders(httpChannel, headers) { + clearRequestHeaders(httpChannel); + appendExtraHTTPHeaders(httpChannel, headers); +} + function causeTypeToString(causeType) { for (let key in Ci.nsIContentPolicy) { if (Ci.nsIContentPolicy[key] === causeType) diff --git a/browser_patches/firefox/juggler/content/FrameTree.js b/browser_patches/firefox/juggler/content/FrameTree.js index 2d59b3c43a..721f392b9b 100644 --- a/browser_patches/firefox/juggler/content/FrameTree.js +++ b/browser_patches/firefox/juggler/content/FrameTree.js @@ -46,8 +46,6 @@ class FrameTree { Ci.nsISupportsWeakReference, ]); - this._addedScrollbarsStylesheetSymbol = Symbol('_addedScrollbarsStylesheetSymbol'); - this._wdm = Cc["@mozilla.org/dom/workers/workerdebuggermanager;1"].createInstance(Ci.nsIWorkerDebuggerManager); this._wdmListener = { QueryInterface: ChromeUtils.generateQI([Ci.nsIWorkerDebuggerManagerListener]), @@ -130,24 +128,12 @@ class FrameTree { } _onDOMWindowCreated(window) { - if (!window[this._addedScrollbarsStylesheetSymbol] && this.scrollbarsHidden) { - const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService); - const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); - const uri = ioService.newURI('chrome://juggler/content/content/hidden-scrollbars.css', null, null); - const sheet = styleSheetService.preloadSheet(uri, styleSheetService.AGENT_SHEET); - window.windowUtils.addSheet(sheet, styleSheetService.AGENT_SHEET); - window[this._addedScrollbarsStylesheetSymbol] = true; - } const frame = this.frameForDocShell(window.docShell); if (!frame) return; frame._onGlobalObjectCleared(); } - setScrollbarsHidden(hidden) { - this.scrollbarsHidden = hidden; - } - setJavaScriptDisabled(javaScriptDisabled) { this._javaScriptDisabled = javaScriptDisabled; for (const frame of this.frames()) diff --git a/browser_patches/firefox/juggler/content/PageAgent.js b/browser_patches/firefox/juggler/content/PageAgent.js index 70dcf04921..255b84f726 100644 --- a/browser_patches/firefox/juggler/content/PageAgent.js +++ b/browser_patches/firefox/juggler/content/PageAgent.js @@ -120,7 +120,8 @@ class PageAgent { // After the dragStart event is dispatched and handled by Web, // it might or might not create a new drag session, depending on its preventing default. setTimeout(() => { - this._browserPage.emit('pageInputEvent', { type: 'juggler-drag-finalized', dragSessionStarted: !!dragService.getCurrentSession() }); + const session = this._getCurrentDragSession(); + this._browserPage.emit('pageInputEvent', { type: 'juggler-drag-finalized', dragSessionStarted: !!session }); }, 0); } }), @@ -526,8 +527,14 @@ class PageAgent { }); } + _getCurrentDragSession() { + const frame = this._frameTree.mainFrame(); + const domWindow = frame?.domWindow(); + return domWindow ? dragService.getCurrentSession(domWindow) : undefined; + } + async _dispatchDragEvent({type, x, y, modifiers}) { - const session = dragService.getCurrentSession(); + const session = this._getCurrentDragSession(); const dropEffect = session.dataTransfer.dropEffect; if ((type === 'drop' && dropEffect !== 'none') || type === 'dragover') { @@ -551,9 +558,8 @@ class PageAgent { return; } if (type === 'dragend') { - const session = dragService.getCurrentSession(); - if (session) - dragService.endDragSession(true); + const session = this._getCurrentDragSession(); + session?.endDragSession(true); return; } } diff --git a/browser_patches/firefox/juggler/content/main.js b/browser_patches/firefox/juggler/content/main.js index 15986bbed9..7eaa704059 100644 --- a/browser_patches/firefox/juggler/content/main.js +++ b/browser_patches/firefox/juggler/content/main.js @@ -45,10 +45,6 @@ function initialize(browsingContext, docShell) { docShell.languageOverride = locale; }, - scrollbarsHidden: (hidden) => { - data.frameTree.setScrollbarsHidden(hidden); - }, - javaScriptDisabled: (javaScriptDisabled) => { data.frameTree.setJavaScriptDisabled(javaScriptDisabled); }, diff --git a/browser_patches/firefox/juggler/protocol/BrowserHandler.js b/browser_patches/firefox/juggler/protocol/BrowserHandler.js index 7de276d017..6a4688e541 100644 --- a/browser_patches/firefox/juggler/protocol/BrowserHandler.js +++ b/browser_patches/firefox/juggler/protocol/BrowserHandler.js @@ -255,10 +255,6 @@ class BrowserHandler { await this._targetRegistry.browserContextForId(browserContextId).setDefaultViewport(nullToUndefined(viewport)); } - async ['Browser.setScrollbarsHidden']({browserContextId, hidden}) { - await this._targetRegistry.browserContextForId(browserContextId).applySetting('scrollbarsHidden', nullToUndefined(hidden)); - } - async ['Browser.setInitScripts']({browserContextId, scripts}) { await this._targetRegistry.browserContextForId(browserContextId).setInitScripts(scripts); } diff --git a/browser_patches/firefox/juggler/protocol/PageHandler.js b/browser_patches/firefox/juggler/protocol/PageHandler.js index bab151b392..8fa9a06361 100644 --- a/browser_patches/firefox/juggler/protocol/PageHandler.js +++ b/browser_patches/firefox/juggler/protocol/PageHandler.js @@ -256,13 +256,6 @@ class PageHandler { return await this._contentPage.send('disposeObject', options); } - async ['Heap.collectGarbage']() { - Services.obs.notifyObservers(null, "child-gc-request"); - Cu.forceGC(); - Services.obs.notifyObservers(null, "child-cc-request"); - Cu.forceCC(); - } - async ['Network.getResponseBody']({requestId}) { return this._pageNetwork.getResponseBody(requestId); } diff --git a/browser_patches/firefox/juggler/protocol/Protocol.js b/browser_patches/firefox/juggler/protocol/Protocol.js index 2b7ad56d6a..1d6e197c97 100644 --- a/browser_patches/firefox/juggler/protocol/Protocol.js +++ b/browser_patches/firefox/juggler/protocol/Protocol.js @@ -394,12 +394,6 @@ const Browser = { viewport: t.Nullable(pageTypes.Viewport), } }, - 'setScrollbarsHidden': { - params: { - browserContextId: t.Optional(t.String), - hidden: t.Boolean, - } - }, 'setInitScripts': { params: { browserContextId: t.Optional(t.String), @@ -487,17 +481,6 @@ const Browser = { }, }; -const Heap = { - targets: ['page'], - types: {}, - events: {}, - methods: { - 'collectGarbage': { - params: {}, - }, - }, -}; - const Network = { targets: ['page'], types: networkTypes, @@ -1013,7 +996,7 @@ const Accessibility = { } this.protocol = { - domains: {Browser, Heap, Page, Runtime, Network, Accessibility}, + domains: {Browser, Page, Runtime, Network, Accessibility}, }; this.checkScheme = checkScheme; this.EXPORTED_SYMBOLS = ['protocol', 'checkScheme']; diff --git a/browser_patches/firefox/patches/bootstrap.diff b/browser_patches/firefox/patches/bootstrap.diff index 4344455a66..8204e591c8 100644 --- a/browser_patches/firefox/patches/bootstrap.diff +++ b/browser_patches/firefox/patches/bootstrap.diff @@ -89,10 +89,10 @@ index b40e0fceb567c0d217adf284e13f434e49cc8467..2c4e6d5fbf8da40954ad6a5b15e41249 DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT; diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn -index f6d425f36a965f03ac82dbe3ab6cde06f12751ac..d60999ab2658b1e1e5f07a8aee530451c44f2957 100644 +index 213a99ed433d5219c2b9a64baad82d14cdbcd432..ee4f6484cdfe80899c28a1d9607494e520bfc93d 100644 --- a/browser/installer/allowed-dupes.mn +++ b/browser/installer/allowed-dupes.mn -@@ -73,6 +73,12 @@ browser/features/webcompat@mozilla.org/shims/empty-shim.txt +@@ -67,6 +67,12 @@ browser/features/webcompat@mozilla.org/shims/empty-shim.txt removed-files #endif @@ -102,11 +102,11 @@ index f6d425f36a965f03ac82dbe3ab6cde06f12751ac..d60999ab2658b1e1e5f07a8aee530451 +chrome/juggler/content/server/stream-utils.js +chrome/marionette/content/stream-utils.js + - # Bug 1496075 - Switch searchplugins to Web Extensions - browser/chrome/browser/search-extensions/amazon/favicon.ico - browser/chrome/browser/search-extensions/amazondotcn/favicon.ico + # Bug 1606928 - There's no reliable way to connect Top Sites favicons with the favicons in the Search Service + browser/chrome/browser/content/activity-stream/data/content/tippytop/favicons/allegro-pl.ico + browser/defaults/settings/main/search-config-icons/96327a73-c433-5eb4-a16d-b090cadfb80b diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in -index 3bf9d511555510414f39db7f99a6b5a2a743f178..bb0f71dd602193536c23f7b865ec5dce3ee02242 100644 +index da760e143740a166df14d055cf3ec7b095b93d10..a7579b3eae69f3b706094693d9b0edaec049e83b 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -189,6 +189,9 @@ @@ -297,7 +297,7 @@ index 61135ab0d7894c500c3c5d80d107e283c01b6830..cc8eb043f1f78214843ec7b335dd9932 bool CanSet(FieldIndex, bool, ContentParent*) { diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp -index b59a70321b6c5801e4a4f916ee303c999747570b..1eded29480eb4b401327da9ed33a63a18e3297b9 100644 +index 18b2bde3da2b1e17938fddda486b1bc4ddcf0e79..793a3d002b10298f7a19a2eae4d377f6f022fd36 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -324,6 +324,8 @@ void CanonicalBrowsingContext::ReplacedBy( @@ -323,7 +323,7 @@ index b59a70321b6c5801e4a4f916ee303c999747570b..1eded29480eb4b401327da9ed33a63a1 } diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp -index 354b2c0d66976fd7fd431902bfc7816131602496..7948aa03c8fd865bf7953faaeea2bda84ade04c8 100644 +index 60cbd5d5b8d202fc30d5ac931ac66030bade65e7..f552a695880c5838c89ce918f61d051577cc5598 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -15,6 +15,12 @@ @@ -600,7 +600,7 @@ index 354b2c0d66976fd7fd431902bfc7816131602496..7948aa03c8fd865bf7953faaeea2bda8 NS_IMETHODIMP nsDocShell::GetIsNavigating(bool* aOut) { *aOut = mIsNavigating; -@@ -4734,7 +4959,7 @@ nsDocShell::GetVisibility(bool* aVisibility) { +@@ -4739,7 +4964,7 @@ nsDocShell::GetVisibility(bool* aVisibility) { } void nsDocShell::ActivenessMaybeChanged() { @@ -609,7 +609,7 @@ index 354b2c0d66976fd7fd431902bfc7816131602496..7948aa03c8fd865bf7953faaeea2bda8 if (RefPtr presShell = GetPresShell()) { presShell->ActivenessMaybeChanged(); } -@@ -6672,6 +6897,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType, +@@ -6681,6 +6906,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType, return false; // no entry to save into } @@ -620,7 +620,7 @@ index 354b2c0d66976fd7fd431902bfc7816131602496..7948aa03c8fd865bf7953faaeea2bda8 MOZ_ASSERT(!mozilla::SessionHistoryInParent(), "mOSHE cannot be non-null with SHIP"); nsCOMPtr viewer = mOSHE->GetDocumentViewer(); -@@ -8401,6 +8630,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) { +@@ -8413,6 +8642,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) { true, // aForceNoOpener getter_AddRefs(newBC)); MOZ_ASSERT(!newBC); @@ -633,7 +633,7 @@ index 354b2c0d66976fd7fd431902bfc7816131602496..7948aa03c8fd865bf7953faaeea2bda8 return rv; } -@@ -9533,6 +9768,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, +@@ -9549,6 +9784,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr); nsCOMPtr req; @@ -650,7 +650,7 @@ index 354b2c0d66976fd7fd431902bfc7816131602496..7948aa03c8fd865bf7953faaeea2bda8 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req)); if (NS_SUCCEEDED(rv)) { -@@ -12710,6 +12955,9 @@ class OnLinkClickEvent : public Runnable { +@@ -12747,6 +12992,9 @@ class OnLinkClickEvent : public Runnable { mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied, mTriggeringPrincipal); } @@ -660,7 +660,7 @@ index 354b2c0d66976fd7fd431902bfc7816131602496..7948aa03c8fd865bf7953faaeea2bda8 return NS_OK; } -@@ -12792,6 +13040,8 @@ nsresult nsDocShell::OnLinkClick( +@@ -12836,6 +13084,8 @@ nsresult nsDocShell::OnLinkClick( nsCOMPtr ev = new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied, aIsTrusted, aTriggeringPrincipal); @@ -781,10 +781,10 @@ index fdc04f16c6f547077ad8c872f9357d85d4513c50..199f8fdb0670265c715f99f5cac1a2b2 * This attempts to save any applicable layout history state (like * scroll position) in the nsISHEntry. This is normally done diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp -index c6cb09e1955d371cd19f563b30b486bcc2318304..d836946872b8e32360a925be5084472191e04f05 100644 +index 235e2fcfccda18b4e923d1c1b02b5e1d9b02b089..e81abc3e18d82fa235a69911eb117ad0dcf54fd2 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp -@@ -3674,6 +3674,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) { +@@ -3757,6 +3757,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) { } void Document::ApplySettingsFromCSP(bool aSpeculative) { @@ -794,7 +794,7 @@ index c6cb09e1955d371cd19f563b30b486bcc2318304..d836946872b8e32360a925be50844721 nsresult rv = NS_OK; if (!aSpeculative) { // 1) apply settings from regular CSP -@@ -3731,6 +3734,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { +@@ -3814,6 +3817,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { MOZ_ASSERT(!mScriptGlobalObject, "CSP must be initialized before mScriptGlobalObject is set!"); @@ -806,7 +806,7 @@ index c6cb09e1955d371cd19f563b30b486bcc2318304..d836946872b8e32360a925be50844721 // If this is a data document - no need to set CSP. if (mLoadedAsData) { return NS_OK; -@@ -4501,6 +4509,10 @@ bool Document::HasFocus(ErrorResult& rv) const { +@@ -4613,6 +4621,10 @@ bool Document::HasFocus(ErrorResult& rv) const { return false; } @@ -817,7 +817,7 @@ index c6cb09e1955d371cd19f563b30b486bcc2318304..d836946872b8e32360a925be50844721 if (!fm->IsInActiveWindow(bc)) { return false; } -@@ -18878,6 +18890,66 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const { +@@ -19080,6 +19092,66 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const { return PreferenceSheet::PrefsFor(*this).mColorScheme; } @@ -885,10 +885,10 @@ index c6cb09e1955d371cd19f563b30b486bcc2318304..d836946872b8e32360a925be50844721 if (!sLoadingForegroundTopLevelContentDocument) { return false; diff --git a/dom/base/Document.h b/dom/base/Document.h -index 7eea29947d91f6b99363d7bf4c69f4e7b3276636..227314db13631b825b9b0701e8f9e5e630f78a72 100644 +index 0021e452414f9b7dc7b32a1065a82986d12dfdd7..2325b7d65bc1fb98b1dce994724c8e75c902834e 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h -@@ -4035,6 +4035,9 @@ class Document : public nsINode, +@@ -4053,6 +4053,9 @@ class Document : public nsINode, // color-scheme meta tag. ColorScheme DefaultColorScheme() const; @@ -899,7 +899,7 @@ index 7eea29947d91f6b99363d7bf4c69f4e7b3276636..227314db13631b825b9b0701e8f9e5e6 static bool AutomaticStorageAccessPermissionCanBeGranted( diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp -index a7229fe412644212747646bee5e111cb427bab52..4fdefb186804ed39d4670cca32e495d95f3546d6 100644 +index e26e0968c11905a39bfcfeea60b4989126780084..376165771df0e215d9e1c78ae5d3669e525bcf31 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -344,14 +344,18 @@ void Navigator::GetAppName(nsAString& aAppName) const { @@ -938,7 +938,7 @@ index a7229fe412644212747646bee5e111cb427bab52..4fdefb186804ed39d4670cca32e495d9 // The returned value is cached by the binding code. The window listens to the // accept languages change and will clear the cache when needed. It has to -@@ -2308,7 +2318,8 @@ bool Navigator::Webdriver() { +@@ -2307,7 +2317,8 @@ bool Navigator::Webdriver() { } #endif @@ -949,7 +949,7 @@ index a7229fe412644212747646bee5e111cb427bab52..4fdefb186804ed39d4670cca32e495d9 AutoplayPolicy Navigator::GetAutoplayPolicy(AutoplayPolicyMediaType aType) { diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h -index 4c400554f9b129f4482b513b46b90b780f2b8796..6efdca2363d83327562751757753abd602c80ddd 100644 +index 6abf6cef230c97815f17f6b7abf9f1b1de274a6f..46ead1f32e0d710b5b32e61dff72a4f772d5421e 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -218,7 +218,7 @@ class Navigator final : public nsISupports, public nsWrapperCache { @@ -962,10 +962,10 @@ index 4c400554f9b129f4482b513b46b90b780f2b8796..6efdca2363d83327562751757753abd6 dom::MediaCapabilities* MediaCapabilities(); dom::MediaSession* MediaSession(); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp -index 1edbffd5353a77fd84bc9abecb0628557512fa67..33376c1d44dbc0561c210e48401d6b173924067d 100644 +index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a69257799fec 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp -@@ -8796,7 +8796,8 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -8829,7 +8829,8 @@ nsresult nsContentUtils::SendMouseEvent( bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized, @@ -975,67 +975,69 @@ index 1edbffd5353a77fd84bc9abecb0628557512fa67..33376c1d44dbc0561c210e48401d6b17 nsPoint offset; nsCOMPtr widget = GetWidget(aPresShell, &offset); if (!widget) return NS_ERROR_FAILURE; -@@ -8804,6 +8805,7 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -8837,6 +8838,7 @@ nsresult nsContentUtils::SendMouseEvent( EventMessage msg; Maybe exitFrom; bool contextMenuKey = false; -+ bool isDragEvent = false; ++ bool isPWDragEventMessage = false; if (aType.EqualsLiteral("mousedown")) { msg = eMouseDown; } else if (aType.EqualsLiteral("mouseup")) { -@@ -8828,6 +8830,12 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -8861,6 +8863,12 @@ nsresult nsContentUtils::SendMouseEvent( msg = eMouseHitTest; } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) { msg = eMouseExploreByTouch; + } else if (aType.EqualsLiteral("dragover")) { + msg = eDragOver; -+ isDragEvent = true; ++ isPWDragEventMessage = true; + } else if (aType.EqualsLiteral("drop")) { + msg = eDrop; -+ isDragEvent = true; ++ isPWDragEventMessage = true; } else { return NS_ERROR_FAILURE; } -@@ -8836,12 +8844,21 @@ nsresult nsContentUtils::SendMouseEvent( - aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE; - } +@@ -8871,7 +8879,14 @@ nsresult nsContentUtils::SendMouseEvent( -- WidgetMouseEvent event(true, msg, widget, -+ std::unique_ptr eventOwner; -+ if (isDragEvent) { -+ eventOwner.reset(new WidgetDragEvent(true, msg, widget)); -+ eventOwner->mReason = aIsWidgetEventSynthesized + Maybe pointerEvent; + Maybe mouseEvent; +- if (IsPointerEventMessage(msg)) { ++ Maybe pwDragEvent; ++ ++ if (isPWDragEventMessage) { ++ pwDragEvent.emplace(true, msg, widget); ++ pwDragEvent->mReason = aIsWidgetEventSynthesized + ? WidgetMouseEvent::eSynthesized + : WidgetMouseEvent::eReal; -+ } else { -+ eventOwner.reset(new WidgetMouseEvent(true, msg, widget, - aIsWidgetEventSynthesized - ? WidgetMouseEvent::eSynthesized - : WidgetMouseEvent::eReal, - contextMenuKey ? WidgetMouseEvent::eContextMenuKey -- : WidgetMouseEvent::eNormal); -+ : WidgetMouseEvent::eNormal)); -+ } -+ WidgetMouseEvent& event = *eventOwner.get(); - event.pointerId = aIdentifier; - event.mModifiers = GetWidgetModifiers(aModifiers); - event.mButton = aButton; -@@ -8852,8 +8869,10 @@ nsresult nsContentUtils::SendMouseEvent( - event.mPressure = aPressure; - event.mInputSource = aInputSourceArg; - event.mClickCount = aClickCount; -+ event.mJugglerEventId = aJugglerEventId; - event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized; - event.mExitFrom = exitFrom; -+ event.convertToPointer = convertToPointer; ++ } else if (IsPointerEventMessage(msg)) { + MOZ_ASSERT(!aIsWidgetEventSynthesized, + "The event shouldn't be dispatched as a synthesized event"); + if (MOZ_UNLIKELY(aIsWidgetEventSynthesized)) { +@@ -8890,8 +8905,11 @@ nsresult nsContentUtils::SendMouseEvent( + contextMenuKey ? WidgetMouseEvent::eContextMenuKey + : WidgetMouseEvent::eNormal); + } ++ + WidgetMouseEvent& mouseOrPointerEvent = ++ pwDragEvent.isSome() ? pwDragEvent.ref() : + pointerEvent.isSome() ? pointerEvent.ref() : mouseEvent.ref(); ++ + mouseOrPointerEvent.pointerId = aIdentifier; + mouseOrPointerEvent.mModifiers = GetWidgetModifiers(aModifiers); + mouseOrPointerEvent.mButton = aButton; +@@ -8904,6 +8922,8 @@ nsresult nsContentUtils::SendMouseEvent( + mouseOrPointerEvent.mClickCount = aClickCount; + mouseOrPointerEvent.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized; + mouseOrPointerEvent.mExitFrom = exitFrom; ++ mouseOrPointerEvent.mJugglerEventId = aJugglerEventId; ++ mouseOrPointerEvent.convertToPointer = convertToPointer; nsPresContext* presContext = aPresShell->GetPresContext(); if (!presContext) return NS_ERROR_FAILURE; diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h -index ef3c1fd7cbb3a6c457ec7d70a50fd412077f4279..bd4e6e5db6273f024684169439fd31e0095b45f4 100644 +index 3837cce20cfb7cc3c5a93e7b595dee632739de5c..81ccfbe139e7041eb862ab3b085f1dae76bf0a5c 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h -@@ -3078,7 +3078,8 @@ class nsContentUtils { +@@ -3093,7 +3093,8 @@ class nsContentUtils { int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, mozilla::PreventDefaultResult* aPreventDefault, @@ -1046,7 +1048,7 @@ index ef3c1fd7cbb3a6c457ec7d70a50fd412077f4279..bd4e6e5db6273f024684169439fd31e0 static void FirePageShowEventForFrameLoaderSwap( nsIDocShellTreeItem* aItem, diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp -index 6d611b4a8485325435267c89c88b5511bb37d2f2..13640d6bd8fc34797f5f0088bf12ff016b4b3ae7 100644 +index e2de2b30c094e30db4d33e6cf8e5fbf83f219876..f937f561c0524e04563129f2cb762ae4127e6462 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -684,6 +684,26 @@ nsDOMWindowUtils::GetPresShellId(uint32_t* aPresShellId) { @@ -1111,7 +1113,7 @@ index 6d611b4a8485325435267c89c88b5511bb37d2f2..13640d6bd8fc34797f5f0088bf12ff01 if (aPreventDefault) { *aPreventDefault = preventDefaultResult != PreventDefaultResult::No; diff --git a/dom/base/nsDOMWindowUtils.h b/dom/base/nsDOMWindowUtils.h -index 63968c9b7a4e418e4c0de6e7a75fa215a36a9105..decf3ea3833ccdffd49a7aded2d600f9416e8306 100644 +index 47ff326b202266b1d7d6af8bdfb72776df8a6a93..b8e084b0c788c46345b1455b8257f1719c851404 100644 --- a/dom/base/nsDOMWindowUtils.h +++ b/dom/base/nsDOMWindowUtils.h @@ -93,7 +93,7 @@ class nsDOMWindowUtils final : public nsIDOMWindowUtils, @@ -1124,7 +1126,7 @@ index 63968c9b7a4e418e4c0de6e7a75fa215a36a9105..decf3ea3833ccdffd49a7aded2d600f9 MOZ_CAN_RUN_SCRIPT nsresult SendTouchEventCommon( diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp -index 587f03849d72d72020e89f4456dec481c9ede9f6..d0a910d3ae25fd4f6545f6d9130c8be04a06ed0e 100644 +index 22c175c93ef7bc81640b0ad71bd6ca9c1082fea6..7d77e91afbfe7aebe0c94793c2e0606715e3acdb 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -1684,6 +1684,10 @@ Maybe nsFocusManager::SetFocusInner(Element* aNewContent, @@ -1170,7 +1172,7 @@ index 587f03849d72d72020e89f4456dec481c9ede9f6..d0a910d3ae25fd4f6545f6d9130c8be0 // care of lowering the present active window. This happens in // a separate runnable to avoid touching multiple windows in diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp -index 460ccc17f2cd34f172215aaf5616badaa44f8ca5..d294373ca9b8987dd8bf056f4dae72c27903dcd7 100644 +index e47d4979078343102f00e93df913ff778b841804..360ab27a8f3394d18b558de80b5d0bbb963c1391 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -2514,10 +2514,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, @@ -1227,10 +1229,10 @@ index 0039d6d91b23953afbd6aec2b4d1f064db3c3b1c..7a6c5da16651d34ea60c69331365d948 // Outer windows only. virtual void EnsureSizeAndPositionUpToDate() override; diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp -index 600fce143a0e1e35a18b980211686436be08533f..ec6f7c60d0a3756dcf8892e4690281e1a65f9b6a 100644 +index 4b54dcd5b4fc9c575552ae82d5ed66f313cdeb72..e75b5f148d55d8f7d7e098a84930fec0e28aa01d 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp -@@ -1387,6 +1387,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions, +@@ -1402,6 +1402,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions, mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv); } @@ -1293,10 +1295,10 @@ index 600fce143a0e1e35a18b980211686436be08533f..ec6f7c60d0a3756dcf8892e4690281e1 DOMQuad& aQuad, const GeometryNode& aFrom, const ConvertCoordinateOptions& aOptions, CallerType aCallerType, diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h -index 2906bbb56c86cd287620b4bd067366f6703299d7..06697f07c7544c816181fa9849ce178bf38303aa 100644 +index 6f980f472aefe147de47212717ca300e62e02952..3d60daf88196ed502fc647cc7b51d2eb70a281ef 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h -@@ -2282,6 +2282,10 @@ class nsINode : public mozilla::dom::EventTarget { +@@ -2303,6 +2303,10 @@ class nsINode : public mozilla::dom::EventTarget { nsTArray>& aResult, ErrorResult& aRv); @@ -1336,7 +1338,7 @@ index cceb725d393d5e5f83c8f87491089c3fa1d57cc3..e906a7fb7c3fd72554613f640dcc272e static bool DumpEnabled(); diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl -index d70f3e18cc8e8f749e5057297161206129871453..2f2be2a6539203d1957bfe580a06ab70a512c053 100644 +index 864890f6a23b21a2a59687e4e2873b6837c05fbb..a34005c323d4b8e35b5bdb2b6eec2a268f8adc4b 100644 --- a/dom/chrome-webidl/BrowsingContext.webidl +++ b/dom/chrome-webidl/BrowsingContext.webidl @@ -53,6 +53,24 @@ enum PrefersColorSchemeOverride { @@ -1378,10 +1380,10 @@ index d70f3e18cc8e8f749e5057297161206129871453..2f2be2a6539203d1957bfe580a06ab70 * A unique identifier for the browser element that is hosting this * BrowsingContext tree. Every BrowsingContext in the element's tree will diff --git a/dom/geolocation/Geolocation.cpp b/dom/geolocation/Geolocation.cpp -index cb9107deb1acfc6f9f3efe87144fcd9bbccd9231..5034c066db8e13dbd01b9bbe79ac2447135f3360 100644 +index 21717aba5547b973e439ae9ba525f358d044d3f8..274cdebc2e0a2eb9f8b7743d24921204a417f76d 100644 --- a/dom/geolocation/Geolocation.cpp +++ b/dom/geolocation/Geolocation.cpp -@@ -23,6 +23,7 @@ +@@ -24,6 +24,7 @@ #include "nsComponentManagerUtils.h" #include "nsContentPermissionHelper.h" #include "nsContentUtils.h" @@ -1389,7 +1391,7 @@ index cb9107deb1acfc6f9f3efe87144fcd9bbccd9231..5034c066db8e13dbd01b9bbe79ac2447 #include "nsGlobalWindowInner.h" #include "mozilla/dom/Document.h" #include "nsINamed.h" -@@ -256,10 +257,8 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) { +@@ -264,10 +265,8 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) { return NS_OK; } @@ -1402,7 +1404,7 @@ index cb9107deb1acfc6f9f3efe87144fcd9bbccd9231..5034c066db8e13dbd01b9bbe79ac2447 CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition(); if (lastPosition.position) { EpochTimeStamp cachedPositionTime_ms; -@@ -437,8 +436,7 @@ void nsGeolocationRequest::Shutdown() { +@@ -475,8 +474,7 @@ void nsGeolocationRequest::Shutdown() { // If there are no other high accuracy requests, the geolocation service will // notify the provider to switch to the default accuracy. if (mOptions && mOptions->mEnableHighAccuracy) { @@ -1412,7 +1414,7 @@ index cb9107deb1acfc6f9f3efe87144fcd9bbccd9231..5034c066db8e13dbd01b9bbe79ac2447 if (gs) { gs->UpdateAccuracy(); } -@@ -727,8 +725,14 @@ void nsGeolocationService::StopDevice() { +@@ -785,8 +783,14 @@ void nsGeolocationService::StopDevice() { StaticRefPtr nsGeolocationService::sService; already_AddRefed @@ -1428,7 +1430,7 @@ index cb9107deb1acfc6f9f3efe87144fcd9bbccd9231..5034c066db8e13dbd01b9bbe79ac2447 if (nsGeolocationService::sService) { result = nsGeolocationService::sService; -@@ -820,7 +824,9 @@ nsresult Geolocation::Init(nsPIDOMWindowInner* aContentDom) { +@@ -878,7 +882,9 @@ nsresult Geolocation::Init(nsPIDOMWindowInner* aContentDom) { // If no aContentDom was passed into us, we are being used // by chrome/c++ and have no mOwner, no mPrincipal, and no need // to prompt. @@ -1477,10 +1479,10 @@ index 7e1af00d05fbafa2d828e2c7e4dcc5c82d115f5b..e85af9718d064e4d2865bc944e9d4ba1 ~Geolocation(); diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp -index 30093e5d408caa054a04adddf63ce2bec384eed6..2852746b6f5b50981dba29a65ce25c1fd55390e3 100644 +index e2a77a11435a80abbb6381ffabbb5711eca0ac0d..a614efef052ca7c39457726d1f1e66f7cff777f8 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp -@@ -57,6 +57,7 @@ +@@ -59,6 +59,7 @@ #include "mozilla/dom/Document.h" #include "mozilla/dom/HTMLDataListElement.h" #include "mozilla/dom/HTMLOptionElement.h" @@ -1488,7 +1490,7 @@ index 30093e5d408caa054a04adddf63ce2bec384eed6..2852746b6f5b50981dba29a65ce25c1f #include "nsIFormControlFrame.h" #include "nsITextControlFrame.h" #include "nsIFrame.h" -@@ -783,6 +784,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) { +@@ -784,6 +785,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) { return NS_ERROR_FAILURE; } @@ -1499,14 +1501,14 @@ index 30093e5d408caa054a04adddf63ce2bec384eed6..2852746b6f5b50981dba29a65ce25c1f + return NS_OK; + } + - if (IsPopupBlocked(doc)) { + if (IsPickerBlocked(doc)) { return NS_OK; } diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl -index 9d185e8e7edcde63f0d2e0c05a32dfddaf71609c..9d48d2e33575c7f214152c6f8140f9a3a3313b44 100644 +index ac0251b4989799e9bb370a8066d10f13154bbc7c..184f4d980c35652c67da06e917e9d0b85ff34cea 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl -@@ -373,6 +373,26 @@ interface nsIDOMWindowUtils : nsISupports { +@@ -374,6 +374,26 @@ interface nsIDOMWindowUtils : nsISupports { [optional] in long aButtons, [optional] in unsigned long aIdentifier); @@ -1534,10 +1536,10 @@ index 9d185e8e7edcde63f0d2e0c05a32dfddaf71609c..9d48d2e33575c7f214152c6f8140f9a3 * touchstart, touchend, touchmove, and touchcancel * diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp -index 27fb1239dbd2a635688d022602d4a49dfff0560a..39f9dd48eef038503a50632c5e1395fecea6cae3 100644 +index 204ee71ece1afa8b416caafcb4bdd242344f1a26..8597f2d0c4bd7a6fbfed9f29d002d0c59c8f8ae9 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp -@@ -1639,6 +1639,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, +@@ -1656,6 +1656,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, if (postLayerization) { postLayerization->Register(); } @@ -1820,7 +1822,7 @@ index 3b39538e51840cd9b1685b2efd2ff2e9ec83608a..c7bf4f2d53b58bbacb22b3ebebf6f3fc return aGlobalOrNull; diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp -index f4aecfaf44d40d651f816c56db4b46c605754132..ef017504972454c12de7d6a7ff38a76a8253a62d 100644 +index 4eafb2247d5aa8e989c0359d6d9d864edf73759d..e0d0b5bc78537c6fa8d0cf02cc6c772993008b91 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -22,6 +22,7 @@ @@ -1867,7 +1869,7 @@ index 2f71b284ee5f7e11f117c447834b48355784448c..2640bd57123c2b03bf4b06a2419cd020 * returned quads are further translated relative to the window * origin -- which is not the layout origin. Further translation diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp -index 4a7ebb25233ce685e73d53085e22337e9ad8bc59..0b7b24a4da5511ff2fa6695eb55f5533b2e574ab 100644 +index 6085248083194be05e85c3be7f0e69fd1928bf3d..23b72e2d0030496d5b05c88f06ed1a30ed33396b 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -998,7 +998,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) { @@ -1889,7 +1891,7 @@ index 4a7ebb25233ce685e73d53085e22337e9ad8bc59..0b7b24a4da5511ff2fa6695eb55f5533 mNavigatorPropertiesLoaded = true; } -@@ -1795,6 +1794,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted( +@@ -1808,6 +1807,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted( } } @@ -1903,7 +1905,7 @@ index 4a7ebb25233ce685e73d53085e22337e9ad8bc59..0b7b24a4da5511ff2fa6695eb55f5533 template void RuntimeService::BroadcastAllWorkers(const Func& aFunc) { AssertIsOnMainThread(); -@@ -2314,6 +2320,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers( +@@ -2333,6 +2339,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers( } } @@ -1919,10 +1921,10 @@ index 4a7ebb25233ce685e73d53085e22337e9ad8bc59..0b7b24a4da5511ff2fa6695eb55f5533 MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aCx); diff --git a/dom/workers/RuntimeService.h b/dom/workers/RuntimeService.h -index f51076ac1480794989999d00577bc9cf1566d5f9..fe15b2e00dc8f0bf203f2af9aad86e16c996d43d 100644 +index 534bbe9ec4f0261189eb3322c1229c1eb5d8802e..6aa99b64fdbbff3704602e944b129879fbdf8c15 100644 --- a/dom/workers/RuntimeService.h +++ b/dom/workers/RuntimeService.h -@@ -109,6 +109,8 @@ class RuntimeService final : public nsIObserver { +@@ -112,6 +112,8 @@ class RuntimeService final : public nsIObserver { void PropagateStorageAccessPermissionGranted( const nsPIDOMWindowInner& aWindow); @@ -1932,10 +1934,10 @@ index f51076ac1480794989999d00577bc9cf1566d5f9..fe15b2e00dc8f0bf203f2af9aad86e16 return mNavigatorProperties; } diff --git a/dom/workers/WorkerCommon.h b/dom/workers/WorkerCommon.h -index d10dabb5c5ff8e17851edf2bd2efc08e74584d8e..53c4070c5fde43b27fb8fbfdcf4c23d8af57fba3 100644 +index 58894a8361c7ef1dddd481ca5877a209a8b8ff5c..c481d40d79b6397b7f1d571bd9f6ae5c0a946217 100644 --- a/dom/workers/WorkerCommon.h +++ b/dom/workers/WorkerCommon.h -@@ -44,6 +44,8 @@ void ResumeWorkersForWindow(const nsPIDOMWindowInner& aWindow); +@@ -47,6 +47,8 @@ void ResumeWorkersForWindow(const nsPIDOMWindowInner& aWindow); void PropagateStorageAccessPermissionGrantedToWorkers( const nsPIDOMWindowInner& aWindow); @@ -1945,10 +1947,10 @@ index d10dabb5c5ff8e17851edf2bd2efc08e74584d8e..53c4070c5fde43b27fb8fbfdcf4c23d8 bool IsWorkerGlobal(JSObject* global); diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp -index 7fbfdb0eeed2fc9d9a6ba12192150d5bdeed40b3..c31ae2724d09036ec2ba0b71cd94f648e9b90868 100644 +index 089f42307becf7c6f81199d970fb8870db494818..63fb760ac831bc88415aee1cddf8b59662e55f37 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp -@@ -682,6 +682,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { +@@ -700,6 +700,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { } }; @@ -1967,7 +1969,7 @@ index 7fbfdb0eeed2fc9d9a6ba12192150d5bdeed40b3..c31ae2724d09036ec2ba0b71cd94f648 class UpdateLanguagesRunnable final : public WorkerThreadRunnable { nsTArray mLanguages; -@@ -2091,6 +2103,16 @@ void WorkerPrivate::UpdateContextOptions( +@@ -2108,6 +2120,16 @@ void WorkerPrivate::UpdateContextOptions( } } @@ -1984,7 +1986,7 @@ index 7fbfdb0eeed2fc9d9a6ba12192150d5bdeed40b3..c31ae2724d09036ec2ba0b71cd94f648 void WorkerPrivate::UpdateLanguages(const nsTArray& aLanguages) { AssertIsOnParentThread(); -@@ -5667,6 +5689,15 @@ void WorkerPrivate::UpdateContextOptionsInternal( +@@ -5736,6 +5758,15 @@ void WorkerPrivate::UpdateContextOptionsInternal( } } @@ -2001,10 +2003,10 @@ index 7fbfdb0eeed2fc9d9a6ba12192150d5bdeed40b3..c31ae2724d09036ec2ba0b71cd94f648 const nsTArray& aLanguages) { WorkerGlobalScope* globalScope = GlobalScope(); diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h -index 57212e01fb75da52187195acfbe052b19464286a..bc75882ee661d5c987187cd11b388443227d59bc 100644 +index dfb96b7b798785d7b75c683bc0969e39487137a3..a463eec618af51fdbc25db509870598846c0fd66 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h -@@ -418,6 +418,8 @@ class WorkerPrivate final +@@ -432,6 +432,8 @@ class WorkerPrivate final void UpdateContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContextOptions); @@ -2013,7 +2015,7 @@ index 57212e01fb75da52187195acfbe052b19464286a..bc75882ee661d5c987187cd11b388443 void UpdateLanguagesInternal(const nsTArray& aLanguages); void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, -@@ -1045,6 +1047,8 @@ class WorkerPrivate final +@@ -1059,6 +1061,8 @@ class WorkerPrivate final void UpdateContextOptions(const JS::ContextOptions& aContextOptions); @@ -2245,10 +2247,10 @@ index 0ec6ee3eb37c6493d8a25352fd0e54e1927bceab..885dba71bc5815e5f6f3ec2700c376aa // No boxes to return return; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp -index 6e588cff05c8d6fdaec53a980fce1bc8d2141953..a173b1154e171d7fa5454b27baf85f72a09501a6 100644 +index f154e05a8c2e2ebf07565d087a42436feeb17f53..0af8c24c0f391c52fe2acfeb01cacb32358e6861 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp -@@ -11063,7 +11063,9 @@ bool PresShell::ComputeActiveness() const { +@@ -11064,7 +11064,9 @@ bool PresShell::ComputeActiveness() const { if (!browserChild->IsVisible()) { MOZ_LOG(gLog, LogLevel::Debug, (" > BrowserChild %p is not visible", browserChild)); @@ -2260,7 +2262,7 @@ index 6e588cff05c8d6fdaec53a980fce1bc8d2141953..a173b1154e171d7fa5454b27baf85f72 // If the browser is visible but just due to be preserving layers diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp -index 2ed62888d70663f3560fcaa9bc29ff98cb44c323..f5540c38df6a064094e013c841d943c63049dd75 100644 +index 0011b1a1a36da0dec7cc6afa6fd689a4c8710d37..25bebd7b03b0b8dc595607bae07f360f3be3f284 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -698,6 +698,10 @@ bool nsLayoutUtils::AllowZoomingForDocument( @@ -2274,7 +2276,7 @@ index 2ed62888d70663f3560fcaa9bc29ff98cb44c323..f5540c38df6a064094e013c841d943c6 // True if we allow zooming for all documents on this platform, or if we are // in RDM. BrowsingContext* bc = aDocument->GetBrowsingContext(); -@@ -9794,6 +9798,9 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont, +@@ -9791,6 +9795,9 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont, /* static */ bool nsLayoutUtils::ShouldHandleMetaViewport(const Document* aDocument) { @@ -2318,20 +2320,20 @@ index cc86d1abf6ccfe48530607c41cd675612cbe5582..8cce20c719fee8a0480ae6ea1fd53c66 bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document* aDocument) { diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp -index 5ff1c5ad8b265f25ab5a18a639e4e5b420d93443..a788218d4f281daee274d14b7dd15f4c19eeddce 100644 +index 21d5a5e1b4193d058c30268ab73c8d595436b381..11b960ec0ff3ea77857cb915d05bbdbb6772bb37 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp -@@ -691,7 +691,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) - mInterceptionInfo(rhs.mInterceptionInfo), +@@ -693,7 +693,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) mHasInjectedCookieForCookieBannerHandling( rhs.mHasInjectedCookieForCookieBannerHandling), -- mWasSchemelessInput(rhs.mWasSchemelessInput) { -+ mWasSchemelessInput(rhs.mWasSchemelessInput), + mWasSchemelessInput(rhs.mWasSchemelessInput), +- mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry) { ++ mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry), + mJugglerLoadIdentifier(rhs.mJugglerLoadIdentifier) { } LoadInfo::LoadInfo( -@@ -2416,4 +2417,16 @@ LoadInfo::SetWasSchemelessInput(bool aWasSchemelessInput) { +@@ -2461,4 +2462,16 @@ LoadInfo::SetHttpsUpgradeTelemetry( return NS_OK; } @@ -2349,23 +2351,26 @@ index 5ff1c5ad8b265f25ab5a18a639e4e5b420d93443..a788218d4f281daee274d14b7dd15f4c + } // namespace mozilla::net diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h -index e6badeeee816bc74af22fb9ef5f88b58f13ac5b7..994216ee9b26e7cbc85b948165051d5d2bc7efb1 100644 +index 52d867196a459578cbea1a4f626afbe51dd1abd5..2904832cbcad476fdebb54c7e24d5f14b1c0fb4b 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h -@@ -408,6 +408,8 @@ class LoadInfo final : public nsILoadInfo { +@@ -413,9 +413,10 @@ class LoadInfo final : public nsILoadInfo { bool mHasInjectedCookieForCookieBannerHandling = false; bool mWasSchemelessInput = false; +- + nsILoadInfo::HTTPSUpgradeTelemetryType mHttpsUpgradeTelemetry = + nsILoadInfo::NO_UPGRADE; + + uint64_t mJugglerLoadIdentifier = 0; }; // This is exposed solely for testing purposes and should not be used outside of diff --git a/netwerk/base/TRRLoadInfo.cpp b/netwerk/base/TRRLoadInfo.cpp -index 48560a8b3be4ace3aab241373ff1eab0e5bb2187..b2114472b04b4e837b1c7b080ce8718f5f67f43b 100644 +index 9dc2bb0da6871b905abd17d931e555429977c6c2..b71cf6393492346f16417b3ba745a235a483be22 100644 --- a/netwerk/base/TRRLoadInfo.cpp +++ b/netwerk/base/TRRLoadInfo.cpp -@@ -870,5 +870,15 @@ TRRLoadInfo::SetWasSchemelessInput(bool aWasSchemelessInput) { +@@ -903,5 +903,15 @@ TRRLoadInfo::SetHttpsUpgradeTelemetry( return NS_ERROR_NOT_IMPLEMENTED; } @@ -2382,14 +2387,13 @@ index 48560a8b3be4ace3aab241373ff1eab0e5bb2187..b2114472b04b4e837b1c7b080ce8718f } // namespace net } // namespace mozilla diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl -index 8ff5e556c98689542297517a7bdf57e0a2ccf400..b1429dbe180cbc84cf467991bb24124f5857d62b 100644 +index 12f43b911006d5b0bbfa9936070dc0d561bc7bb4..94d20cdca548534ad5e4ef4f937e287c58768870 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl -@@ -1544,4 +1544,6 @@ interface nsILoadInfo : nsISupports - * Whether the load has gone through the URL bar, where the fixup had to add * the protocol scheme. +@@ -1586,4 +1586,5 @@ interface nsILoadInfo : nsISupports */ - [infallible] attribute boolean wasSchemelessInput; -+ + [infallible] attribute nsILoadInfo_HTTPSUpgradeTelemetryType httpsUpgradeTelemetry; + + [infallible] attribute unsigned long long jugglerLoadIdentifier; }; diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl @@ -2405,19 +2409,19 @@ index 7f91d2df6f8bb4020c75c132dc8f6bf26625fa1e..ba6569f4be8fc54ec96ee44d5de45a09 /** * Set the status and reason for the forthcoming synthesized response. diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp -index dfd80e8867ec46464ddcfc30316236c824950cb1..a702bbe63cf56984519000854e9f487dcac3cee4 100644 +index 10f65a549ce886bf7f19de02714482e28a8931a5..f41d32ce90f7345ad5a9bd90e420354865f35235 100644 --- a/netwerk/ipc/DocumentLoadListener.cpp +++ b/netwerk/ipc/DocumentLoadListener.cpp -@@ -168,6 +168,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext, - loadInfo->SetHasValidUserGestureActivation( - aLoadState->HasValidUserGestureActivation()); +@@ -171,6 +171,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext, + loadInfo->SetTextDirectiveUserActivation( + aLoadState->GetTextDirectiveUserActivation()); loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh()); + loadInfo->SetJugglerLoadIdentifier(aLoadState->GetLoadIdentifier()); return loadInfo.forget(); } diff --git a/netwerk/protocol/http/InterceptedHttpChannel.cpp b/netwerk/protocol/http/InterceptedHttpChannel.cpp -index 5695d46f924abe6b765f3645d746cc4248051c1c..d28ead55f6a8458f70ca43c693e7396c5dc53858 100644 +index e81a4538fd45c13aa60d933de5f4f32ce69fb5f2..d7945f81295c497485a09696f06ce041c1cd8079 100644 --- a/netwerk/protocol/http/InterceptedHttpChannel.cpp +++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp @@ -727,6 +727,14 @@ NS_IMPL_ISUPPORTS(ResetInterceptionHeaderVisitor, nsIHttpHeaderVisitor) @@ -2590,7 +2594,7 @@ index df1c5e464b845b6a8bfedadb86d0e7aab7fd3ffc..34451e791bb59f635134de702d9e5f64 } diff --git a/toolkit/components/browser/nsIWebBrowserChrome.idl b/toolkit/components/browser/nsIWebBrowserChrome.idl -index 217beda78edf31bab4c37209964d7a5bf5425195..7ba723410eb93328a8f078c58a96eefc2599feea 100644 +index 75555352b8a15a50e4a21e34fc8ede4e9246c7cc..72855a404effa42b6c55cd0c2fcb8bdd6c2b3f9f 100644 --- a/toolkit/components/browser/nsIWebBrowserChrome.idl +++ b/toolkit/components/browser/nsIWebBrowserChrome.idl @@ -74,6 +74,9 @@ interface nsIWebBrowserChrome : nsISupports @@ -2621,10 +2625,10 @@ index 00a5381133f8cec0de452c31c7151801a1acc0b9..5d3e3d6f566dc724f257beaeb994ceda if (provider.failed) { diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp -index 32b1ac481382dd6aa3dda5572f013c2447a1a004..808031fbeb9b99b67c13c99c66b1aa1aff41f48a 100644 +index 144628a310662eb393d8c1a4fffbec3cf5fd1dff..69fa66f27d0533f3d90801acbfa23039ef81f7df 100644 --- a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp +++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp -@@ -525,7 +525,7 @@ void PopulateLanguages() { +@@ -632,7 +632,7 @@ void PopulateLanguages() { // sufficient to only collect this information as the other properties are // just reformats of Navigator::GetAcceptLanguages. nsTArray languages; @@ -2662,10 +2666,10 @@ index 654903fadb709be976b72f36f155e23bc0622152..815b3dc24c9fda6b1db6c4666ac68904 int32_t aMaxSelfProgress, int32_t aCurTotalProgress, diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp -index 0767cb1539f940e5f634b58de44d876606903a09..dc0d72b4ff36d5ba7808528aefecb33f05b6672c 100644 +index cdba76dc8ae2206a58d7e5eb6eba97c2c3732513..266fdc6235363eafc6c7b587d5c0f597deee6e59 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp +++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp -@@ -1861,7 +1861,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent( +@@ -1865,7 +1865,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent( // Open a minimal popup. *aIsPopupRequested = true; @@ -2679,10 +2683,10 @@ index 0767cb1539f940e5f634b58de44d876606903a09..dc0d72b4ff36d5ba7808528aefecb33f /** diff --git a/toolkit/mozapps/update/UpdateService.sys.mjs b/toolkit/mozapps/update/UpdateService.sys.mjs -index deaed885c759d8e53ebf0beb53c5b7c4d4bd82f0..8e01e16490ab063361220d363494dfdf00442342 100644 +index be01248253ee1bcc9435c3e8223ed032f498a023..0f05923c29a023511b72a81ec527300cafa17760 100644 --- a/toolkit/mozapps/update/UpdateService.sys.mjs +++ b/toolkit/mozapps/update/UpdateService.sys.mjs -@@ -3875,6 +3875,8 @@ export class UpdateService { +@@ -3888,6 +3888,8 @@ export class UpdateService { } get disabledForTesting() { @@ -2756,7 +2760,7 @@ index fe72a2715da8846146377e719559c16e6ef1f7ff..a5959143bac8f62ee359fa3883a844f3 // nsDocumentViewer::LoadComplete that doesn't do various things // that are not relevant here because this wasn't an actual diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp -index b9120ededd25707c90f33f65d3cead26433efdac..4d5728a73786d804d6b32da4d42934da2864eda1 100644 +index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790eafcbc6a 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -112,6 +112,7 @@ @@ -2968,45 +2972,21 @@ index 1c25e9d9a101233f71e92288a0f93125b81ac1c5..22cf67b0f6e3ddd2b3ed725a314ba6a9 } #endif diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h -index d29b406524c8b4afe437b559e33b4b2b5824ee58..6bef9c1657f93f90f96735d76fedb6ba3888b5c1 100644 +index 3d469853bbd30c433ee7b6d2be7175caa568196e..214b92f0a8913fb6667b7554410d4cd58c53cad3 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h -@@ -258,6 +258,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, - : mReason(eReal), - mContextMenuTrigger(eNormal), - mClickCount(0), -+ mJugglerEventId(0), - mIgnoreRootScrollFrame(false), - mClickEventPrevented(false) {} - -@@ -269,6 +270,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, - mReason(aReason), - mContextMenuTrigger(eNormal), - mClickCount(0), -+ mJugglerEventId(0), - mIgnoreRootScrollFrame(false), - mClickEventPrevented(false) {} - -@@ -288,6 +290,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, - mReason(aReason), - mContextMenuTrigger(aContextMenuTrigger), - mClickCount(0), -+ mJugglerEventId(0), - mIgnoreRootScrollFrame(false), - mClickEventPrevented(false) { - if (aMessage == eContextMenu) { -@@ -336,6 +339,9 @@ class WidgetMouseEvent : public WidgetMouseEventBase, +@@ -327,6 +327,9 @@ class WidgetMouseEvent : public WidgetMouseEventBase, // Otherwise, this must be 0. - uint32_t mClickCount; + uint32_t mClickCount = 0; + // Unique event ID -+ uint32_t mJugglerEventId; ++ uint32_t mJugglerEventId = 0; + // Whether the event should ignore scroll frame bounds during dispatch. - bool mIgnoreRootScrollFrame; - -@@ -348,6 +354,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, + bool mIgnoreRootScrollFrame = false; +@@ -341,6 +344,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, + mContextMenuTrigger = aEvent.mContextMenuTrigger; mExitFrom = aEvent.mExitFrom; mClickCount = aEvent.mClickCount; + mJugglerEventId = aEvent.mJugglerEventId; @@ -3226,7 +3206,7 @@ index facd2bc65afab8ec1aa322faa20a67464964dfb9..d6dea95472bec6006411753c3dfdab2e } // namespace widget diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp -index 419b3bf94011e6874588b042fa520e75522ed2c3..07dc3954986d8257dc4fce1aa810623bb5d90bbd 100644 +index c6095751bc1e9bbe907e64fb634b799cac31bb0a..ce1b995015843babeab0e3bf4e357d45066b3cab 100644 --- a/widget/headless/HeadlessWidget.cpp +++ b/widget/headless/HeadlessWidget.cpp @@ -111,6 +111,8 @@ void HeadlessWidget::Destroy() { @@ -3238,7 +3218,7 @@ index 419b3bf94011e6874588b042fa520e75522ed2c3..07dc3954986d8257dc4fce1aa810623b nsBaseWidget::OnDestroy(); nsBaseWidget::Destroy(); -@@ -620,5 +622,14 @@ nsresult HeadlessWidget::SynthesizeNativeTouchpadPan( +@@ -613,5 +615,14 @@ nsresult HeadlessWidget::SynthesizeNativeTouchpadPan( return NS_OK; } @@ -3268,7 +3248,7 @@ index 9856991ef32f25f51942f8cd664a09bec2192c70..948947a421179e91c51005aeb83ed0d1 ~HeadlessWidget(); bool mEnabled; diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h -index 8ba46829357fc4acc47bf20842fd869902efa000..a1b5b2c5230d90981bd563d4df2d2bf1c2e05cef 100644 +index 02775a7f27f5697bc33872d997198ce305556970..6c1ae0e371ee012ef47c8e9c74f949da05ad0025 100644 --- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -234,6 +234,7 @@ struct ParamTraits { diff --git a/browser_patches/firefox/preferences/playwright.cfg b/browser_patches/firefox/preferences/playwright.cfg index 1921f19c38..1eb51a0fdb 100644 --- a/browser_patches/firefox/preferences/playwright.cfg +++ b/browser_patches/firefox/preferences/playwright.cfg @@ -100,6 +100,11 @@ pref("extensions.formautofill.addresses.supported", "off"); // firefox behavior with other browser defaults. pref("security.enterprise_roots.enabled", true); +// There's a security features warning that might be shown on certain Linux distributions & configurations: +// https://support.mozilla.org/en-US/kb/install-firefox-linux#w_security-features-warning +// This notification should never be shown in automation scenarios. +pref("security.sandbox.warn_unprivileged_namespaces", false); + // Avoid stalling on shutdown, after "xpcom-will-shutdown" phase. // This at least happens when shutting down soon after launching. // See AppShutdown.cpp for more details on shutdown phases. diff --git a/browser_patches/webkit/UPSTREAM_CONFIG.sh b/browser_patches/webkit/UPSTREAM_CONFIG.sh index c90cb30712..57f8fb13f9 100644 --- a/browser_patches/webkit/UPSTREAM_CONFIG.sh +++ b/browser_patches/webkit/UPSTREAM_CONFIG.sh @@ -1,3 +1,3 @@ REMOTE_URL="https://github.com/WebKit/WebKit.git" BASE_BRANCH="main" -BASE_REVISION="f371dbc2bb4292037ed394e2162150a16ef977fc" +BASE_REVISION="2ea46ab90e6511139bfb94415205038b672381e0" diff --git a/browser_patches/webkit/patches/bootstrap.diff b/browser_patches/webkit/patches/bootstrap.diff index 9b8aa596c4..df3776b1a6 100644 --- a/browser_patches/webkit/patches/bootstrap.diff +++ b/browser_patches/webkit/patches/bootstrap.diff @@ -1,8 +1,8 @@ diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt -index 3a1013bab702f71303ee800f6b3e9a65a08f4de6..9448f06498ea591e51516d5ef7b543f63655b6ae 100644 +index 74eeee34f579a6596f8d59ad81f5303dbaba1556..6bf3b11b950a8fe0af990b916501e73858b2f51c 100644 --- a/Source/JavaScriptCore/CMakeLists.txt +++ b/Source/JavaScriptCore/CMakeLists.txt -@@ -1392,22 +1392,27 @@ set(JavaScriptCore_INSPECTOR_DOMAINS +@@ -1385,22 +1385,27 @@ set(JavaScriptCore_INSPECTOR_DOMAINS ${JAVASCRIPTCORE_DIR}/inspector/protocol/CSS.json ${JAVASCRIPTCORE_DIR}/inspector/protocol/Canvas.json ${JAVASCRIPTCORE_DIR}/inspector/protocol/Console.json @@ -1701,7 +1701,7 @@ index 24891ad836086fd23024fcb4d08ca63f6974c812..29f4b6b1923383fec7a99d28a4e815dc private: enum ArgumentRequirement { ArgumentRequired, ArgumentNotRequired }; diff --git a/Source/ThirdParty/libwebrtc/CMakeLists.txt b/Source/ThirdParty/libwebrtc/CMakeLists.txt -index a17275db76e30bf2f42bc8faa6dea14383b2ab27..f630ddaddfa5a5b048e028c233e84e8bec1e0370 100644 +index 2557956d875e6d5be0d83368f4eaaa8083585c26..da7bde1dc256e9e1f4d13469e150970f5090a993 100644 --- a/Source/ThirdParty/libwebrtc/CMakeLists.txt +++ b/Source/ThirdParty/libwebrtc/CMakeLists.txt @@ -453,6 +453,7 @@ set(webrtc_SOURCES @@ -1724,7 +1724,7 @@ index a17275db76e30bf2f42bc8faa6dea14383b2ab27..f630ddaddfa5a5b048e028c233e84e8b Source/third_party/libyuv/source/compare.cc Source/third_party/libyuv/source/compare_common.cc Source/third_party/libyuv/source/compare_gcc.cc -@@ -2402,6 +2408,10 @@ set(webrtc_INCLUDE_DIRECTORIES PRIVATE +@@ -2406,6 +2412,10 @@ set(webrtc_INCLUDE_DIRECTORIES PRIVATE Source/third_party/libsrtp/config Source/third_party/libsrtp/crypto/include Source/third_party/libsrtp/include @@ -1736,25 +1736,26 @@ index a17275db76e30bf2f42bc8faa6dea14383b2ab27..f630ddaddfa5a5b048e028c233e84e8b Source/third_party/opus/src/celt Source/third_party/opus/src/include diff --git a/Source/ThirdParty/libwebrtc/Configurations/Base-libwebrtc.xcconfig b/Source/ThirdParty/libwebrtc/Configurations/Base-libwebrtc.xcconfig -index 6b20d97d3d46359b2b2f9b4e8454a65c2ddbe9e3..80883fe3659389a3c385fd46ecd905bc0923d3ef 100644 +index 0c5c8e689bdddec766f9de5bffd4444a5e068d77..330dd1f585e530722178c65c883641a2b8c0f1bd 100644 --- a/Source/ThirdParty/libwebrtc/Configurations/Base-libwebrtc.xcconfig +++ b/Source/ThirdParty/libwebrtc/Configurations/Base-libwebrtc.xcconfig -@@ -22,6 +22,7 @@ - // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - HEADER_SEARCH_PATHS = Source Source/third_party/libsrtp/crypto/include Source/third_party/libsrtp/include Source/third_party/boringssl/src/include Source/third_party/libyuv/include Source/webrtc/sdk/objc/Framework/Headers Source/webrtc/common_audio/signal_processing/include Source/webrtc/modules/audio_coding/codecs/isac/main/include Source/third_party/opus/src/celt Source/third_party/opus/src/include Source/third_party/opus/src/src Source/webrtc/modules/audio_device/mac Source/webrtc/modules/audio_device/ios Source/webrtc Source/webrtc/sdk/objc Source/webrtc/sdk/objc/base Source/webrtc/sdk/objc/Framework/Classes Source/third_party/libsrtp/config Source/webrtc/sdk/objc/Framework/Classes/Common Source/webrtc/sdk/objc/Framework/Classes/Video Source/webrtc/sdk/objc/Framework/Classes/PeerConnection Source/third_party/abseil-cpp Source/third_party/libvpx/source/libvpx Source/third_party/libwebm/webm_parser/include Source/third_party/crc32c/config Source/third_party/crc32c/include Source/third_party/crc32c/src/include Source/third_party/libaom/source/libaom Source/third_party/protobuf/src; -+HEADER_SEARCH_PATHS = ${HEADER_SEARCH_PATHS} Source/third_party/libwebm/mkvmuxer Source/third_party/libvpx/source/libvpx/third_party/libwebm; +@@ -24,6 +24,8 @@ + HEADER_SEARCH_PATHS = Source Source/third_party/libsrtp/crypto/include Source/third_party/libsrtp/include Source/third_party/boringssl/src/include Source/third_party/libyuv/include Source/webrtc/webkit_sdk/objc/Framework/Headers Source/webrtc/common_audio/signal_processing/include Source/webrtc/modules/audio_coding/codecs/isac/main/include Source/third_party/opus/src/celt Source/third_party/opus/src/include Source/third_party/opus/src/src Source/webrtc/modules/audio_device/mac Source/webrtc/modules/audio_device/ios Source/webrtc Source/webrtc/webkit_sdk/objc Source/webrtc/webkit_sdk/objc/base Source/webrtc/webkit_sdk/objc/Framework/Classes Source/third_party/libsrtp/config Source/webrtc/webkit_sdk/objc/Framework/Classes/Common Source/webrtc/webkit_sdk/objc/Framework/Classes/Video Source/webrtc/webkit_sdk/objc/Framework/Classes/PeerConnection Source/third_party/abseil-cpp Source/third_party/libvpx/source/libvpx Source/third_party/libwebm/webm_parser/include Source/third_party/crc32c/config Source/third_party/crc32c/include Source/third_party/crc32c/src/include Source/third_party/libaom/source/libaom; USE_HEADERMAP = NO; ++HEADER_SEARCH_PATHS = ${HEADER_SEARCH_PATHS} Source/third_party/libwebm/mkvmuxer Source/third_party/libvpx/source/libvpx/third_party/libwebm; ++ WARNING_CFLAGS = -Wno-deprecated-declarations $(inherited); + + // FIXME: Set WEBRTC_USE_BUILTIN_ISAC_FIX and WEBRTC_USE_BUILTIN_ISAC_FLOAT for iOS and Mac diff --git a/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp b/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp -index fca61ffe9f0563d87b364e0fa681ceab1d7bb26f..5c0d72f6c0bab0bc0011a123302c5654ec5664ba 100644 +index 57588b1d0c8eefc9ab5a145a5e6cd65b0bd71f6c..1fca47ffc1166cd37a2ea56337440f26db6a2830 100644 --- a/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp +++ b/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp -@@ -403,3 +403,24 @@ __ZN3rtc17AsyncPacketSocket20NotifyPacketReceivedERKNS_14ReceivedPacketE - __ZN3rtc17AsyncPacketSocket30RegisterReceivedPacketCallbackEN4absl12AnyInvocableIFvPS0_RKNS_14ReceivedPacketEEEE - __ZN3rtc17AsyncPacketSocket32DeregisterReceivedPacketCallbackEv - __ZN3rtc18NetworkManagerBaseC2Ev +@@ -403,3 +403,24 @@ __ZN3rtc15CountIPMaskBitsERKNS_9IPAddressE + __ZN3rtc19IPAddressPrecedenceERKNS_9IPAddressE + __ZNK3rtc16InterfaceAddresseqERKS0_ + __ZNK3rtc16InterfaceAddress8ToStringEv +__ZN8mkvmuxer11SegmentInfo15set_writing_appEPKc +__ZN8mkvmuxer11SegmentInfo4InitEv +__ZN8mkvmuxer7Segment10OutputCuesEb @@ -1777,7 +1778,7 @@ index fca61ffe9f0563d87b364e0fa681ceab1d7bb26f..5c0d72f6c0bab0bc0011a123302c5654 +_vpx_codec_version_str +_vpx_codec_vp8_cx diff --git a/Source/ThirdParty/libwebrtc/Source/webrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/Source/ThirdParty/libwebrtc/Source/webrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc -index 625cb7fefc6a699d9e2f28c6acc1bc7681ea3984..cd6677a7ffd3321978427a9fc6fbed5179aebb60 100644 +index 9ada3cdc5f6ceecabdc4b17998754a7bf3adb0e9..1136def8438ec98a8f96bfd67fd9b0691bb3ffaf 100644 --- a/Source/ThirdParty/libwebrtc/Source/webrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc +++ b/Source/ThirdParty/libwebrtc/Source/webrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -16,6 +16,7 @@ @@ -1801,7 +1802,7 @@ index f95c3b6c6b73a01974f26d88bcc533e5032ddb66..6a9368c60824cd32649c93286522d779 #include "api/array_view.h" diff --git a/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj b/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj -index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47a131e4ee 100644 +index 4450f961579b52a88876cfc2d72a59bab4ccb863..4a217d81585ce495237aa013d3ace98a7bee394e 100644 --- a/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj +++ b/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj @@ -35,6 +35,20 @@ @@ -1825,7 +1826,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 /* Begin PBXBuildFile section */ 2D6BFF60280A93DF00A1A74F /* video_coding.h in Headers */ = {isa = PBXBuildFile; fileRef = 4131C45B234C81710028A615 /* video_coding.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D6BFF61280A93EC00A1A74F /* video_codec_initializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4131C45E234C81720028A615 /* video_codec_initializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; -@@ -5193,6 +5207,9 @@ +@@ -5144,6 +5158,9 @@ DDF30D9127C5C725006A526F /* receive_side_congestion_controller.h in Headers */ = {isa = PBXBuildFile; fileRef = DDF30D9027C5C725006A526F /* receive_side_congestion_controller.h */; }; DDF30D9527C5C756006A526F /* bwe_defines.h in Headers */ = {isa = PBXBuildFile; fileRef = DDF30D9327C5C756006A526F /* bwe_defines.h */; }; DDF30D9627C5C756006A526F /* remote_bitrate_estimator.h in Headers */ = {isa = PBXBuildFile; fileRef = DDF30D9427C5C756006A526F /* remote_bitrate_estimator.h */; }; @@ -1835,7 +1836,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ -@@ -5681,6 +5698,13 @@ +@@ -5632,6 +5649,13 @@ remoteGlobalIDString = DDF30D0527C5C003006A526F; remoteInfo = absl; }; @@ -1849,7 +1850,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ -@@ -11465,6 +11489,9 @@ +@@ -11217,6 +11241,9 @@ DDF30D9027C5C725006A526F /* receive_side_congestion_controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = receive_side_congestion_controller.h; sourceTree = ""; }; DDF30D9327C5C756006A526F /* bwe_defines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bwe_defines.h; sourceTree = ""; }; DDF30D9427C5C756006A526F /* remote_bitrate_estimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = remote_bitrate_estimator.h; sourceTree = ""; }; @@ -1859,7 +1860,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 FB39D0D11200F0E300088E69 /* libwebrtc.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libwebrtc.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ -@@ -20596,6 +20623,7 @@ +@@ -20069,6 +20096,7 @@ isa = PBXGroup; children = ( CDFD2F9224C4B2F90048DAC3 /* common */, @@ -1867,7 +1868,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 CDEBB19224C0191800ADBD44 /* webm_parser */, ); path = libwebm; -@@ -21007,6 +21035,16 @@ +@@ -20480,6 +20508,16 @@ path = include; sourceTree = ""; }; @@ -1884,7 +1885,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 FB39D06E1200ED9200088E69 = { isa = PBXGroup; children = ( -@@ -24293,6 +24331,7 @@ +@@ -23772,6 +23810,7 @@ ); dependencies = ( 410B3827292B73E90003E515 /* PBXTargetDependency */, @@ -1892,7 +1893,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 DD2E76E827C6B69A00F2A74C /* PBXTargetDependency */, CDEBB4CC24C01AB400ADBD44 /* PBXTargetDependency */, 411ED040212E0811004320BA /* PBXTargetDependency */, -@@ -24375,6 +24414,7 @@ +@@ -23854,6 +23893,7 @@ 4460B8B92B155B6A00392062 /* vp9_qp_parser_fuzzer */, 444A6EF02AEADFC9005FE121 /* vp9_replay_fuzzer */, 44945C512B9BA1C300447FFD /* webm_fuzzer */, @@ -1900,7 +1901,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 ); }; /* End PBXProject section */ -@@ -24458,6 +24498,23 @@ +@@ -23937,6 +23977,23 @@ shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Scripts/create-symlink-to-altroot.sh\"\n"; }; @@ -1924,7 +1925,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ -@@ -26471,6 +26528,9 @@ +@@ -25924,6 +25981,9 @@ 5CDD865E1E43B8B500621E92 /* min_max_operations.c in Sources */, 4189395B242A71F5007FDC41 /* min_video_bitrate_experiment.cc in Sources */, 41B8D8FB28CB85CB00E5FA37 /* missing_mandatory_parameter_cause.cc in Sources */, @@ -1934,7 +1935,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 4131C387234B957D0028A615 /* moving_average.cc in Sources */, 41FCBB1521B1F7AA00A5DF27 /* moving_average.cc in Sources */, 5CD286101E6A64C90094FDC8 /* moving_max.cc in Sources */, -@@ -27372,6 +27432,11 @@ +@@ -26796,6 +26856,11 @@ target = DDF30D0527C5C003006A526F /* absl */; targetProxy = DD2E76E727C6B69A00F2A74C /* PBXContainerItemProxy */; }; @@ -1946,7 +1947,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ -@@ -27947,6 +28012,27 @@ +@@ -27371,6 +27436,27 @@ }; name = Production; }; @@ -1974,7 +1975,7 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 FB39D0711200ED9200088E69 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5D7C59C71208C68B001C873E /* DebugRelease.xcconfig */; -@@ -28249,6 +28335,16 @@ +@@ -27673,6 +27759,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Production; }; @@ -1992,10 +1993,10 @@ index 5e185d2c3aae7eb99b72dbbb1a04af638a6dfc63..d2fbb40ee5d8349d617b287368ae1a47 isa = XCConfigurationList; buildConfigurations = ( diff --git a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml -index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba045b1f4aec 100644 +index adfe9cf8a01ce98f1a7cee8c77ff35f697e60f2e..e663c77ee07785a3b74a8f2eead876dd483d0d28 100644 --- a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml +++ b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml -@@ -563,6 +563,7 @@ ApplePayEnabled: +@@ -575,6 +575,7 @@ ApplePayEnabled: default: false # FIXME: This is on by default in WebKit2 PLATFORM(COCOA). Perhaps we should consider turning it on for WebKitLegacy as well. @@ -2003,7 +2004,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 AsyncClipboardAPIEnabled: type: bool status: mature -@@ -573,7 +574,7 @@ AsyncClipboardAPIEnabled: +@@ -585,7 +586,7 @@ AsyncClipboardAPIEnabled: default: false WebKit: "PLATFORM(COCOA) || PLATFORM(GTK)" : true @@ -2012,7 +2013,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 WebCore: default: false -@@ -1810,6 +1811,7 @@ CrossOriginEmbedderPolicyEnabled: +@@ -1893,6 +1894,7 @@ CrossOriginEmbedderPolicyEnabled: WebCore: default: false @@ -2020,7 +2021,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 CrossOriginOpenerPolicyEnabled: type: bool status: stable -@@ -1864,7 +1866,7 @@ CustomPasteboardDataEnabled: +@@ -1947,7 +1949,7 @@ CustomPasteboardDataEnabled: WebKitLegacy: default: false WebKit: @@ -2029,7 +2030,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 default: false CustomStateSetEnabled: -@@ -1923,6 +1925,7 @@ DOMAudioSessionFullEnabled: +@@ -2006,6 +2008,7 @@ DOMAudioSessionFullEnabled: WebCore: default: false @@ -2037,7 +2038,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 DOMPasteAccessRequestsEnabled: type: bool status: internal -@@ -1934,7 +1937,7 @@ DOMPasteAccessRequestsEnabled: +@@ -2017,7 +2020,7 @@ DOMPasteAccessRequestsEnabled: default: false WebKit: "PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(VISION)": true @@ -2046,7 +2047,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 WebCore: default: false -@@ -2278,7 +2281,7 @@ DirectoryUploadEnabled: +@@ -2361,7 +2364,7 @@ DirectoryUploadEnabled: WebKitLegacy: default: false WebKit: @@ -2055,7 +2056,20 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 default: false WebCore: default: false -@@ -3304,6 +3307,7 @@ InspectorAttachmentSide: +@@ -2783,10 +2786,10 @@ FullScreenEnabled: + WebKitLegacy: + default: false + WebKit: +- "PLATFORM(GTK) || PLATFORM(WPE)": true ++ "PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(WPE)": true + default: false + WebCore: +- "PLATFORM(GTK) || PLATFORM(WPE)": true ++ "PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(WPE)": true + default: false + sharedPreferenceForWebProcess: true + +@@ -3423,6 +3426,7 @@ InspectorAttachmentSide: WebKit: default: 0 @@ -2063,7 +2077,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 InspectorStartsAttached: type: bool status: embedder -@@ -3311,7 +3315,7 @@ InspectorStartsAttached: +@@ -3430,7 +3434,7 @@ InspectorStartsAttached: exposed: [ WebKit ] defaultValue: WebKit: @@ -2072,7 +2086,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 InspectorWindowFrame: type: String -@@ -3650,9 +3654,10 @@ LayoutViewportHeightExpansionFactor: +@@ -3781,9 +3785,10 @@ LayoutViewportHeightExpansionFactor: WebCore: default: 0 @@ -2084,7 +2098,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 category: html humanReadableName: "Lazy iframe loading" humanReadableDescription: "Enable lazy iframe loading support" -@@ -3660,9 +3665,9 @@ LazyIframeLoadingEnabled: +@@ -3791,9 +3796,9 @@ LazyIframeLoadingEnabled: WebKitLegacy: default: true WebKit: @@ -2096,7 +2110,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 LazyImageLoadingEnabled: type: bool -@@ -5084,7 +5089,7 @@ PermissionsAPIEnabled: +@@ -5237,7 +5242,7 @@ PermissionsAPIEnabled: WebKitLegacy: default: false WebKit: @@ -2105,7 +2119,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 default: false WebCore: default: false -@@ -5143,6 +5148,19 @@ PitchCorrectionAlgorithm: +@@ -5297,6 +5302,19 @@ PitchCorrectionAlgorithm: WebCore: default: MediaPlayerEnums::PitchCorrectionAlgorithm::BestAllAround @@ -2125,7 +2139,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 PointerLockOptionsEnabled: type: bool status: testable -@@ -5697,7 +5715,7 @@ ScreenOrientationAPIEnabled: +@@ -5853,7 +5871,7 @@ ScreenOrientationAPIEnabled: WebKitLegacy: default: false WebKit: @@ -2134,15 +2148,15 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 WebCore: default: false -@@ -6946,6 +6964,7 @@ UseCGDisplayListsForDOMRendering: - WebKit: +@@ -7110,6 +7128,7 @@ UseCGDisplayListsForDOMRendering: default: true + sharedPreferenceForWebProcess: true +# Playwright: force-disable on Windows. UseGPUProcessForCanvasRenderingEnabled: type: bool status: stable -@@ -6958,7 +6977,7 @@ UseGPUProcessForCanvasRenderingEnabled: +@@ -7122,7 +7141,7 @@ UseGPUProcessForCanvasRenderingEnabled: defaultValue: WebKit: "ENABLE(GPU_PROCESS_BY_DEFAULT)": true @@ -2151,16 +2165,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 default: false UseGPUProcessForDOMRenderingEnabled: -@@ -6968,7 +6987,7 @@ UseGPUProcessForDOMRenderingEnabled: - humanReadableName: "GPU Process: DOM Rendering" - humanReadableDescription: "Enable DOM rendering in GPU Process" - webcoreBinding: none -- condition: ENABLE(GPU_PROCESS) -+ condition: ENABLE(GPU_PROCESS) && !PLATFORM(WIN) - exposed: [ WebKit ] - defaultValue: - WebKit: -@@ -7000,6 +7019,7 @@ UseGPUProcessForMediaEnabled: +@@ -7165,6 +7184,7 @@ UseGPUProcessForMediaEnabled: "ENABLE(GPU_PROCESS_BY_DEFAULT)": true default: false @@ -2168,7 +2173,7 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 UseGPUProcessForWebGLEnabled: type: bool status: internal -@@ -7011,7 +7031,7 @@ UseGPUProcessForWebGLEnabled: +@@ -7176,7 +7196,7 @@ UseGPUProcessForWebGLEnabled: default: false WebKit: "ENABLE(GPU_PROCESS_BY_DEFAULT) && ENABLE(GPU_PROCESS_WEBGL_BY_DEFAULT)": true @@ -2178,10 +2183,10 @@ index 4d6f1829d52f1a0ea19c1a0de58e86304360c177..59a7410430ecc8db82f5b0bfcc11ba04 WebCore: "ENABLE(GPU_PROCESS_BY_DEFAULT) && ENABLE(GPU_PROCESS_WEBGL_BY_DEFAULT)": true diff --git a/Source/WTF/wtf/PlatformEnable.h b/Source/WTF/wtf/PlatformEnable.h -index 48c40a6bfa8ae0a275bbd8f3ae62f4701916fa48..a32e26e01d83999c575649a9fc0d96c71b655259 100644 +index 8d8c9fd0544f63fefcac20756addcb2933e5ab1a..322f0a70eb3154cb130b27f38cfb017acec1bf34 100644 --- a/Source/WTF/wtf/PlatformEnable.h +++ b/Source/WTF/wtf/PlatformEnable.h -@@ -401,7 +401,7 @@ +@@ -405,7 +405,7 @@ // ORIENTATION_EVENTS should never get enabled on Desktop, only Mobile. #if !defined(ENABLE_ORIENTATION_EVENTS) @@ -2190,7 +2195,7 @@ index 48c40a6bfa8ae0a275bbd8f3ae62f4701916fa48..a32e26e01d83999c575649a9fc0d96c7 #endif #if !defined(ENABLE_OVERFLOW_SCROLLING_TOUCH) -@@ -506,7 +506,7 @@ +@@ -510,7 +510,7 @@ #endif #if !defined(ENABLE_TOUCH_EVENTS) @@ -2200,10 +2205,10 @@ index 48c40a6bfa8ae0a275bbd8f3ae62f4701916fa48..a32e26e01d83999c575649a9fc0d96c7 #if !defined(ENABLE_TOUCH_ACTION_REGIONS) diff --git a/Source/WTF/wtf/PlatformEnableCocoa.h b/Source/WTF/wtf/PlatformEnableCocoa.h -index 5fef3978fdfb0dc92609688fdf282ea26a0859ef..70190e180003951e48c4084a1788f504730171ca 100644 +index 93ed6901c29ee2e814efa89f3ec96594f6c8c662..48aaaa90e26e3660253ff833d2ae50272d6c9a2c 100644 --- a/Source/WTF/wtf/PlatformEnableCocoa.h +++ b/Source/WTF/wtf/PlatformEnableCocoa.h -@@ -781,7 +781,7 @@ +@@ -787,7 +787,7 @@ #endif #if !defined(ENABLE_SEC_ITEM_SHIM) @@ -2213,10 +2218,19 @@ index 5fef3978fdfb0dc92609688fdf282ea26a0859ef..70190e180003951e48c4084a1788f504 #if !defined(ENABLE_SERVER_PRECONNECT) diff --git a/Source/WTF/wtf/PlatformHave.h b/Source/WTF/wtf/PlatformHave.h -index 33d00e530b23f32e279214a88379041ec2a7c3b7..0d7b3f08bef8f010d96d85df5d1672dac87054ec 100644 +index b2a6395bbdc9e2877e28b7585f051ddebfbd137d..7ce9ce53d19a7be51018e90eb6b57e409de570a0 100644 --- a/Source/WTF/wtf/PlatformHave.h +++ b/Source/WTF/wtf/PlatformHave.h -@@ -425,7 +425,7 @@ +@@ -278,7 +278,7 @@ + #define HAVE_FLOAT16_INSTRUCTION 1 + #endif + +-#if ((CPU(ARM64) || (CPU(X86_64) && !PLATFORM(PLAYSTATION))) && defined(__FLT16_MANT_DIG__)) ++#if PLATFORM(COCOA) && (CPU(ARM64) || CPU(X86_64)) + #define HAVE_FLOAT16 1 + #endif + +@@ -433,7 +433,7 @@ #define HAVE_FOUNDATION_WITH_SAME_SITE_COOKIE_SUPPORT 1 #endif @@ -2225,7 +2239,7 @@ index 33d00e530b23f32e279214a88379041ec2a7c3b7..0d7b3f08bef8f010d96d85df5d1672da #define HAVE_OS_DARK_MODE_SUPPORT 1 #endif -@@ -1248,7 +1248,8 @@ +@@ -1260,7 +1260,8 @@ #endif #if PLATFORM(MAC) @@ -2235,6 +2249,41 @@ index 33d00e530b23f32e279214a88379041ec2a7c3b7..0d7b3f08bef8f010d96d85df5d1672da #endif #if !defined(HAVE_LOCKDOWN_MODE_PDF_ADDITIONS) && \ +diff --git a/Source/WTF/wtf/StdLibExtras.h b/Source/WTF/wtf/StdLibExtras.h +index f99e08e4982e87c54ee0cbd43f0a43489366f2cd..89f264d11a853fef42dbd6310510e8004c65e88e 100644 +--- a/Source/WTF/wtf/StdLibExtras.h ++++ b/Source/WTF/wtf/StdLibExtras.h +@@ -26,6 +26,7 @@ + + #pragma once + ++#include + #include + #include + #include +@@ -39,6 +40,22 @@ + #include + #include + ++// FIXME: Custom implementation not needed once all Linux systems use >libstdc++-10. ++#if !defined(__cpp_lib_bit_cast) || __cpp_lib_bit_cast < 201806L ++namespace std { ++ ++template ::value ++ && std::is_trivially_copyable::value, ++ int>::type = 0> ++inline constexpr T bit_cast(const U &value) { ++ return __builtin_bit_cast(T, value); ++} ++ ++} ++#endif ++ + // Use this macro to declare and define a debug-only global variable that may have a + // non-trivial constructor and destructor. When building with clang, this will suppress + // warnings about global constructors and exit-time destructors. diff --git a/Source/WTF/wtf/unicode/UTF8Conversion.h b/Source/WTF/wtf/unicode/UTF8Conversion.h index 007b8fe3292f326504013be8198ae020f7aacf35..4439f901b4a9a92d881c7cee24ad9cd28149d276 100644 --- a/Source/WTF/wtf/unicode/UTF8Conversion.h @@ -2251,10 +2300,10 @@ index 007b8fe3292f326504013be8198ae020f7aacf35..4439f901b4a9a92d881c7cee24ad9cd2 namespace Unicode { diff --git a/Source/WebCore/DerivedSources.make b/Source/WebCore/DerivedSources.make -index 3c93bf646040ac804afc9c98b855829e98b87269..366b9c6714aa4f378a156577bd932a31ee5737a6 100644 +index 10d994c5a2cdcca44e4eccab4f308bfc5ac31eba..8b977dba42a481a70e36e72f068d904e05324e3e 100644 --- a/Source/WebCore/DerivedSources.make +++ b/Source/WebCore/DerivedSources.make -@@ -1156,6 +1156,10 @@ JS_BINDING_IDLS := \ +@@ -1159,6 +1159,10 @@ JS_BINDING_IDLS := \ $(WebCore)/dom/SubscriberCallback.idl \ $(WebCore)/dom/SubscriptionObserver.idl \ $(WebCore)/dom/SubscriptionObserverCallback.idl \ @@ -2265,7 +2314,7 @@ index 3c93bf646040ac804afc9c98b855829e98b87269..366b9c6714aa4f378a156577bd932a31 $(WebCore)/dom/Text.idl \ $(WebCore)/dom/TextDecoder.idl \ $(WebCore)/dom/TextDecoderStream.idl \ -@@ -1745,9 +1749,6 @@ JS_BINDING_IDLS := \ +@@ -1749,9 +1753,6 @@ JS_BINDING_IDLS := \ ADDITIONAL_BINDING_IDLS = \ DocumentTouch.idl \ GestureEvent.idl \ @@ -2327,7 +2376,7 @@ index 506ebb25fa290f27a75674a6fe5506fc311910d6..07d34c567b42aca08b188243c3f036f6 [self sendSpeechEndIfNeeded]; diff --git a/Source/WebCore/PlatformWPE.cmake b/Source/WebCore/PlatformWPE.cmake -index c6a03b56d8358316c9ce422c1a11438bd216f80f..69fbd319b7cd084ca125a8db1b5d92ef6a4dc10f 100644 +index 34db6a238244d9e0b0ab3478b449cdcfa4993169..672de4d1c7430952ff200725a5a72986cd7aa5cb 100644 --- a/Source/WebCore/PlatformWPE.cmake +++ b/Source/WebCore/PlatformWPE.cmake @@ -60,6 +60,7 @@ list(APPEND WebCore_PRIVATE_FRAMEWORK_HEADERS @@ -2339,10 +2388,10 @@ index c6a03b56d8358316c9ce422c1a11438bd216f80f..69fbd319b7cd084ca125a8db1b5d92ef set(CSS_VALUE_PLATFORM_DEFINES "HAVE_OS_DARK_MODE_SUPPORT=1") diff --git a/Source/WebCore/SourcesCocoa.txt b/Source/WebCore/SourcesCocoa.txt -index 8ca60190e2f9110b74e37350ae12de7ff324de0e..0ad2e7621561b0a0eb67dc39555a2b99169df9c2 100644 +index c5f7d089135e35c382700110a7f120cfe39d3e28..8ccc00a48d9b5622ccbd8fc750159d82c655e8fb 100644 --- a/Source/WebCore/SourcesCocoa.txt +++ b/Source/WebCore/SourcesCocoa.txt -@@ -716,3 +716,9 @@ testing/cocoa/WebViewVisualIdentificationOverlay.mm +@@ -713,3 +713,9 @@ testing/cocoa/WebViewVisualIdentificationOverlay.mm platform/graphics/angle/GraphicsContextGLANGLE.cpp @no-unify platform/graphics/cocoa/GraphicsContextGLCocoa.mm @no-unify platform/graphics/cv/GraphicsContextGLCVCocoa.cpp @no-unify @@ -2353,10 +2402,10 @@ index 8ca60190e2f9110b74e37350ae12de7ff324de0e..0ad2e7621561b0a0eb67dc39555a2b99 +JSTouchList.cpp +// Playwright end diff --git a/Source/WebCore/SourcesGTK.txt b/Source/WebCore/SourcesGTK.txt -index af2081f34b9d8d97864a6e9507805abc9e8eb6d7..06ed467b2c6e529baba22a04e03a4858f8552e19 100644 +index 599d83f5c87ddf7a0b1d3b62e84cba13c4f98804..192231714aab59ca708092d62e580238b0aedbe0 100644 --- a/Source/WebCore/SourcesGTK.txt +++ b/Source/WebCore/SourcesGTK.txt -@@ -110,3 +110,10 @@ platform/unix/LoggingUnix.cpp +@@ -112,3 +112,10 @@ platform/unix/LoggingUnix.cpp platform/unix/SharedMemoryUnix.cpp platform/xdg/MIMETypeRegistryXdg.cpp @@ -2368,7 +2417,7 @@ index af2081f34b9d8d97864a6e9507805abc9e8eb6d7..06ed467b2c6e529baba22a04e03a4858 +JSSpeechSynthesisEventInit.cpp +// Playwright: end. diff --git a/Source/WebCore/SourcesWPE.txt b/Source/WebCore/SourcesWPE.txt -index 92f1879df295fc63a9194dc54d3f7499c5fe3041..67c40d056aee6a8149ed1ff16ce4c835e19f7f6c 100644 +index 9508e6bb5bad8463ce2122a51612c53af47f5421..be18a3c31b7a4bfd184d4a35f2eeeb2eedd5af9b 100644 --- a/Source/WebCore/SourcesWPE.txt +++ b/Source/WebCore/SourcesWPE.txt @@ -46,6 +46,8 @@ editing/libwpe/EditorLibWPE.cpp @@ -2380,7 +2429,7 @@ index 92f1879df295fc63a9194dc54d3f7499c5fe3041..67c40d056aee6a8149ed1ff16ce4c835 page/linux/ResourceUsageOverlayLinux.cpp page/linux/ResourceUsageThreadLinux.cpp -@@ -87,6 +89,17 @@ platform/text/LocaleICU.cpp +@@ -89,6 +91,17 @@ platform/text/LocaleICU.cpp platform/unix/LoggingUnix.cpp platform/unix/SharedMemoryUnix.cpp @@ -2399,10 +2448,10 @@ index 92f1879df295fc63a9194dc54d3f7499c5fe3041..67c40d056aee6a8149ed1ff16ce4c835 +JSSpeechSynthesisEventInit.cpp +// Playwright: end. diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj -index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316aee41ffd 100644 +index 3ddce4b693ef515291c9873c87e9046df7b2b2e9..70fc70c56cdb71f3555819a0ad060cd7a26c4010 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj -@@ -6198,6 +6198,13 @@ +@@ -6199,6 +6199,13 @@ EDE3A5000C7A430600956A37 /* ColorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = EDE3A4FF0C7A430600956A37 /* ColorMac.h */; settings = {ATTRIBUTES = (Private, ); }; }; EDEC98030AED7E170059137F /* WebCorePrefix.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEC98020AED7E170059137F /* WebCorePrefix.h */; }; EFCC6C8F20FE914400A2321B /* CanvasActivityRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -2416,7 +2465,7 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 F12171F616A8CF0B000053CA /* WebVTTElement.h in Headers */ = {isa = PBXBuildFile; fileRef = F12171F416A8BC63000053CA /* WebVTTElement.h */; }; F32BDCD92363AACA0073B6AE /* UserGestureEmulationScope.h in Headers */ = {isa = PBXBuildFile; fileRef = F32BDCD72363AACA0073B6AE /* UserGestureEmulationScope.h */; }; F344C7141125B82C00F26EEE /* InspectorFrontendClient.h in Headers */ = {isa = PBXBuildFile; fileRef = F344C7121125B82C00F26EEE /* InspectorFrontendClient.h */; settings = {ATTRIBUTES = (Private, ); }; }; -@@ -20153,6 +20160,14 @@ +@@ -20164,6 +20171,14 @@ EDEC98020AED7E170059137F /* WebCorePrefix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = WebCorePrefix.h; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; EFB7287B2124C73D005C2558 /* CanvasActivityRecord.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CanvasActivityRecord.cpp; sourceTree = ""; }; EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CanvasActivityRecord.h; sourceTree = ""; }; @@ -2431,7 +2480,7 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 F12171F316A8BC63000053CA /* WebVTTElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebVTTElement.cpp; sourceTree = ""; }; F12171F416A8BC63000053CA /* WebVTTElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebVTTElement.h; sourceTree = ""; }; F32BDCD52363AAC90073B6AE /* UserGestureEmulationScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserGestureEmulationScope.cpp; sourceTree = ""; }; -@@ -27805,6 +27820,11 @@ +@@ -27819,6 +27834,11 @@ BC4A5324256055590028C592 /* TextDirectionSubmenuInclusionBehavior.h */, 2D4F96F11A1ECC240098BF88 /* TextIndicator.cpp */, 2D4F96F21A1ECC240098BF88 /* TextIndicator.h */, @@ -2443,7 +2492,7 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 F48570A42644C76D00C05F71 /* TranslationContextMenuInfo.h */, F4E1965F21F26E4E00285078 /* UndoItem.cpp */, 2ECDBAD521D8906300F00ECD /* UndoItem.h */, -@@ -34147,6 +34167,8 @@ +@@ -34164,6 +34184,8 @@ 29E4D8DF16B0940F00C84704 /* PlatformSpeechSynthesizer.h */, 1AD8F81A11CAB9E900E93E54 /* PlatformStrategies.cpp */, 1AD8F81911CAB9E900E93E54 /* PlatformStrategies.h */, @@ -2452,7 +2501,7 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 0FD7C21D23CE41E30096D102 /* PlatformWheelEvent.cpp */, 935C476A09AC4D4F00A6AAB4 /* PlatformWheelEvent.h */, F491A66A2A9FEFA300F96146 /* PlatformWheelEvent.serialization.in */, -@@ -36817,6 +36839,7 @@ +@@ -36844,6 +36866,7 @@ AD6E71AB1668899D00320C13 /* DocumentSharedObjectPool.h */, 6BDB5DC1227BD3B800919770 /* DocumentStorageAccess.cpp */, 6BDB5DC0227BD3B800919770 /* DocumentStorageAccess.h */, @@ -2460,7 +2509,7 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 7CE7FA5B1EF882300060C9D6 /* DocumentTouch.cpp */, 7CE7FA591EF882300060C9D6 /* DocumentTouch.h */, A8185F3209765765005826D9 /* DocumentType.cpp */, -@@ -41547,6 +41570,8 @@ +@@ -41585,6 +41608,8 @@ F4E90A3C2B52038E002DA469 /* PlatformTextAlternatives.h in Headers */, 0F7D07331884C56C00B4AF86 /* PlatformTextTrack.h in Headers */, 074E82BB18A69F0E007EF54C /* PlatformTimeRanges.h in Headers */, @@ -2469,7 +2518,7 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 CDD08ABD277E542600EA3755 /* PlatformTrackConfiguration.h in Headers */, CD1F9B022700323D00617EB6 /* PlatformVideoColorPrimaries.h in Headers */, CD1F9B01270020B700617EB6 /* PlatformVideoColorSpace.h in Headers */, -@@ -42826,6 +42851,7 @@ +@@ -42864,6 +42889,7 @@ 0F54DD081881D5F5003EEDBB /* Touch.h in Headers */, 71B7EE0D21B5C6870031C1EF /* TouchAction.h in Headers */, 0F54DD091881D5F5003EEDBB /* TouchEvent.h in Headers */, @@ -2477,7 +2526,7 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 0F54DD0A1881D5F5003EEDBB /* TouchList.h in Headers */, 070334D71459FFD5008D8D45 /* TrackBase.h in Headers */, BE88E0C21715CE2600658D98 /* TrackListBase.h in Headers */, -@@ -43980,6 +44006,8 @@ +@@ -44018,6 +44044,8 @@ 2D22830323A8470700364B7E /* CursorMac.mm in Sources */, 5CBD59592280E926002B22AA /* CustomHeaderFields.cpp in Sources */, 07E4BDBF2A3A5FAB000D5509 /* DictationCaretAnimator.cpp in Sources */, @@ -2486,7 +2535,7 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 7CE6CBFD187F394900D46BF5 /* FormatConverter.cpp in Sources */, 4667EA3E2968D9DA00BAB1E2 /* GameControllerHapticEffect.mm in Sources */, 46FE73D32968E52000B8064C /* GameControllerHapticEngines.mm in Sources */, -@@ -44068,6 +44096,9 @@ +@@ -44106,6 +44134,9 @@ CE88EE262414467B007F29C2 /* TextAlternativeWithRange.mm in Sources */, BE39137129B267F500FA5D4F /* TextTransformCocoa.cpp in Sources */, 51DF6D800B92A18E00C2DC85 /* ThreadCheck.mm in Sources */, @@ -2497,10 +2546,10 @@ index 2dd82a349b4c111364b9032a5e5a3d81e238798e..f18701e5aa5a6dd0bd2619a0cfb92316 538EC8021F96AF81004D22A8 /* UnifiedSource1.cpp in Sources */, 538EC8051F96AF81004D22A8 /* UnifiedSource2-mm.mm in Sources */, diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp -index a934d32a2b01e6273e1d97a7b8fd7e0999495dc5..379eee720dda27b29b32f26ab5a10d83ce78f4b9 100644 +index 2a4e9158e4bf30f6331402e3a205dfabfa068e9a..33187f427cf77e05f5424651880b9f20f3fb9645 100644 --- a/Source/WebCore/accessibility/AccessibilityObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityObject.cpp -@@ -67,6 +67,7 @@ +@@ -68,6 +68,7 @@ #include "HTMLSlotElement.h" #include "HTMLTextAreaElement.h" #include "HitTestResult.h" @@ -2508,7 +2557,7 @@ index a934d32a2b01e6273e1d97a7b8fd7e0999495dc5..379eee720dda27b29b32f26ab5a10d83 #include "LocalFrame.h" #include "LocalizedStrings.h" #include "MathMLNames.h" -@@ -3968,9 +3969,14 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const +@@ -3974,9 +3975,14 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const if (roleValue() == AccessibilityRole::ApplicationDialog) return AccessibilityObjectInclusion::IncludeObject; @@ -2548,10 +2597,10 @@ index 7641906564fb1e480f56923343a8ee6149f60820..ed530124becb719e66b211f468c24376 return textLength; } diff --git a/Source/WebCore/bindings/js/WebCoreBuiltinNames.h b/Source/WebCore/bindings/js/WebCoreBuiltinNames.h -index bec74918c24d17a88bb1583504d00e2a11a5e6c6..8c86fc1552c091e6751e87c60af728b4ce37f2a1 100644 +index 84431d6ef57495c781d1ac872c9118ecd6f0c9fd..38cf041f31cf54106706ae95d2ea47c88818b503 100644 --- a/Source/WebCore/bindings/js/WebCoreBuiltinNames.h +++ b/Source/WebCore/bindings/js/WebCoreBuiltinNames.h -@@ -183,6 +183,8 @@ namespace WebCore { +@@ -185,6 +185,8 @@ namespace WebCore { macro(DelayNode) \ macro(DeprecationReportBody) \ macro(DigitalCredential) \ @@ -2681,7 +2730,7 @@ index 9b344003de17b96d8b9ca8c7f32143a27543b1ea..2208a3f2b7d930bcd291e65b474d4c30 ] partial interface Element { // Returns Promise if PointerLockOptionsEnabled Runtime Flag is set, otherwise returns undefined. diff --git a/Source/WebCore/dom/PointerEvent.cpp b/Source/WebCore/dom/PointerEvent.cpp -index 204b5f08ba950ead5f7d853d3c7fc9274ce46a26..e2f117a2a3e221fc4ca14b02c82cda952f0cd63b 100644 +index af8212c75d96b7bbc638a865e0a0b5ab907a5140..579b4e77ea64f956aa1b08b754b842b6e6b5f207 100644 --- a/Source/WebCore/dom/PointerEvent.cpp +++ b/Source/WebCore/dom/PointerEvent.cpp @@ -27,9 +27,11 @@ @@ -2696,8 +2745,8 @@ index 204b5f08ba950ead5f7d853d3c7fc9274ce46a26..e2f117a2a3e221fc4ca14b02c82cda95 #include namespace WebCore { -@@ -133,4 +135,51 @@ Vector> PointerEvent::getCoalescedEvents() - return m_coalescedEvents; +@@ -232,4 +234,59 @@ void PointerEvent::receivedTarget() + predictedEvent->setTarget(this->target()); } +#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS_FAMILY) && !PLATFORM(WPE) @@ -2722,26 +2771,34 @@ index 204b5f08ba950ead5f7d853d3c7fc9274ce46a26..e2f117a2a3e221fc4ca14b02c82cda95 + return nullAtom(); +} + -+Ref PointerEvent::create(const PlatformTouchEvent& event, unsigned index, bool isPrimary, Ref&& view, const IntPoint& touchDelta) ++Ref PointerEvent::create(const PlatformTouchEvent& event, const Vector>& coalescedEvents, const Vector>& predictedEvents, unsigned touchIndex, bool isPrimary, Ref&& view, const IntPoint& touchDelta) +{ -+ const auto& type = pointerEventType(event.touchPoints().at(index).state()); -+ return adoptRef(*new PointerEvent(type, event, typeIsCancelable(type), index, isPrimary, WTFMove(view), touchDelta)); ++ const auto& type = pointerEventType(event.touchPoints().at(touchIndex).state()); ++ return adoptRef(*new PointerEvent(type, event, coalescedEvents, predictedEvents, typeCanBubble(type), typeIsCancelable(type), touchIndex, isPrimary, WTFMove(view), touchDelta)); +} + -+Ref PointerEvent::create(const AtomString& type, const PlatformTouchEvent& event, unsigned index, bool isPrimary, Ref&& view, const IntPoint& touchDelta) ++Ref PointerEvent::create(const PlatformTouchEvent& event, const Vector>& coalescedEvents, const Vector>& predictedEvents, CanBubble canBubble, IsCancelable isCancelable, unsigned touchIndex, bool isPrimary, Ref&& view, const IntPoint& touchDelta) +{ -+ return adoptRef(*new PointerEvent(type, event, typeIsCancelable(type), index, isPrimary, WTFMove(view), touchDelta)); ++ const auto& type = pointerEventType(event.touchPoints().at(touchIndex).state()); ++ return adoptRef(*new PointerEvent(type, event, coalescedEvents, predictedEvents, canBubble, isCancelable, touchIndex, isPrimary, WTFMove(view), touchDelta)); +} + -+PointerEvent::PointerEvent(const AtomString& type, const PlatformTouchEvent& event, IsCancelable isCancelable, unsigned index, bool isPrimary, Ref&& view, const IntPoint& touchDelta) -+ : MouseEvent(EventInterfaceType::PointerEvent, type, typeCanBubble(type), isCancelable, typeIsComposed(type), event.timestamp().approximateMonotonicTime(), WTFMove(view), 0, -+ event.touchPoints().at(index).pos(), event.touchPoints().at(index).pos(), touchDelta.x(), touchDelta.y(), event.modifiers(), buttonForType(type), buttonsForType(type), nullptr, 0, SyntheticClickType::NoTap, IsSimulated::No, IsTrusted::Yes) -+ , m_pointerId(event.touchPoints().at(index).id()) -+ , m_width(2 * event.touchPoints().at(index).radiusX()) -+ , m_height(2 * event.touchPoints().at(index).radiusY()) -+ , m_pressure(event.touchPoints().at(index).force()) ++Ref PointerEvent::create(const AtomString& type, const PlatformTouchEvent& event, const Vector>& coalescedEvents, const Vector>& predictedEvents, unsigned touchIndex, bool isPrimary, Ref&& view, const IntPoint& touchDelta) ++{ ++ return adoptRef(*new PointerEvent(type, event, coalescedEvents, predictedEvents, typeCanBubble(type), typeIsCancelable(type), touchIndex, isPrimary, WTFMove(view), touchDelta)); ++} ++ ++PointerEvent::PointerEvent(const AtomString& type, const PlatformTouchEvent& event, const Vector>& coalescedEvents, const Vector>& predictedEvents, CanBubble canBubble, IsCancelable isCancelable, unsigned touchIndex, bool isPrimary, Ref&& view, const IntPoint& touchDelta) ++ : MouseEvent(EventInterfaceType::PointerEvent, type, canBubble, isCancelable, typeIsComposed(type), event.timestamp().approximateMonotonicTime(), WTFMove(view), 0, ++ event.touchPoints().at(touchIndex).pos(), event.touchPoints().at(touchIndex).pos(), touchDelta.x(), touchDelta.y(), event.modifiers(), buttonForType(type), buttonsForType(type), nullptr, 0, SyntheticClickType::NoTap, { }, { }, IsSimulated::No, IsTrusted::Yes) ++ , m_pointerId(event.touchPoints().at(touchIndex).id()) ++ , m_width(2 * event.touchPoints().at(touchIndex).radiusX()) ++ , m_height(2 * event.touchPoints().at(touchIndex).radiusY()) ++ , m_pressure(event.touchPoints().at(touchIndex).force()) + , m_pointerType(touchPointerEventType()) + , m_isPrimary(isPrimary) ++ , m_coalescedEvents(coalescedEvents) ++ , m_predictedEvents(predictedEvents) +{ +} + @@ -2749,7 +2806,7 @@ index 204b5f08ba950ead5f7d853d3c7fc9274ce46a26..e2f117a2a3e221fc4ca14b02c82cda95 + } // namespace WebCore diff --git a/Source/WebCore/dom/PointerEvent.h b/Source/WebCore/dom/PointerEvent.h -index c54bbf8060253b9000f3da9be8ff327a2625ff86..6b05fc5f61444ea9dc7775491801c585ae44045e 100644 +index f168aae7c04ef261961ec58b98c20086209370d0..3127dca1b62429bde1db5c3d0e613af1b5318ffe 100644 --- a/Source/WebCore/dom/PointerEvent.h +++ b/Source/WebCore/dom/PointerEvent.h @@ -34,6 +34,8 @@ @@ -2761,22 +2818,22 @@ index c54bbf8060253b9000f3da9be8ff327a2625ff86..6b05fc5f61444ea9dc7775491801c585 #endif #if ENABLE(TOUCH_EVENTS) && PLATFORM(WPE) -@@ -88,7 +90,7 @@ public: +@@ -90,7 +92,7 @@ public: static Ref create(const AtomString& type, MouseButton, const MouseEvent&, PointerID, const String& pointerType, CanBubble, IsCancelable); static Ref create(const AtomString& type, PointerID, const String& pointerType, IsPrimary = IsPrimary::No); -#if ENABLE(TOUCH_EVENTS) && (PLATFORM(IOS_FAMILY) || PLATFORM(WPE)) +#if ENABLE(TOUCH_EVENTS) - static Ref create(const PlatformTouchEvent&, unsigned touchIndex, bool isPrimary, Ref&&, const IntPoint& touchDelta = { }); - static Ref create(const AtomString& type, const PlatformTouchEvent&, unsigned touchIndex, bool isPrimary, Ref&&, const IntPoint& touchDelta = { }); - #endif -@@ -144,7 +146,7 @@ private: - PointerEvent(const AtomString&, Init&&); + static Ref create(const PlatformTouchEvent&, const Vector>& coalescedEvents, const Vector>& predictedEvents, unsigned touchIndex, bool isPrimary, Ref&&, const IntPoint& touchDelta = { }); + static Ref create(const PlatformTouchEvent&, const Vector>& coalescedEvents, const Vector>& predictedEvents, CanBubble, IsCancelable, unsigned touchIndex, bool isPrimary, Ref&& view, const IntPoint& touchDelta = { }); + static Ref create(const AtomString& type, const PlatformTouchEvent&, const Vector>& coalescedEvents, const Vector>& predictedEvents, unsigned touchIndex, bool isPrimary, Ref&&, const IntPoint& touchDelta = { }); +@@ -151,7 +153,7 @@ private: + PointerEvent(const AtomString&, Init&&, IsTrusted); PointerEvent(const AtomString& type, MouseButton, const MouseEvent&, PointerID, const String& pointerType, CanBubble, IsCancelable); PointerEvent(const AtomString& type, PointerID, const String& pointerType, IsPrimary); -#if ENABLE(TOUCH_EVENTS) && (PLATFORM(IOS_FAMILY) || PLATFORM(WPE)) +#if ENABLE(TOUCH_EVENTS) - PointerEvent(const AtomString& type, const PlatformTouchEvent&, IsCancelable isCancelable, unsigned touchIndex, bool isPrimary, Ref&&, const IntPoint& touchDelta = { }); + PointerEvent(const AtomString& type, const PlatformTouchEvent&, const Vector>& coalescedEvents, const Vector>& predictedEvents, CanBubble canBubble, IsCancelable isCancelable, unsigned touchIndex, bool isPrimary, Ref&&, const IntPoint& touchDelta = { }); #endif diff --git a/Source/WebCore/editing/libwpe/EditorLibWPE.cpp b/Source/WebCore/editing/libwpe/EditorLibWPE.cpp @@ -5388,7 +5445,7 @@ index 2ca6ee01a341eefead66a92e2af77875263a9df3..131bbd8c268a748b43cac105370d7b73 protected: static SameSiteInfo sameSiteInfo(const Document&, IsForDOMCookieAccess = IsForDOMCookieAccess::No); diff --git a/Source/WebCore/loader/DocumentLoader.cpp b/Source/WebCore/loader/DocumentLoader.cpp -index 22b83991d32085991eb502728333dc7648ab883f..6bd27284c1799448f387338738c7a7fbc8cc3690 100644 +index 255bc13d586402e96890a3d756cef8467da21dce..d1793d8491e191e3ea76674a3f7e0bbc7411b1aa 100644 --- a/Source/WebCore/loader/DocumentLoader.cpp +++ b/Source/WebCore/loader/DocumentLoader.cpp @@ -766,8 +766,10 @@ void DocumentLoader::willSendRequest(ResourceRequest&& newRequest, const Resourc @@ -5423,10 +5480,10 @@ index 22b83991d32085991eb502728333dc7648ab883f..6bd27284c1799448f387338738c7a7fb { ASSERT(navigationID); diff --git a/Source/WebCore/loader/DocumentLoader.h b/Source/WebCore/loader/DocumentLoader.h -index 3f7d86c6ba98d5d5a6ad716bd3d78885bb9411e0..94dbc77f454b70733d7f15db8fac00ac72fb8657 100644 +index 90f993d39598e45732849d4eaa400c049f8f81d9..cf8244e052afad8830880bd998aeaed04d5157aa 100644 --- a/Source/WebCore/loader/DocumentLoader.h +++ b/Source/WebCore/loader/DocumentLoader.h -@@ -207,6 +207,8 @@ public: +@@ -208,6 +208,8 @@ public: WEBCORE_EXPORT virtual void detachFromFrame(LoadWillContinueInAnotherProcess); @@ -5436,10 +5493,10 @@ index 3f7d86c6ba98d5d5a6ad716bd3d78885bb9411e0..94dbc77f454b70733d7f15db8fac00ac CheckedPtr checkedFrameLoader() const; WEBCORE_EXPORT SubresourceLoader* mainResourceLoader() const; diff --git a/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp -index 231a764c5b88f9986e324587e62f94abd119fff8..260b5368d67f75616e7aeba7c3170fb52af5f054 100644 +index 67f6448abad80e7cbb08efd34cf8b67637a1bc35..2b5f6fab94e2f917c84cb9a37a3d9a7fc8b79aaf 100644 --- a/Source/WebCore/loader/FrameLoader.cpp +++ b/Source/WebCore/loader/FrameLoader.cpp -@@ -1322,6 +1322,7 @@ void FrameLoader::loadInSameDocument(URL url, RefPtr stat +@@ -1297,6 +1297,7 @@ void FrameLoader::loadInSameDocument(URL url, RefPtr stat } m_client->dispatchDidNavigateWithinPage(); @@ -5447,7 +5504,7 @@ index 231a764c5b88f9986e324587e62f94abd119fff8..260b5368d67f75616e7aeba7c3170fb5 document->statePopped(stateObject ? stateObject.releaseNonNull() : SerializedScriptValue::nullValue()); m_client->dispatchDidPopStateWithinPage(); -@@ -1821,6 +1822,7 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t +@@ -1798,6 +1799,7 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t const String& httpMethod = loader->request().httpMethod(); if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, policyChecker().loadType(), newURL)) { @@ -5455,7 +5512,7 @@ index 231a764c5b88f9986e324587e62f94abd119fff8..260b5368d67f75616e7aeba7c3170fb5 RefPtr oldDocumentLoader = m_documentLoader; NavigationAction action { frame->protectedDocument().releaseNonNull(), loader->request(), InitiatedByMainFrame::Unknown, loader->isRequestFromClientOrUserInput(), policyChecker().loadType(), isFormSubmission }; -@@ -1857,7 +1859,9 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t +@@ -1834,7 +1836,9 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t } RELEASE_ASSERT(!isBackForwardLoadType(policyChecker().loadType()) || frame->history().provisionalItem()); @@ -5465,7 +5522,7 @@ index 231a764c5b88f9986e324587e62f94abd119fff8..260b5368d67f75616e7aeba7c3170fb5 continueLoadAfterNavigationPolicy(request, RefPtr { weakFormState.get() }.get(), navigationPolicyDecision, allowNavigationToInvalidURL); completionHandler(); }, PolicyDecisionMode::Asynchronous); -@@ -3126,10 +3130,15 @@ String FrameLoader::userAgent(const URL& url) const +@@ -3111,10 +3115,15 @@ String FrameLoader::userAgent(const URL& url) const String FrameLoader::navigatorPlatform() const { @@ -5483,7 +5540,7 @@ index 231a764c5b88f9986e324587e62f94abd119fff8..260b5368d67f75616e7aeba7c3170fb5 } void FrameLoader::dispatchOnloadEvents() -@@ -3594,6 +3603,8 @@ void FrameLoader::receivedMainResourceError(const ResourceError& error, LoadWill +@@ -3579,6 +3588,8 @@ void FrameLoader::receivedMainResourceError(const ResourceError& error, LoadWill checkCompleted(); if (frame->page()) checkLoadComplete(loadWillContinueInAnotherProcess); @@ -5492,7 +5549,7 @@ index 231a764c5b88f9986e324587e62f94abd119fff8..260b5368d67f75616e7aeba7c3170fb5 } void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, const SecurityOrigin* requesterOrigin, bool shouldContinue, NavigationHistoryBehavior historyHandling) -@@ -4476,9 +4487,6 @@ String FrameLoader::referrer() const +@@ -4461,9 +4472,6 @@ String FrameLoader::referrer() const void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() { @@ -5502,7 +5559,7 @@ index 231a764c5b88f9986e324587e62f94abd119fff8..260b5368d67f75616e7aeba7c3170fb5 Vector> worlds; ScriptController::getAllWorlds(worlds); for (auto& world : worlds) -@@ -4488,13 +4496,12 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() +@@ -4473,13 +4481,12 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world) { Ref frame = m_frame.get(); @@ -5535,7 +5592,7 @@ index 91340dc21042f545592b442bc42dbceed06219b2..f3591fe333761b10a25ddaf4a4f8d721 virtual bool shouldPerformSecurityChecks() const { return false; } virtual bool havePerformedSecurityChecks(const ResourceResponse&) const { return false; } diff --git a/Source/WebCore/loader/NavigationScheduler.cpp b/Source/WebCore/loader/NavigationScheduler.cpp -index 9af8f29088f6ac3841fe68cd8629ef18a6ca5675..f28e41daee51b3d4fd6c185532c6acf941dd30ad 100644 +index 3eb4ad66cbbc66cd72b0d74dde61bc0c0508ba00..9456d74f591d8e32ad0b1f24be9172c6017ef317 100644 --- a/Source/WebCore/loader/NavigationScheduler.cpp +++ b/Source/WebCore/loader/NavigationScheduler.cpp @@ -703,7 +703,7 @@ void NavigationScheduler::startTimer() @@ -5582,10 +5639,10 @@ index b74c5258454b0df9f74aa8a5297674b733925685..b6c3999745368c7f7e2e6176bfca6dc0 void ProgressTracker::incrementProgress(ResourceLoaderIdentifier identifier, const ResourceResponse& response) diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.cpp b/Source/WebCore/loader/cache/CachedResourceLoader.cpp -index 0838ffc91ef3b9b3990bb05bb0c52dec1128f92e..6f536869db9c2aa0264d2e800bc9f3e7adb9a379 100644 +index e65b301a2dbe9a873e872a6904a7344b9a3efe08..a8d34d05fb3b8f3b118f71fd223b673ee975998c 100644 --- a/Source/WebCore/loader/cache/CachedResourceLoader.cpp +++ b/Source/WebCore/loader/cache/CachedResourceLoader.cpp -@@ -1089,8 +1089,11 @@ ResourceErrorOr> CachedResourceLoader::requ +@@ -1123,8 +1123,11 @@ ResourceErrorOr> CachedResourceLoader::requ request.updateReferrerPolicy(document() ? document()->referrerPolicy() : ReferrerPolicy::Default); @@ -5597,9 +5654,9 @@ index 0838ffc91ef3b9b3990bb05bb0c52dec1128f92e..6f536869db9c2aa0264d2e800bc9f3e7 + // request.setCachingPolicy(CachingPolicy::DisallowCaching); + } - Ref page = *frame->page(); - -@@ -1703,8 +1706,9 @@ Vector> CachedResourceLoader::allCachedSVGImages() const + if (RefPtr documentLoader = m_documentLoader.get()) { + bool madeHTTPS { request.resourceRequest().wasSchemeOptimisticallyUpgraded() }; +@@ -1741,8 +1744,9 @@ Vector> CachedResourceLoader::allCachedSVGImages() const ResourceErrorOr> CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest&& request) { @@ -5612,10 +5669,10 @@ index 0838ffc91ef3b9b3990bb05bb0c52dec1128f92e..6f536869db9c2aa0264d2e800bc9f3e7 ASSERT(m_document); if (request.charset().isEmpty() && m_document && (type == CachedResource::Type::Script || type == CachedResource::Type::CSSStyleSheet)) diff --git a/Source/WebCore/page/ChromeClient.h b/Source/WebCore/page/ChromeClient.h -index 1712a19473ab6db4db50a1d746846a091dce7417..ed27724b92ba34e0f2a543f398701f31a6764bc3 100644 +index 6344c5f7d24e89cfd1ae70adf75ee591f15e797f..37e166d8b121aed461ed5aec838d3b29fe7fca4a 100644 --- a/Source/WebCore/page/ChromeClient.h +++ b/Source/WebCore/page/ChromeClient.h -@@ -339,7 +339,7 @@ public: +@@ -342,7 +342,7 @@ public: #endif #if ENABLE(ORIENTATION_EVENTS) @@ -5625,7 +5682,7 @@ index 1712a19473ab6db4db50a1d746846a091dce7417..ed27724b92ba34e0f2a543f398701f31 #if ENABLE(INPUT_TYPE_COLOR) diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp -index 82f9dab5460c78f49a48adfb8c1fcaf19b0a0b9e..b3c8a054f7c6e0b9db2cdc50fd077c687ccdd62b 100644 +index 75ce051422ff276cf6ba5e699fb88c19c7474360..9eeeeb7aafc527392d5870e966c2d926848c9fdc 100644 --- a/Source/WebCore/page/EventHandler.cpp +++ b/Source/WebCore/page/EventHandler.cpp @@ -4346,6 +4346,12 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr @@ -5720,7 +5777,7 @@ index 5b365008debe6b8d5a95a572a4c2725b0a7a519d..2c6ad49a45a1759f446aced179c0c5a7 struct SnapshotOptions { diff --git a/Source/WebCore/page/History.cpp b/Source/WebCore/page/History.cpp -index 097302d7502b8a28db7c7ab8d778d17365b03533..4ab5ee0e71b2010ea5297e8d1d4ec03b9fd27097 100644 +index 22ef70201db214f4a7c9c6b4d1eef699e1d7f334..7ddfe9b9c890927cc6a7d19fe1b82c466c5fc835 100644 --- a/Source/WebCore/page/History.cpp +++ b/Source/WebCore/page/History.cpp @@ -32,6 +32,7 @@ @@ -5731,9 +5788,9 @@ index 097302d7502b8a28db7c7ab8d778d17365b03533..4ab5ee0e71b2010ea5297e8d1d4ec03b #include "LocalFrame.h" #include "LocalFrameLoaderClient.h" #include "Logging.h" -@@ -304,6 +305,8 @@ ExceptionOr History::stateObjectAdded(RefPtr&& data +@@ -303,6 +304,8 @@ ExceptionOr History::stateObjectAdded(RefPtr&& data + m_mostRecentStateObjectUsage = payloadSize; - auto historyBehavior = stateObjectType == StateObjectType::Replace ? NavigationHistoryBehavior::Replace : NavigationHistoryBehavior::Push; frame->loader().updateURLAndHistory(fullURL, WTFMove(data), historyBehavior); + InspectorInstrumentation::didNavigateWithinPage(*frame); + @@ -5741,7 +5798,7 @@ index 097302d7502b8a28db7c7ab8d778d17365b03533..4ab5ee0e71b2010ea5297e8d1d4ec03b } diff --git a/Source/WebCore/page/LocalFrame.cpp b/Source/WebCore/page/LocalFrame.cpp -index 119ae91c675f4bf1d03b9fc4878c9449416583b8..f0a10738508ca82499b474b8f221fdaf18ec3efc 100644 +index fd1f944368f0afe4cf52774ded2007505f7322a3..0942514d3b2e4d65b31c6a4ec7eb930972d15bab 100644 --- a/Source/WebCore/page/LocalFrame.cpp +++ b/Source/WebCore/page/LocalFrame.cpp @@ -40,6 +40,7 @@ @@ -6228,10 +6285,10 @@ index c2fc366ed759fb9e1d83821c4fff4b05a6b8fe35..d3eec8aa3129ae6567b1402968374015 ViewportArguments m_viewportArguments; diff --git a/Source/WebCore/page/Page.cpp b/Source/WebCore/page/Page.cpp -index 72ffbbfd28edb23a93c8c8fba76ab25a7f7b0f72..8d787339e6b570e210765bb1cddec6c4705c2a25 100644 +index ab89eb4e743e8e5aca46cdc0923eada1acc9f527..46dd2458db397a74402bcd4a14227bb8850126fa 100644 --- a/Source/WebCore/page/Page.cpp +++ b/Source/WebCore/page/Page.cpp -@@ -592,6 +592,45 @@ void Page::setOverrideViewportArguments(const std::optional& +@@ -605,6 +605,45 @@ void Page::setOverrideViewportArguments(const std::optional& document->updateViewportArguments(); } @@ -6277,7 +6334,7 @@ index 72ffbbfd28edb23a93c8c8fba76ab25a7f7b0f72..8d787339e6b570e210765bb1cddec6c4 ScrollingCoordinator* Page::scrollingCoordinator() { if (!m_scrollingCoordinator && m_settings->scrollingCoordinatorEnabled()) { -@@ -3873,6 +3912,26 @@ void Page::setUseDarkAppearanceOverride(std::optional valueOverride) +@@ -3912,6 +3951,26 @@ void Page::setUseDarkAppearanceOverride(std::optional valueOverride) #endif } @@ -6305,10 +6362,10 @@ index 72ffbbfd28edb23a93c8c8fba76ab25a7f7b0f72..8d787339e6b570e210765bb1cddec6c4 { if (insets == m_fullscreenInsets) diff --git a/Source/WebCore/page/Page.h b/Source/WebCore/page/Page.h -index 67a128b27cdd46115158ec20e7ce38dd56fde3d6..8ec6a45348412257db2b027f6f15027a21725466 100644 +index cbed2c94bb7c3c0b437369b6b36913760d2d07aa..d5d7f00f3d59b9d6043c340512ba4131185b94bd 100644 --- a/Source/WebCore/page/Page.h +++ b/Source/WebCore/page/Page.h -@@ -341,6 +341,9 @@ public: +@@ -346,6 +346,9 @@ public: const std::optional& overrideViewportArguments() const { return m_overrideViewportArguments; } WEBCORE_EXPORT void setOverrideViewportArguments(const std::optional&); @@ -6318,7 +6375,7 @@ index 67a128b27cdd46115158ec20e7ce38dd56fde3d6..8ec6a45348412257db2b027f6f15027a static void refreshPlugins(bool reload); WEBCORE_EXPORT PluginData& pluginData(); void clearPluginData(); -@@ -405,6 +408,10 @@ public: +@@ -412,6 +415,10 @@ public: #if ENABLE(DRAG_SUPPORT) DragController& dragController() { return m_dragController.get(); } const DragController& dragController() const { return m_dragController.get(); } @@ -6329,7 +6386,7 @@ index 67a128b27cdd46115158ec20e7ce38dd56fde3d6..8ec6a45348412257db2b027f6f15027a #endif FocusController& focusController() const { return *m_focusController; } WEBCORE_EXPORT CheckedRef checkedFocusController() const; -@@ -588,6 +595,10 @@ public: +@@ -595,6 +602,10 @@ public: WEBCORE_EXPORT void effectiveAppearanceDidChange(bool useDarkAppearance, bool useElevatedUserInterfaceLevel); bool defaultUseDarkAppearance() const { return m_useDarkAppearance; } void setUseDarkAppearanceOverride(std::optional); @@ -6340,7 +6397,7 @@ index 67a128b27cdd46115158ec20e7ce38dd56fde3d6..8ec6a45348412257db2b027f6f15027a #if ENABLE(TEXT_AUTOSIZING) float textAutosizingWidth() const { return m_textAutosizingWidth; } -@@ -1035,6 +1046,11 @@ public: +@@ -1043,6 +1054,11 @@ public: WEBCORE_EXPORT void setInteractionRegionsEnabled(bool); #endif @@ -6352,7 +6409,7 @@ index 67a128b27cdd46115158ec20e7ce38dd56fde3d6..8ec6a45348412257db2b027f6f15027a #if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS_FAMILY) DeviceOrientationUpdateProvider* deviceOrientationUpdateProvider() const { return m_deviceOrientationUpdateProvider.get(); } #endif -@@ -1248,6 +1264,9 @@ private: +@@ -1264,6 +1280,9 @@ private: #if ENABLE(DRAG_SUPPORT) UniqueRef m_dragController; @@ -6362,7 +6419,7 @@ index 67a128b27cdd46115158ec20e7ce38dd56fde3d6..8ec6a45348412257db2b027f6f15027a #endif std::unique_ptr m_focusController; #if ENABLE(CONTEXT_MENUS) -@@ -1327,6 +1346,8 @@ private: +@@ -1344,6 +1363,8 @@ private: bool m_useElevatedUserInterfaceLevel { false }; bool m_useDarkAppearance { false }; std::optional m_useDarkAppearanceOverride; @@ -6371,7 +6428,7 @@ index 67a128b27cdd46115158ec20e7ce38dd56fde3d6..8ec6a45348412257db2b027f6f15027a #if ENABLE(TEXT_AUTOSIZING) float m_textAutosizingWidth { 0 }; -@@ -1506,6 +1527,11 @@ private: +@@ -1524,6 +1545,11 @@ private: #endif std::optional m_overrideViewportArguments; @@ -6410,7 +6467,7 @@ index 6ee10d6ca68ad8ea93ae45adb0876c80953d3278..d4e14d54af6cd21fc5649d18dbc6af20 Ref protectedPage() const; diff --git a/Source/WebCore/page/PointerCaptureController.cpp b/Source/WebCore/page/PointerCaptureController.cpp -index 315c1bbcff9bdacc7e25eb91d8d4d2ede4d61a9a..8e918fe4a64a425814fd8d4b951bf48dda74fde8 100644 +index 2e85dc0e40c12857b9bac2f672a9b6332f3156df..0b64153cdf6db62f42e332f269acb399176bdb42 100644 --- a/Source/WebCore/page/PointerCaptureController.cpp +++ b/Source/WebCore/page/PointerCaptureController.cpp @@ -195,7 +195,7 @@ bool PointerCaptureController::preventsCompatibilityMouseEventsForIdentifier(Poi @@ -6422,7 +6479,7 @@ index 315c1bbcff9bdacc7e25eb91d8d4d2ede4d61a9a..8e918fe4a64a425814fd8d4b951bf48d static bool hierarchyHasCapturingEventListeners(Element* target, const AtomString& eventName) { for (RefPtr currentNode = target; currentNode; currentNode = currentNode->parentInComposedTree()) { -@@ -501,7 +501,7 @@ void PointerCaptureController::cancelPointer(PointerID pointerId, const IntPoint +@@ -513,7 +513,7 @@ void PointerCaptureController::cancelPointer(PointerID pointerId, const IntPoint capturingData->pendingTargetOverride = nullptr; capturingData->state = CapturingData::State::Cancelled; @@ -6678,20 +6735,6 @@ index c359242a7967dab94b8dc3c276a6df5473527145..64b0c6a0bfdf27a0305c25e8b8e0cda6 IntSize dragImageSize(DragImageRef) { -diff --git a/Source/WebCore/platform/MIMETypeRegistry.cpp b/Source/WebCore/platform/MIMETypeRegistry.cpp -index 092649a26549e8fc7c5683ad47585b4d7b1a0eab..7944352d9218b6237cfb4baceb686fcfa056c0ef 100644 ---- a/Source/WebCore/platform/MIMETypeRegistry.cpp -+++ b/Source/WebCore/platform/MIMETypeRegistry.cpp -@@ -663,6 +663,9 @@ bool MIMETypeRegistry::canShowMIMEType(const String& mimeType) - if (startsWithLettersIgnoringASCIICase(mimeType, "text/"_s)) - return !isUnsupportedTextMIMEType(mimeType); - -+ if (equalLettersIgnoringASCIICase(mimeType, "application/x-zerosize"_s)) -+ return true; -+ - return false; - } - diff --git a/Source/WebCore/platform/Pasteboard.h b/Source/WebCore/platform/Pasteboard.h index 86205341336734eebfb9f1c98bb4b8ac4e81c3aa..1fd303a89a0431806f91ac072037806e7d6a2fe7 100644 --- a/Source/WebCore/platform/Pasteboard.h @@ -6851,12 +6894,12 @@ index 6c64c7040eb190c3d67380070e884a8230029c26..d0f8341c538cbc2323ac0074a5ef3226 + } // namespace WebCore diff --git a/Source/WebCore/platform/PlatformTouchEvent.h b/Source/WebCore/platform/PlatformTouchEvent.h -index 27b46407faf6ae9d245856fd0ed664984e8729f8..2b8912789dcda373ceffa8f23996566ff07df2dd 100644 +index 23f011953c66f401553bedfaef3485af215ae083..a73da2ebe47f0d8dc57f3d0159e8f299abb61c96 100644 --- a/Source/WebCore/platform/PlatformTouchEvent.h +++ b/Source/WebCore/platform/PlatformTouchEvent.h -@@ -38,7 +38,7 @@ public: +@@ -42,7 +42,7 @@ public: - const Vector& touchPoints() const { return m_touchPoints; } + const Vector& predictedEvents() const { return m_predictedEvents; } -#if PLATFORM(WPE) +#if !ENABLE(IOS_TOUCH_EVENTS) @@ -8064,10 +8107,10 @@ index 20b1437401c9560e22fd9e218d8193f09ac2aaaa..ff32706566fefa61030a9e7a1523987d ResourceResponseBase::Source source; ResourceResponseBase::Type type; diff --git a/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm b/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm -index eeea738bc83ef0bf7d7791286915cc65ed662925..fa4c9b8209a6d080c978efcab92d005aaf8bd0b3 100644 +index 4ea947b9e847bed7abe42519481f563fc064b385..7beb54c1196128f67ef42331c89cb28ff9c8d450 100644 --- a/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm +++ b/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm -@@ -519,6 +519,22 @@ bool NetworkStorageSession::setCookieFromDOM(const URL& firstParty, const SameSi +@@ -491,6 +491,22 @@ bool NetworkStorageSession::setCookieFromDOM(const URL& firstParty, const SameSi return false; } @@ -8818,7 +8861,7 @@ index 0000000000000000000000000000000000000000..a76b583a1e65cd6999fab4784c22dd9c + +} // namespace WebCore diff --git a/Source/WebCore/rendering/AncestorSubgridIterator.cpp b/Source/WebCore/rendering/AncestorSubgridIterator.cpp -index 9e7a774c2e0e591e491e0cda3657ae02f4543453..3161944e8f455d4dd33f6bbfd81ae2bb54d0787a 100644 +index 4613cfc6af155593459f8af9c2bf4211a01383b9..209542305a8454da2f80691ab7759b0f62b32604 100644 --- a/Source/WebCore/rendering/AncestorSubgridIterator.cpp +++ b/Source/WebCore/rendering/AncestorSubgridIterator.cpp @@ -30,7 +30,7 @@ @@ -8888,7 +8931,7 @@ index 1d8488e0d36288e09cd5662bd7f770ade95dfee3..dee07f87b47d62d4ef8ede45824bdb2f WorkerOrWorkletGlobalScope& m_globalScope; }; diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp -index 5f283341f89b3bc3be0c7508a0d995144764bb45..dc618d18fcfe7ef819f3724847f3d4e4c6771fc2 100644 +index 6a87d4478288777a9a08fc92669841ea0d19c936..7fd9ee105769519f212d4e4d2480665ab437e274 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp @@ -96,6 +96,8 @@ @@ -8900,7 +8943,7 @@ index 5f283341f89b3bc3be0c7508a0d995144764bb45..dc618d18fcfe7ef819f3724847f3d4e4 #endif #if ENABLE(APPLE_PAY_REMOTE_UI) -@@ -1090,6 +1092,14 @@ void NetworkConnectionToWebProcess::clearPageSpecificData(PageIdentifier pageID) +@@ -1098,6 +1100,14 @@ void NetworkConnectionToWebProcess::clearPageSpecificData(PageIdentifier pageID) storageSession->clearPageSpecificDataForResourceLoadStatistics(pageID); } @@ -8916,10 +8959,10 @@ index 5f283341f89b3bc3be0c7508a0d995144764bb45..dc618d18fcfe7ef819f3724847f3d4e4 { if (auto* storageSession = networkProcess().storageSession(m_sessionID)) diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h -index 0b2641927c4f8b17381b8e8c5bfa5e666506e229..22073e29d1104d928e5ca2fa56ffe830ede1f8a8 100644 +index d5d5921514e7f3abf30e67352d2f20ca32e3c175..e684f945a3057587f9ec2c03ecf062dca48a2a1a 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h -@@ -343,6 +343,8 @@ private: +@@ -347,6 +347,8 @@ private: void clearPageSpecificData(WebCore::PageIdentifier); @@ -8929,7 +8972,7 @@ index 0b2641927c4f8b17381b8e8c5bfa5e666506e229..22073e29d1104d928e5ca2fa56ffe830 void logUserInteraction(RegistrableDomain&&); diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in -index 1edf0f0137dcf67dd425ce92180cdd5a1811475b..0af045974298fee7227b0cfa8b2b9e6c54779ec0 100644 +index e3a5c58cd28d82e62aa08b4800fb559d78a8af39..c9db082407eaa4fc1b69c5ae0e211606ed09c2c3 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in @@ -74,6 +74,8 @@ messages -> NetworkConnectionToWebProcess LegacyReceiver { @@ -8942,10 +8985,10 @@ index 1edf0f0137dcf67dd425ce92180cdd5a1811475b..0af045974298fee7227b0cfa8b2b9e6c LogUserInteraction(WebCore::RegistrableDomain domain) ResourceLoadStatisticsUpdated(Vector statistics) -> () diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.cpp b/Source/WebKit/NetworkProcess/NetworkProcess.cpp -index 5d7274e691dbe4a9c4763b349abca7d94e878554..d58697dd16691e1caa53a5548eb68cff1309f163 100644 +index 6a5370ac4579a2f3c4b7b1d7c8a1d4df510e8892..8c444c89546460f3a5c1c5ff92427ff8f1af681f 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.cpp +++ b/Source/WebKit/NetworkProcess/NetworkProcess.cpp -@@ -659,6 +659,12 @@ void NetworkProcess::registrableDomainsExemptFromWebsiteDataDeletion(PAL::Sessio +@@ -666,6 +666,12 @@ void NetworkProcess::registrableDomainsExemptFromWebsiteDataDeletion(PAL::Sessio completionHandler({ }); } @@ -8959,7 +9002,7 @@ index 5d7274e691dbe4a9c4763b349abca7d94e878554..d58697dd16691e1caa53a5548eb68cff { if (auto* session = networkSession(sessionID)) { diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.h b/Source/WebKit/NetworkProcess/NetworkProcess.h -index 95572ae0054657f1f8f2840291d49f8d23340990..38f6df007d51c53792d270f3d50fa2f1b77b4ca1 100644 +index 2b4e411ca19004bd4f18949f3196dc06c1995769..404b2d5d1ac5ce0a63a776ae4fc5022604c60a4e 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.h +++ b/Source/WebKit/NetworkProcess/NetworkProcess.h @@ -33,6 +33,7 @@ @@ -8989,10 +9032,10 @@ index 95572ae0054657f1f8f2840291d49f8d23340990..38f6df007d51c53792d270f3d50fa2f1 void clearUserInteraction(PAL::SessionID, RegistrableDomain&&, CompletionHandler&&); void deleteAndRestrictWebsiteDataForRegistrableDomains(PAL::SessionID, OptionSet, RegistrableDomainsToDeleteOrRestrictWebsiteDataFor&&, bool shouldNotifyPage, CompletionHandler&&)>&&); diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.messages.in b/Source/WebKit/NetworkProcess/NetworkProcess.messages.in -index 9679dc2ceea7b085638c19c00ba9fd04e71507da..130f12138c427a90dfffb96d7e219a258e819d10 100644 +index 6178b4ee6d042a8e69f895ae56ca7e5393766b3d..1aa28797177a714faeb9a747388f02d1334906f6 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.messages.in +++ b/Source/WebKit/NetworkProcess/NetworkProcess.messages.in -@@ -82,6 +82,8 @@ messages -> NetworkProcess LegacyReceiver { +@@ -83,6 +83,8 @@ messages -> NetworkProcess LegacyReceiver { SetInspectionForServiceWorkersAllowed(PAL::SessionID sessionID, bool inspectable) @@ -9002,7 +9045,7 @@ index 9679dc2ceea7b085638c19c00ba9fd04e71507da..130f12138c427a90dfffb96d7e219a25 ClearUserInteraction(PAL::SessionID sessionID, WebCore::RegistrableDomain resourceDomain) -> () DumpResourceLoadStatistics(PAL::SessionID sessionID) -> (String dumpedStatistics) diff --git a/Source/WebKit/NetworkProcess/NetworkSession.h b/Source/WebKit/NetworkProcess/NetworkSession.h -index f5a62296667657a7f094627e0792cb4284c04ec4..93c34c61f1cb29f3ed4c1c787da6f3efe994cfb2 100644 +index 8d86772e1b2b5e73e7f8c22042947ec4d7b610cc..9ec2764722032a5f0272757a33df1372218e8411 100644 --- a/Source/WebKit/NetworkProcess/NetworkSession.h +++ b/Source/WebKit/NetworkProcess/NetworkSession.h @@ -200,6 +200,9 @@ public: @@ -9015,7 +9058,7 @@ index f5a62296667657a7f094627e0792cb4284c04ec4..93c34c61f1cb29f3ed4c1c787da6f3ef void removeSoftUpdateLoader(ServiceWorkerSoftUpdateLoader* loader) { m_softUpdateLoaders.remove(loader); } void addNavigationPreloaderTask(ServiceWorkerFetchTask&); ServiceWorkerFetchTask* navigationPreloaderTaskFromFetchIdentifier(WebCore::FetchIdentifier); -@@ -309,6 +312,7 @@ protected: +@@ -311,6 +314,7 @@ protected: bool m_privateClickMeasurementDebugModeEnabled { false }; std::optional m_ephemeralMeasurement; bool m_isRunningEphemeralMeasurementTest { false }; @@ -9024,10 +9067,10 @@ index f5a62296667657a7f094627e0792cb4284c04ec4..93c34c61f1cb29f3ed4c1c787da6f3ef HashSet> m_keptAliveLoads; diff --git a/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm b/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm -index 6c4c0e37a75426d90667c9ec595f8fa1b19ca502..45d1e5916c8cc5ec0136cf3b3596e9efb5e4a1ec 100644 +index 78f92e5cf513a57060372a2448f291bbc90fdb7c..ea46b6ff9dc22fbb1df215c8e7e318a2dd6892ae 100644 --- a/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm +++ b/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm -@@ -769,6 +769,8 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didRece +@@ -771,6 +771,8 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didRece if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { sessionCocoa->setClientAuditToken(challenge); @@ -9036,7 +9079,7 @@ index 6c4c0e37a75426d90667c9ec595f8fa1b19ca502..45d1e5916c8cc5ec0136cf3b3596e9ef NSURLSessionTaskTransactionMetrics *metrics = task._incompleteTaskMetrics.transactionMetrics.lastObject; auto tlsVersion = (tls_protocol_version_t)metrics.negotiatedTLSProtocolVersion.unsignedShortValue; -@@ -1113,6 +1115,13 @@ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)data +@@ -1120,6 +1122,13 @@ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)data resourceResponse.setDeprecatedNetworkLoadMetrics(WebCore::copyTimingData(taskMetrics, networkDataTask->networkLoadMetrics())); @@ -9657,10 +9700,10 @@ index ea1eb9f00feaaecf73bdddc37c904e88f43bfa85..8a631e5293a11abd650958baad4e9678 #endif }; diff --git a/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in b/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in -index 54bfa4555c6dd11f8ee8e3a75df6ba97c1032e84..c795925b062886a3eee42ef3b37e2f084ae55f7d 100644 +index 7261dea8449a1c8f477aa2be76c5a2f887bfc330..9d87c926c337a59b3acdb952936fe466db42622a 100644 --- a/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in +++ b/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in -@@ -2691,6 +2691,9 @@ class WebCore::AuthenticationChallenge { +@@ -2699,6 +2699,9 @@ class WebCore::AuthenticationChallenge { class WebCore::DragData { #if PLATFORM(COCOA) String pasteboardName(); @@ -9670,7 +9713,7 @@ index 54bfa4555c6dd11f8ee8e3a75df6ba97c1032e84..c795925b062886a3eee42ef3b37e2f08 #endif WebCore::IntPoint clientPosition(); WebCore::IntPoint globalPosition(); -@@ -3254,6 +3257,7 @@ enum class WebCore::WasPrivateRelayed : bool; +@@ -3261,6 +3264,7 @@ enum class WebCore::WasPrivateRelayed : bool; String httpStatusText; String httpVersion; WebCore::HTTPHeaderMap httpHeaderFields; @@ -9784,7 +9827,7 @@ index 8e4e2d6d5ebb08fba210fe0a328d45290348dd11..32a43192ec1e918c33b1b046b71d2ec5 const String& text() const { return m_text; } diff --git a/Source/WebKit/Shared/WebMouseEvent.h b/Source/WebKit/Shared/WebMouseEvent.h -index 5da1ed78e5a55bf63e9e52e33dfa9e704922589a..6630725885bbfe6123537ea799bf5b6885ea977f 100644 +index fd4722dd38df74f259d8add02025549022a0a205..e1c33f6d766707170935b3e77e81098cc8e3786d 100644 --- a/Source/WebKit/Shared/WebMouseEvent.h +++ b/Source/WebKit/Shared/WebMouseEvent.h @@ -70,6 +70,7 @@ public: @@ -9796,10 +9839,10 @@ index 5da1ed78e5a55bf63e9e52e33dfa9e704922589a..6630725885bbfe6123537ea799bf5b68 void setPosition(const WebCore::IntPoint& position) { m_position = position; } const WebCore::IntPoint& globalPosition() const { return m_globalPosition; } diff --git a/Source/WebKit/Shared/WebPageCreationParameters.h b/Source/WebKit/Shared/WebPageCreationParameters.h -index 61fc6649bc99de246c5f0cb6171b717e89d69425..c2c998d337c0f399d8f35daa804c995fae66d50c 100644 +index 80d76574435e35abf76eb640da5f93023dda9c0b..f167e8969328160136ce33368ce1fa746ee852df 100644 --- a/Source/WebKit/Shared/WebPageCreationParameters.h +++ b/Source/WebKit/Shared/WebPageCreationParameters.h -@@ -299,6 +299,8 @@ struct WebPageCreationParameters { +@@ -303,6 +303,8 @@ struct WebPageCreationParameters { bool httpsUpgradeEnabled { true }; @@ -9809,7 +9852,7 @@ index 61fc6649bc99de246c5f0cb6171b717e89d69425..c2c998d337c0f399d8f35daa804c995f bool allowsDeprecatedSynchronousXMLHttpRequestDuringUnload { false }; #endif diff --git a/Source/WebKit/Shared/WebPageCreationParameters.serialization.in b/Source/WebKit/Shared/WebPageCreationParameters.serialization.in -index 96619aedf71cb8527e6c3da65b055f2c9a242c8e..34e23875c78420e3ef93beb67f14eac609d8d9d3 100644 +index 7ff97d01848ce5c63e2ab89433083a91d4ac67ec..9c4af6d477d66294e46b05d673924c4956970d3a 100644 --- a/Source/WebKit/Shared/WebPageCreationParameters.serialization.in +++ b/Source/WebKit/Shared/WebPageCreationParameters.serialization.in @@ -227,6 +227,8 @@ enum class WebCore::UserInterfaceLayoutDirection : bool; @@ -9892,10 +9935,10 @@ index 0000000000000000000000000000000000000000..f4f09d171ebf9774b3f8744751d220d3 + bool canSmartReplace() +} diff --git a/Source/WebKit/Shared/unix/AuxiliaryProcessMain.cpp b/Source/WebKit/Shared/unix/AuxiliaryProcessMain.cpp -index 5bae771f7b19ebeaea42edca80198a598f1b49e4..82fa8826f7bd505f596fd7f0d378d62ba1ac3f2f 100644 +index f5de9fcc55cb68a75dd8cf748d3d5b3e27fd9555..04a29727614a02d04eb9a0eb3779d28d60bdef12 100644 --- a/Source/WebKit/Shared/unix/AuxiliaryProcessMain.cpp +++ b/Source/WebKit/Shared/unix/AuxiliaryProcessMain.cpp -@@ -38,6 +38,15 @@ +@@ -44,6 +44,15 @@ namespace WebKit { @@ -9911,10 +9954,10 @@ index 5bae771f7b19ebeaea42edca80198a598f1b49e4..82fa8826f7bd505f596fd7f0d378d62b AuxiliaryProcessMainCommon::AuxiliaryProcessMainCommon() { #if ENABLE(BREAKPAD) -@@ -57,6 +66,10 @@ bool AuxiliaryProcessMainCommon::parseCommandLine(int argc, char** argv) - if (argc > 3 && argv[3] && !strcmp(argv[3], "--configure-jsc-for-testing")) +@@ -97,6 +106,10 @@ bool AuxiliaryProcessMainCommon::parseCommandLine(int argc, char** argv) JSC::Config::configureForTesting(); #endif + +// Playwright begin + if (hasArgument("--enable-shared-array-buffer", argc, argv)) + m_parameters.shouldEnableSharedArrayBuffer = true; @@ -9938,7 +9981,7 @@ index 9edb5fbcd103cd8d1b224dfd60ac88aabe9626d2..9ed392ae3809f8bda92a2765ffadc643 JSC::Config::configureForTesting(); else if (!strcmp(argv[i], "-disable-jit")) diff --git a/Source/WebKit/Shared/win/WebEventFactory.cpp b/Source/WebKit/Shared/win/WebEventFactory.cpp -index 4d418e2bd7f970bc5bfebceb88adb172e5eb8540..e988f9011fa194224f7376e134d50fc553725289 100644 +index 4d418e2bd7f970bc5bfebceb88adb172e5eb8540..8a58380d830f9a8aec3b4240d9c8cf7e65eaccdc 100644 --- a/Source/WebKit/Shared/win/WebEventFactory.cpp +++ b/Source/WebKit/Shared/win/WebEventFactory.cpp @@ -484,7 +484,7 @@ WebKeyboardEvent WebEventFactory::createWebKeyboardEvent(HWND hwnd, UINT message @@ -9946,12 +9989,12 @@ index 4d418e2bd7f970bc5bfebceb88adb172e5eb8540..e988f9011fa194224f7376e134d50fc5 WebTouchEvent WebEventFactory::createWebTouchEvent() { - return WebTouchEvent(); -+ return WebTouchEvent({ WebEventType::TouchMove, OptionSet { }, WallTime::now()}, { }); ++ return WebTouchEvent({ WebEventType::TouchMove, OptionSet { }, WallTime::now()}, { }, { }, { }); } #endif // ENABLE(TOUCH_EVENTS) diff --git a/Source/WebKit/Sources.txt b/Source/WebKit/Sources.txt -index 3844b4e93816ad5e4a1ac4156516bc29c0203eb2..922a99ed0e62d66e39eb20b05f757870da8be85e 100644 +index f6bf1fc5c07af2398b8f4bd054f06ad8c87ef9db..19fa3d40595e204170d5302499f36f9d73a7f20b 100644 --- a/Source/WebKit/Sources.txt +++ b/Source/WebKit/Sources.txt @@ -377,6 +377,7 @@ Shared/XR/XRDeviceProxy.cpp @@ -10005,7 +10048,7 @@ index 3844b4e93816ad5e4a1ac4156516bc29c0203eb2..922a99ed0e62d66e39eb20b05f757870 UIProcess/Media/AudioSessionRoutingArbitratorProxy.cpp UIProcess/Media/MediaUsageManager.cpp diff --git a/Source/WebKit/SourcesCocoa.txt b/Source/WebKit/SourcesCocoa.txt -index fe64c9af8d7f79b86941404d5aed66424a675bf7..0f1e5aa7a24ddc03c7462226312b4ffb354de632 100644 +index ef6151bae0fc9c79eb542cfa094242eb7d25e765..37a74fc32140c2cc7b93e3b93f05f0a73c6decd3 100644 --- a/Source/WebKit/SourcesCocoa.txt +++ b/Source/WebKit/SourcesCocoa.txt @@ -270,6 +270,7 @@ UIProcess/API/Cocoa/_WKArchiveExclusionRule.mm @@ -10016,7 +10059,7 @@ index fe64c9af8d7f79b86941404d5aed66424a675bf7..0f1e5aa7a24ddc03c7462226312b4ffb UIProcess/API/Cocoa/_WKContentRuleListAction.mm UIProcess/API/Cocoa/_WKContextMenuElementInfo.mm UIProcess/API/Cocoa/_WKCustomHeaderFields.mm @no-unify -@@ -454,6 +455,7 @@ UIProcess/Inspector/ios/WKInspectorHighlightView.mm +@@ -455,6 +456,7 @@ UIProcess/Inspector/ios/WKInspectorHighlightView.mm UIProcess/Inspector/ios/WKInspectorNodeSearchGestureRecognizer.mm UIProcess/Inspector/mac/RemoteWebInspectorUIProxyMac.mm @@ -10025,7 +10068,7 @@ index fe64c9af8d7f79b86941404d5aed66424a675bf7..0f1e5aa7a24ddc03c7462226312b4ffb UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm UIProcess/Inspector/mac/WKInspectorViewController.mm diff --git a/Source/WebKit/SourcesGTK.txt b/Source/WebKit/SourcesGTK.txt -index 94d0f078de20208836e4a1a63f6dd0ce4e7ab556..fce80ee80cb3f2079b7405d49b5646af060c10be 100644 +index b2c71c867b291874c8b9cc8c36a4f19cdb20482c..838518aa70c36cfd27a915bd329bef6adb53845a 100644 --- a/Source/WebKit/SourcesGTK.txt +++ b/Source/WebKit/SourcesGTK.txt @@ -130,6 +130,7 @@ UIProcess/API/glib/WebKitAutomationSession.cpp @no-unify @@ -10036,15 +10079,15 @@ index 94d0f078de20208836e4a1a63f6dd0ce4e7ab556..fce80ee80cb3f2079b7405d49b5646af UIProcess/API/glib/WebKitContextMenuClient.cpp @no-unify UIProcess/API/glib/WebKitCookieManager.cpp @no-unify UIProcess/API/glib/WebKitCredential.cpp @no-unify -@@ -257,6 +258,7 @@ UIProcess/glib/DisplayLinkGLib.cpp - UIProcess/glib/DisplayVBlankMonitor.cpp +@@ -258,6 +259,7 @@ UIProcess/glib/DisplayVBlankMonitor.cpp UIProcess/glib/DisplayVBlankMonitorDRM.cpp UIProcess/glib/DisplayVBlankMonitorTimer.cpp + UIProcess/glib/FenceMonitor.cpp +UIProcess/glib/InspectorPlaywrightAgentClientGLib.cpp UIProcess/glib/ScreenManager.cpp UIProcess/glib/WebPageProxyGLib.cpp UIProcess/glib/WebProcessPoolGLib.cpp -@@ -272,6 +274,7 @@ UIProcess/gtk/ClipboardGtk4.cpp @no-unify +@@ -273,6 +275,7 @@ UIProcess/gtk/ClipboardGtk4.cpp @no-unify UIProcess/gtk/WebDateTimePickerGtk.cpp UIProcess/gtk/GtkSettingsManager.cpp UIProcess/gtk/HardwareAccelerationManager.cpp @@ -10052,7 +10095,7 @@ index 94d0f078de20208836e4a1a63f6dd0ce4e7ab556..fce80ee80cb3f2079b7405d49b5646af UIProcess/gtk/KeyBindingTranslator.cpp UIProcess/gtk/PointerLockManager.cpp @no-unify UIProcess/gtk/PointerLockManagerWayland.cpp @no-unify -@@ -284,6 +287,8 @@ UIProcess/gtk/ViewGestureControllerGtk.cpp +@@ -285,6 +288,8 @@ UIProcess/gtk/ViewGestureControllerGtk.cpp UIProcess/gtk/WebColorPickerGtk.cpp UIProcess/gtk/WebContextMenuProxyGtk.cpp UIProcess/gtk/WebDataListSuggestionsDropdownGtk.cpp @@ -10062,7 +10105,7 @@ index 94d0f078de20208836e4a1a63f6dd0ce4e7ab556..fce80ee80cb3f2079b7405d49b5646af UIProcess/gtk/WebPasteboardProxyGtk.cpp UIProcess/gtk/WebPopupMenuProxyGtk.cpp diff --git a/Source/WebKit/SourcesWPE.txt b/Source/WebKit/SourcesWPE.txt -index e6c9ee4c1e8ee1ad95ae6595e061378b377a3501..71882922bff1ad74a96b0bca2ea5522f81ef70ed 100644 +index df35af2dde4e966d500d67a732f9637cace6336d..2295fbc40e103f01812daf9aafd2182edc96cc0d 100644 --- a/Source/WebKit/SourcesWPE.txt +++ b/Source/WebKit/SourcesWPE.txt @@ -132,6 +132,7 @@ UIProcess/API/glib/WebKitAuthenticationRequest.cpp @no-unify @@ -10089,15 +10132,15 @@ index e6c9ee4c1e8ee1ad95ae6595e061378b377a3501..71882922bff1ad74a96b0bca2ea5522f UIProcess/API/wpe/WebKitInputMethodContextWPE.cpp @no-unify UIProcess/API/wpe/WebKitInputMethodContextImplWPE.cpp @no-unify UIProcess/API/wpe/WebKitPopupMenu.cpp @no-unify -@@ -227,6 +230,7 @@ UIProcess/glib/DisplayLinkGLib.cpp - UIProcess/glib/DisplayVBlankMonitor.cpp +@@ -228,6 +231,7 @@ UIProcess/glib/DisplayVBlankMonitor.cpp UIProcess/glib/DisplayVBlankMonitorDRM.cpp UIProcess/glib/DisplayVBlankMonitorTimer.cpp + UIProcess/glib/FenceMonitor.cpp +UIProcess/glib/InspectorPlaywrightAgentClientGLib.cpp UIProcess/glib/ScreenManager.cpp UIProcess/glib/WebPageProxyGLib.cpp UIProcess/glib/WebProcessPoolGLib.cpp -@@ -259,7 +263,12 @@ UIProcess/linux/MemoryPressureMonitor.cpp +@@ -260,7 +264,12 @@ UIProcess/linux/MemoryPressureMonitor.cpp UIProcess/soup/WebProcessPoolSoup.cpp UIProcess/wpe/AcceleratedBackingStoreDMABuf.cpp @@ -10110,7 +10153,7 @@ index e6c9ee4c1e8ee1ad95ae6595e061378b377a3501..71882922bff1ad74a96b0bca2ea5522f UIProcess/wpe/WebPageProxyWPE.cpp UIProcess/wpe/WebPreferencesWPE.cpp -@@ -286,6 +295,8 @@ WebProcess/WebCoreSupport/glib/WebEditorClientGLib.cpp +@@ -287,6 +296,8 @@ WebProcess/WebCoreSupport/glib/WebEditorClientGLib.cpp WebProcess/WebCoreSupport/soup/WebFrameNetworkingContext.cpp @@ -10180,7 +10223,7 @@ index 6dc23b36695692f1052de941d4d89dcd72e9f337..804cbc2666d410d7465036621f1c7a16 bool shouldIgnorePointerEventsNone() const { return m_request.shouldIgnorePointerEventsNone; } diff --git a/Source/WebKit/UIProcess/API/APIUIClient.h b/Source/WebKit/UIProcess/API/APIUIClient.h -index 9ecfb4e61a015c97e3adaeccfcf52ce24735eeed..decae9b739c9692921305b87449f6557a07959c2 100644 +index ad14acfbfe6ddc134471e52337a6e378951e94ac..c08cb43238008c4e2f49cb8db575db8f915b9faa 100644 --- a/Source/WebKit/UIProcess/API/APIUIClient.h +++ b/Source/WebKit/UIProcess/API/APIUIClient.h @@ -115,6 +115,7 @@ public: @@ -10235,7 +10278,7 @@ index 026121d114c5fcad84c1396be8d692625beaa3bd..edd6e5cae033124c589959a42522fde0 } #endif diff --git a/Source/WebKit/UIProcess/API/C/WKPage.cpp b/Source/WebKit/UIProcess/API/C/WKPage.cpp -index 4abc7ac9b65c92e1f8ac122b97abf32c69631eb7..93e67e9e60ffa2536be7da1b70a7704d6eb3313f 100644 +index 5b8eb0a1b7394a6fd45fc14eb14ca44e72d6ba2a..fad70f71c236a4d087f5ade713eb74a89bc6124e 100644 --- a/Source/WebKit/UIProcess/API/C/WKPage.cpp +++ b/Source/WebKit/UIProcess/API/C/WKPage.cpp @@ -1781,6 +1781,13 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient @@ -10322,7 +10365,7 @@ index 1484f064ec89ee8c25c35df9f0a4462896699415..0622f4d5fc9144b9059395d9d0730a4a // Version 15. WKPageDecidePolicyForSpeechRecognitionPermissionRequestCallback decidePolicyForSpeechRecognitionPermissionRequest; diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm b/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm -index 857afb1b892c2ee7327808f3dab0cff441c92c52..332bb2e687d6b97fd11f1366ade5b17841bcae58 100644 +index bc2cfdf33cf27ab45ddddf32075467313a063021..5f25865f940fa1d89c9f7d7588b2bf915814d156 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm @@ -702,6 +702,16 @@ - (void)_setMediaCaptureRequiresSecureConnection:(BOOL)requiresSecureConnection @@ -10339,11 +10382,11 @@ index 857afb1b892c2ee7327808f3dab0cff441c92c52..332bb2e687d6b97fd11f1366ade5b178 + _preferences->setAlternateWebMPlayerEnabled(enabled); +} + - - (double)_inactiveMediaCaptureSteamRepromptIntervalInMinutes + - (double)_inactiveMediaCaptureStreamRepromptIntervalInMinutes { - return _preferences->inactiveMediaCaptureSteamRepromptIntervalInMinutes(); + return _preferences->inactiveMediaCaptureStreamRepromptIntervalInMinutes(); diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h -index 950a5587c9ed75292e6ad8b4f898b73de3dabc25..f315b1023c6910e23e88d18022a18b6710308a55 100644 +index 9908171d94c74d52a4c4a83d6af11ccbaa8790b7..89f30465c0eaecefe2f5496de682857867a289ff 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h +++ b/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h @@ -119,6 +119,7 @@ typedef NS_ENUM(NSInteger, _WKPitchCorrectionAlgorithm) { @@ -10353,7 +10396,7 @@ index 950a5587c9ed75292e6ad8b4f898b73de3dabc25..f315b1023c6910e23e88d18022a18b67 +@property (nonatomic, setter=_setAlternateWebMPlayerEnabled:) BOOL _alternateWebMPlayerEnabled WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)); @property (nonatomic, setter=_setEnumeratingAllNetworkInterfacesEnabled:) BOOL _enumeratingAllNetworkInterfacesEnabled WK_API_AVAILABLE(macos(10.13), ios(11.0)); @property (nonatomic, setter=_setICECandidateFilteringEnabled:) BOOL _iceCandidateFilteringEnabled WK_API_AVAILABLE(macos(10.13.4), ios(11.3)); - @property (nonatomic, setter=_setInactiveMediaCaptureSteamRepromptIntervalInMinutes:) double _inactiveMediaCaptureSteamRepromptIntervalInMinutes WK_API_AVAILABLE(macos(10.13.4), ios(11.3)); + @property (nonatomic, setter=_setInactiveMediaCaptureStreamRepromptIntervalInMinutes:) double _inactiveMediaCaptureStreamRepromptIntervalInMinutes WK_API_AVAILABLE(macos(10.13.4), ios(11.3)); diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegate.h b/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegate.h index ca205ac38f92e95c24830d30657d093afd22f02b..aeaf6335ab9fc0590cd723e0c585677a014c4549 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegate.h @@ -10393,7 +10436,7 @@ index eff4cf557033561ab20762d93a58c2d71f5505f0..2fd5a2515c54d9edcab48fa3d993298f NS_ASSUME_NONNULL_END diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm -index 2432da7fa381ba09f73f2126b978e4b454e42e4b..d3f1ce3d339b6ebd6d61100eb8d4c71d86258f00 100644 +index e085f3a02dce0f90d2c75c23d9ee8c61c5d63231..467633532033cc8019ac48b6d21baf1691805ed8 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm @@ -51,6 +51,7 @@ @@ -11746,10 +11789,10 @@ index 720c88818bdb4cde3cb58e95785454754f6c1396..2658e6709a13e5d6258abca956ec52bc void didChangePageID(WKWPE::View&) override; void didReceiveUserMessage(WKWPE::View&, WebKit::UserMessage&&, CompletionHandler&&) override; diff --git a/Source/WebKit/UIProcess/Automation/WebAutomationSession.h b/Source/WebKit/UIProcess/Automation/WebAutomationSession.h -index 65bf3b71e451aad11039130d2d23a68f5fce499f..99a1402270bcd210107bcc4f029837891886cbdd 100644 +index 07eda283848542ed7e8a2aa0abdfa543c8aa756d..75dfd2b0c2122c472cd796973aa4d1cefec471a3 100644 --- a/Source/WebKit/UIProcess/Automation/WebAutomationSession.h +++ b/Source/WebKit/UIProcess/Automation/WebAutomationSession.h -@@ -233,6 +233,8 @@ public: +@@ -235,6 +235,8 @@ public: void didDestroyFrame(WebCore::FrameIdentifier); @@ -11758,7 +11801,7 @@ index 65bf3b71e451aad11039130d2d23a68f5fce499f..99a1402270bcd210107bcc4f02983789 private: RefPtr webPageProxyForHandle(const String&); String handleForWebPageProxy(const WebPageProxy&); -@@ -284,7 +286,6 @@ private: +@@ -286,7 +288,6 @@ private: // Get base64-encoded PNG data from a bitmap. static std::optional platformGetBase64EncodedPNGData(WebCore::ShareableBitmap::Handle&&); @@ -11767,7 +11810,7 @@ index 65bf3b71e451aad11039130d2d23a68f5fce499f..99a1402270bcd210107bcc4f02983789 // Save base64-encoded file contents to a local file path and return the path. // This reuses the basename of the remote file path so that the filename exposed to DOM API remains the same. diff --git a/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp b/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp -index a889806fbc7d1329a6bd7a6d179d31318cc2f010..197f2fdc887eef0a25f82a5d4702e6d34a3a126e 100644 +index a6864205241a0c2f31661f8a2629bdba4feae9b5..aa528ad3a1c1c850eed91ba01d500aaff8550d4a 100644 --- a/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp +++ b/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp @@ -165,7 +165,11 @@ void AuxiliaryProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& lau @@ -11783,12 +11826,12 @@ index a889806fbc7d1329a6bd7a6d179d31318cc2f010..197f2fdc887eef0a25f82a5d4702e6d3 platformGetLaunchOptions(launchOptions); } diff --git a/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h b/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h -index e792deb624dc217fd9502461c0f7cabf789d812c..43b2912a05eaea0911662934305e94a6cc1477d1 100644 +index 306d6ae98067d79851f4622e5aabebe76d293a2e..701a70f88bc1978c9904eef19d33761e9668ea37 100644 --- a/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h +++ b/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h -@@ -277,13 +277,16 @@ protected: - static RefPtr fetchAudioComponentServerRegistrations(); - #endif +@@ -287,13 +287,16 @@ protected: + + InitializationActivityAndGrant initializationActivityAndGrant(); + /* playwright revert 50f8fee - make protected to allow use from WebProcessProxy */ + Vector platformOverrideLanguages() const; @@ -11949,7 +11992,7 @@ index 957f7f088087169668a9b4f1ba65d9f206a2a836..15e44c8d5b6a3eafb7f1148707366b0c class PopUpSOAuthorizationSession final : public SOAuthorizationSession { public: diff --git a/Source/WebKit/UIProcess/Cocoa/UIDelegate.h b/Source/WebKit/UIProcess/Cocoa/UIDelegate.h -index f6b1c41cd18d751f0622b4ecf00b5355b6212a17..5b116ff1a8c94906f94802af233c7f1e97d47d00 100644 +index 5f1c130ef8235f1002ffd0fa156cd5ff9e14a019..0c73c4cc5ccd1ab8745322926c68865d80fb2264 100644 --- a/Source/WebKit/UIProcess/Cocoa/UIDelegate.h +++ b/Source/WebKit/UIProcess/Cocoa/UIDelegate.h @@ -105,6 +105,7 @@ private: @@ -11960,7 +12003,7 @@ index f6b1c41cd18d751f0622b4ecf00b5355b6212a17..5b116ff1a8c94906f94802af233c7f1e void presentStorageAccessConfirmDialog(const WTF::String& requestingDomain, const WTF::String& currentDomain, CompletionHandler&&); void requestStorageAccessConfirm(WebPageProxy&, WebFrameProxy*, const WebCore::RegistrableDomain& requestingDomain, const WebCore::RegistrableDomain& currentDomain, std::optional&&, CompletionHandler&&) final; void decidePolicyForGeolocationPermissionRequest(WebPageProxy&, WebFrameProxy&, const FrameInfoData&, Function&) final; -@@ -221,6 +222,7 @@ private: +@@ -222,6 +223,7 @@ private: bool webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler : 1; bool webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler : 1; bool webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler : 1; @@ -11969,7 +12012,7 @@ index f6b1c41cd18d751f0622b4ecf00b5355b6212a17..5b116ff1a8c94906f94802af233c7f1e bool webViewRequestStorageAccessPanelForDomainUnderCurrentDomainForQuirkDomainsCompletionHandler : 1; bool webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler : 1; diff --git a/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm b/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm -index 3ac9df925437afb3b4d37107fc6c02dc9ee41fa6..eb0dc8703860ef5006d6df26a6ca6115a871ed0f 100644 +index 71a1c0e707aa595be47e134096a5534c03c631a2..a7d2aee9b65ac13c9d2a231252f4c50a50f79404 100644 --- a/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm +++ b/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm @@ -118,6 +118,7 @@ void UIDelegate::setDelegate(id delegate) @@ -11980,7 +12023,7 @@ index 3ac9df925437afb3b4d37107fc6c02dc9ee41fa6..eb0dc8703860ef5006d6df26a6ca6115 m_delegateMethods.webViewRequestStorageAccessPanelUnderFirstPartyCompletionHandler = [delegate respondsToSelector:@selector(_webView:requestStorageAccessPanelForDomain:underCurrentDomain:completionHandler:)]; m_delegateMethods.webViewRequestStorageAccessPanelForDomainUnderCurrentDomainForQuirkDomainsCompletionHandler = [delegate respondsToSelector:@selector(_webView:requestStorageAccessPanelForDomain:underCurrentDomain:forQuirkDomains:completionHandler:)]; m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(_webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:completionHandler:)]; -@@ -447,6 +448,15 @@ void UIDelegate::UIClient::runJavaScriptPrompt(WebPageProxy& page, const WTF::St +@@ -449,6 +450,15 @@ void UIDelegate::UIClient::runJavaScriptPrompt(WebPageProxy& page, const WTF::St }).get()]; } @@ -11997,10 +12040,10 @@ index 3ac9df925437afb3b4d37107fc6c02dc9ee41fa6..eb0dc8703860ef5006d6df26a6ca6115 { if (!m_uiDelegate) diff --git a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm -index 4e64905585de45ffa75c2d5b2ff2c3748fa15d15..342549d7c38432d9304ebd0bc1b55e4154f54948 100644 +index 7716347a00ce1829a464a8b9da072bc783e94a3b..ebac53978038e95870823b16440816c86e195a01 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm -@@ -38,6 +38,7 @@ +@@ -39,6 +39,7 @@ #import "LoadParameters.h" #import "MessageSenderInlines.h" #import "PageClient.h" @@ -12008,7 +12051,7 @@ index 4e64905585de45ffa75c2d5b2ff2c3748fa15d15..342549d7c38432d9304ebd0bc1b55e41 #import "PlaybackSessionManagerProxy.h" #import "QuickLookThumbnailLoader.h" #import "RemoteLayerTreeTransaction.h" -@@ -298,10 +299,84 @@ bool WebPageProxy::scrollingUpdatesDisabledForTesting() +@@ -299,10 +300,84 @@ bool WebPageProxy::scrollingUpdatesDisabledForTesting() void WebPageProxy::startDrag(const DragItem& dragItem, ShareableBitmap::Handle&& dragImageHandle) { @@ -12095,7 +12138,7 @@ index 4e64905585de45ffa75c2d5b2ff2c3748fa15d15..342549d7c38432d9304ebd0bc1b55e41 #if ENABLE(ATTACHMENT_ELEMENT) diff --git a/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm -index 36b1489cbe2de1698e6baebef5dc8f8e6d1f3acd..bdffd070443436f026b987aa86d1c22e61463dbf 100644 +index 9bfeb3569a88ea4c98f9948aa5a0229a4c3c9f35..44664d0a9a454e7d63d34b3603cf1f8a12c43548 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm @@ -434,7 +434,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END @@ -15952,10 +15995,10 @@ index 0000000000000000000000000000000000000000..e7a3dcc533294bb6e12f65d79b5b716b + +#endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp b/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp -index c5df687a8093a3ee9bdddbecb69629584c197012..f09af9804e7a587a3c1a444317ba1a16ca52fb7f 100644 +index be2380f8940e72d217687fb12f30f5d8dd3fc242..0f8eede082cb1d034fdb84d1630736dc31d732cf 100644 --- a/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp +++ b/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp -@@ -188,6 +188,13 @@ void ProcessLauncher::launchProcess() +@@ -197,6 +197,13 @@ void ProcessLauncher::launchProcess() nargs++; } #endif @@ -15969,7 +16012,7 @@ index c5df687a8093a3ee9bdddbecb69629584c197012..f09af9804e7a587a3c1a444317ba1a16 char** argv = g_newa(char*, nargs); unsigned i = 0; -@@ -203,6 +210,10 @@ void ProcessLauncher::launchProcess() +@@ -213,6 +220,10 @@ void ProcessLauncher::launchProcess() if (configureJSCForTesting) argv[i++] = const_cast("--configure-jsc-for-testing"); #endif @@ -16008,7 +16051,7 @@ index fac881d7c3d44758591d7a9f392a3992ce9f9a72..35eba5a0b31fc6e2d6e5c05c9f866c03 BOOL result = ::CreateProcess(0, commandLine.data(), 0, 0, true, 0, 0, 0, &startupInfo, &processInformation); diff --git a/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp b/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp -index 919dede3e9653f6e16d6772a90a023223c0845a9..8e0a214d02468c900dedc803204d4704b4aa6d12 100644 +index a2e5f12221607f68257a2bea47442decc4f3bba7..5c5330abce8839d9ebb0f90853199094458b9f5a 100644 --- a/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp +++ b/Source/WebKit/UIProcess/Media/RemoteMediaSessionCoordinatorProxy.cpp @@ -30,6 +30,7 @@ @@ -16020,7 +16063,7 @@ index 919dede3e9653f6e16d6772a90a023223c0845a9..8e0a214d02468c900dedc803204d4704 #include "RemoteMediaSessionCoordinatorProxyMessages.h" #include "WebPageProxy.h" diff --git a/Source/WebKit/UIProcess/PageClient.h b/Source/WebKit/UIProcess/PageClient.h -index cc9598fc619fb1bf3f4070d52a7587e209d4a147..58d92b530a60ce327cad1b47566bbb57450e955e 100644 +index 46a5ef93ae5d51029f3d037f9f0917f315c6386a..a52ad99dd1cb9401d42cbe8f06d9ecf0c65b5778 100644 --- a/Source/WebKit/UIProcess/PageClient.h +++ b/Source/WebKit/UIProcess/PageClient.h @@ -72,6 +72,11 @@ @@ -16048,7 +16091,7 @@ index cc9598fc619fb1bf3f4070d52a7587e209d4a147..58d92b530a60ce327cad1b47566bbb57 namespace WebKit { class PageClient; } -@@ -373,7 +384,20 @@ public: +@@ -374,7 +385,20 @@ public: virtual void selectionDidChange() = 0; #endif @@ -16594,7 +16637,7 @@ index e2ded3c65b9680346be2534a3e970e2f425a83a8..b0c006c1dcbfae4b33530f8eae04f986 WebPageProxy* page() const { return m_page.get(); } diff --git a/Source/WebKit/UIProcess/WebFrameProxy.cpp b/Source/WebKit/UIProcess/WebFrameProxy.cpp -index 21a9f2d213db48f58881d0873cfd17ce962080b8..7932e3de12dc1af7f3aad4a9cdbb8bf0004a67ba 100644 +index e0b661cc8a30d93db78ab5763cedb178d1507c51..663a0bb5984163a95f059dfb6cba0f9453f80222 100644 --- a/Source/WebKit/UIProcess/WebFrameProxy.cpp +++ b/Source/WebKit/UIProcess/WebFrameProxy.cpp @@ -31,6 +31,7 @@ @@ -16882,7 +16925,7 @@ index 0000000000000000000000000000000000000000..b70bfe0411571f4d181a7fae3186aaae +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/WebPageInspectorInputAgent.cpp b/Source/WebKit/UIProcess/WebPageInspectorInputAgent.cpp new file mode 100644 -index 0000000000000000000000000000000000000000..247c1817e85e5935eacef5e5ecdc07737dbf4a85 +index 0000000000000000000000000000000000000000..447af997796cf74a2c6259f16aad4da5581254f9 --- /dev/null +++ b/Source/WebKit/UIProcess/WebPageInspectorInputAgent.cpp @@ -0,0 +1,394 @@ @@ -17241,7 +17284,7 @@ index 0000000000000000000000000000000000000000..247c1817e85e5935eacef5e5ecdc0773 + touchPoints.append(WebPlatformTouchPoint(id, state, position, position, radius, rotationAngle, force)); + } + -+ WebTouchEvent touchEvent({WebEventType::TouchStart, eventModifiers, WallTime::now()}, WTFMove(touchPoints)); ++ WebTouchEvent touchEvent({WebEventType::TouchStart, eventModifiers, WallTime::now()}, WTFMove(touchPoints), {}, {}); + m_page.legacyMainFrameProcess().sendWithAsyncReply(Messages::WebPage::TouchEvent(touchEvent), [callback] (std::optional eventType, bool) { + if (!eventType) { + callback->sendFailure("Failed to dispatch touch event."_s); @@ -17374,7 +17417,7 @@ index 0000000000000000000000000000000000000000..26a2a3c0791c334f811ec99a630314f8 + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp -index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14e4716584 100644 +index 4dad8418faa1a7fcc734d5813f3a9b550a8fba21..c598c65d2ef9e6421611f5e56362dcd9b455598a 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.cpp +++ b/Source/WebKit/UIProcess/WebPageProxy.cpp @@ -190,12 +190,14 @@ @@ -17446,7 +17489,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } void WebPageProxy::addAllMessageReceivers() -@@ -1414,6 +1429,7 @@ void WebPageProxy::finishAttachingToWebProcess(ProcessLaunchReason reason) +@@ -1408,6 +1423,7 @@ void WebPageProxy::finishAttachingToWebProcess(ProcessLaunchReason reason) protectedPageClient()->didRelaunchProcess(); internals().pageLoadState.didSwapWebProcesses(); @@ -17454,7 +17497,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } void WebPageProxy::didAttachToRunningProcess() -@@ -1422,7 +1438,7 @@ void WebPageProxy::didAttachToRunningProcess() +@@ -1416,7 +1432,7 @@ void WebPageProxy::didAttachToRunningProcess() #if ENABLE(FULLSCREEN_API) ASSERT(!m_fullScreenManager); @@ -17463,7 +17506,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 #endif #if ENABLE(VIDEO_PRESENTATION_MODE) ASSERT(!m_playbackSessionManager); -@@ -1823,6 +1839,21 @@ WebProcessProxy& WebPageProxy::ensureRunningProcess() +@@ -1817,6 +1833,21 @@ WebProcessProxy& WebPageProxy::ensureRunningProcess() return m_legacyMainFrameProcess; } @@ -17482,10 +17525,10 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 + return navigation; +} + - RefPtr WebPageProxy::loadRequest(ResourceRequest&& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData) + RefPtr WebPageProxy::loadRequest(ResourceRequest&& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData, IsPerformingHTTPFallback isPerformingHTTPFallback) { if (m_isClosed) -@@ -2422,6 +2453,61 @@ void WebPageProxy::setControlledByAutomation(bool controlled) +@@ -2418,6 +2449,61 @@ void WebPageProxy::setControlledByAutomation(bool controlled) websiteDataStore().protectedNetworkProcess()->send(Messages::NetworkProcess::SetSessionIsControlledByAutomation(m_websiteDataStore->sessionID(), m_controlledByAutomation), 0); } @@ -17547,7 +17590,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 void WebPageProxy::createInspectorTarget(IPC::Connection& connection, const String& targetId, Inspector::InspectorTargetType type) { MESSAGE_CHECK_BASE(!targetId.isEmpty(), &connection); -@@ -2663,6 +2749,24 @@ void WebPageProxy::updateActivityState(OptionSet flagsToUpdate) +@@ -2659,6 +2745,24 @@ void WebPageProxy::updateActivityState(OptionSet flagsToUpdate) bool wasVisible = isViewVisible(); Ref pageClient = this->pageClient(); internals().activityState.remove(flagsToUpdate); @@ -17572,7 +17615,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 if (flagsToUpdate & ActivityState::IsFocused && pageClient->isViewFocused()) internals().activityState.add(ActivityState::IsFocused); if (flagsToUpdate & ActivityState::WindowIsActive && pageClient->isViewWindowActive()) -@@ -3401,7 +3505,7 @@ void WebPageProxy::performDragOperation(DragData& dragData, const String& dragSt +@@ -3397,7 +3501,7 @@ void WebPageProxy::performDragOperation(DragData& dragData, const String& dragSt grantAccessToCurrentPasteboardData(dragStorageName); #endif @@ -17581,7 +17624,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 performDragControllerAction(DragControllerAction::PerformDragOperation, dragData); #else if (!hasRunningProcess()) -@@ -3418,6 +3522,8 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag +@@ -3414,6 +3518,8 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag if (!hasRunningProcess()) return; @@ -17590,7 +17633,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 auto completionHandler = [this, protectedThis = Ref { *this }, action, dragData] (std::optional dragOperation, WebCore::DragHandlingMethod dragHandlingMethod, bool mouseIsOverFileInput, unsigned numberOfItemsToBeAccepted, const IntRect& insertionRect, const IntRect& editableElementRect, std::optional remoteUserInputEventData) mutable { if (!remoteUserInputEventData) { didPerformDragControllerAction(dragOperation, dragHandlingMethod, mouseIsOverFileInput, numberOfItemsToBeAccepted, insertionRect, editableElementRect); -@@ -3427,7 +3533,7 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag +@@ -3423,7 +3529,7 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag performDragControllerAction(action, dragData, remoteUserInputEventData->targetFrameID); }; @@ -17599,7 +17642,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 UNUSED_PARAM(frameID); String url = dragData.asURL(); if (!url.isEmpty()) -@@ -3449,14 +3555,34 @@ void WebPageProxy::didPerformDragControllerAction(std::optionaldidPerformDragControllerAction(); @@ -17637,7 +17680,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 didStartDrag(); } #endif -@@ -3477,6 +3603,24 @@ void WebPageProxy::dragEnded(const IntPoint& clientPosition, const IntPoint& glo +@@ -3473,6 +3599,24 @@ void WebPageProxy::dragEnded(const IntPoint& clientPosition, const IntPoint& glo setDragCaretRect({ }); } @@ -17662,7 +17705,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 void WebPageProxy::didStartDrag() { if (!hasRunningProcess()) -@@ -3484,6 +3628,16 @@ void WebPageProxy::didStartDrag() +@@ -3480,6 +3624,16 @@ void WebPageProxy::didStartDrag() discardQueuedMouseEvents(); send(Messages::WebPage::DidStartDrag()); @@ -17679,7 +17722,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } void WebPageProxy::dragCancelled() -@@ -3622,16 +3776,37 @@ void WebPageProxy::processNextQueuedMouseEvent() +@@ -3622,26 +3776,47 @@ void WebPageProxy::processNextQueuedMouseEvent() process->startResponsivenessTimer(); } @@ -17697,10 +17740,26 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 + sandboxExtensions = SandboxExtension::createHandlesForMachLookup({ "com.apple.iconservices"_s, "com.apple.iconservices.store"_s }, process->auditToken(), SandboxExtension::MachBootstrapOptions::EnableMachBootstrap); #endif -- LOG_WITH_STREAM(MouseHandling, stream << "UIProcess: sent mouse event " << eventType << " (queue size " << internals().mouseEventQueue.size() << ")"); -- sendMouseEvent(m_mainFrame->frameID(), event, WTFMove(sandboxExtensions)); -+ LOG_WITH_STREAM(MouseHandling, stream << "UIProcess: sent mouse event " << eventType << " (queue size " << internals().mouseEventQueue.size() << ")"); -+ sendMouseEvent(m_mainFrame->frameID(), event, WTFMove(sandboxExtensions)); +- auto eventWithCoalescedEvents = event; ++ auto eventWithCoalescedEvents = event; + +- if (event.type() == WebEventType::MouseMove) { +- internals().coalescedMouseEvents.append(event); +- eventWithCoalescedEvents.setCoalescedEvents(internals().coalescedMouseEvents); +- } ++ if (event.type() == WebEventType::MouseMove) { ++ internals().coalescedMouseEvents.append(event); ++ eventWithCoalescedEvents.setCoalescedEvents(internals().coalescedMouseEvents); ++ } + +- LOG_WITH_STREAM(MouseHandling, stream << "UIProcess: sent mouse event " << eventType << " (queue size " << internals().mouseEventQueue.size() << ", coalesced events size " << internals().coalescedMouseEvents.size() << ")"); ++ LOG_WITH_STREAM(MouseHandling, stream << "UIProcess: sent mouse event " << eventType << " (queue size " << internals().mouseEventQueue.size() << ", coalesced events size " << internals().coalescedMouseEvents.size() << ")"); + +- sendMouseEvent(m_mainFrame->frameID(), eventWithCoalescedEvents, WTFMove(sandboxExtensions)); ++ sendMouseEvent(m_mainFrame->frameID(), eventWithCoalescedEvents, WTFMove(sandboxExtensions)); + +- internals().coalescedMouseEvents.clear(); ++ internals().coalescedMouseEvents.clear(); + } else { +#if PLATFORM(WIN) || PLATFORM(COCOA) + DragData dragData(*m_dragSelectionData, event.position(), event.globalPosition(), m_dragSourceOperationMask); @@ -17723,7 +17782,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } void WebPageProxy::doAfterProcessingAllPendingMouseEvents(WTF::Function&& action) -@@ -3802,6 +3977,8 @@ void WebPageProxy::wheelEventHandlingCompleted(bool wasHandled) +@@ -3812,6 +3987,8 @@ void WebPageProxy::wheelEventHandlingCompleted(bool wasHandled) if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->wheelEventsFlushedForPage(*this); @@ -17732,7 +17791,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } void WebPageProxy::cacheWheelEventScrollingAccelerationCurve(const NativeWebWheelEvent& nativeWheelEvent) -@@ -3940,7 +4117,7 @@ static TrackingType mergeTrackingTypes(TrackingType a, TrackingType b) +@@ -3950,7 +4127,7 @@ static TrackingType mergeTrackingTypes(TrackingType a, TrackingType b) void WebPageProxy::updateTouchEventTracking(const WebTouchEvent& touchStartEvent) { @@ -17741,7 +17800,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 for (auto& touchPoint : touchStartEvent.touchPoints()) { auto location = touchPoint.location(); auto update = [this, location](TrackingType& trackingType, EventTrackingRegions::EventType eventType) { -@@ -4547,6 +4724,7 @@ void WebPageProxy::receivedNavigationActionPolicyDecision(WebProcessProxy& proce +@@ -4557,6 +4734,7 @@ void WebPageProxy::receivedNavigationActionPolicyDecision(WebProcessProxy& proce void WebPageProxy::receivedPolicyDecision(PolicyAction action, API::Navigation* navigation, RefPtr&& websitePolicies, Ref&& navigationAction, WillContinueLoadInNewProcess willContinueLoadInNewProcess, std::optional sandboxExtensionHandle, std::optional&& consoleMessage, CompletionHandler&& completionHandler) { @@ -17749,7 +17808,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 if (!hasRunningProcess()) return completionHandler(PolicyDecision { }); -@@ -5478,6 +5656,12 @@ void WebPageProxy::pageScaleFactorDidChange(IPC::Connection& connection, double +@@ -5488,6 +5666,12 @@ void WebPageProxy::pageScaleFactorDidChange(IPC::Connection& connection, double m_pageScaleFactor = scaleFactor; } @@ -17762,7 +17821,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 void WebPageProxy::pluginScaleFactorDidChange(IPC::Connection& connection, double pluginScaleFactor) { MESSAGE_CHECK_BASE(scaleFactorIsValid(pluginScaleFactor), &connection); -@@ -6033,6 +6217,7 @@ void WebPageProxy::didDestroyNavigationShared(Ref&& process, ui +@@ -6063,6 +6247,7 @@ void WebPageProxy::didDestroyNavigationShared(Ref&& process, ui Ref protectedPageClient { pageClient() }; m_navigationState->didDestroyNavigation(process->coreProcessIdentifier(), navigationID); @@ -17770,7 +17829,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } void WebPageProxy::didStartProvisionalLoadForFrame(FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, uint64_t navigationID, URL&& url, URL&& unreachableURL, const UserData& userData) -@@ -6300,6 +6485,8 @@ void WebPageProxy::didFailProvisionalLoadForFrameShared(Ref&& p +@@ -6367,6 +6552,8 @@ void WebPageProxy::didFailProvisionalLoadForFrameShared(Ref&& p m_failingProvisionalLoadURL = { }; @@ -17779,23 +17838,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 // If the provisional page's load fails then we destroy the provisional page. if (m_provisionalPage && m_provisionalPage->mainFrame() == &frame && willContinueLoading == WillContinueLoading::No) m_provisionalPage = nullptr; -@@ -6967,7 +7154,14 @@ void WebPageProxy::beginSafeBrowsingCheck(const URL&, bool, WebFramePolicyListen - - void WebPageProxy::decidePolicyForNavigationActionAsync(NavigationActionData&& data, CompletionHandler&& completionHandler) - { -- decidePolicyForNavigationActionAsyncShared(protectedLegacyMainFrameProcess(), WTFMove(data), WTFMove(completionHandler)); -+ if (m_inspectorController->shouldPauseLoading()) { -+ decidePolicyForNavigationActionAsyncShared(protectedLegacyMainFrameProcess(), WTFMove(data), WTFMove(completionHandler)); -+ m_inspectorController->setContinueLoadingCallback([this, protectedThis = Ref { *this }, data = WTFMove(data), completionHandler = WTFMove(completionHandler)] () mutable { -+ decidePolicyForNavigationActionAsyncShared(protectedLegacyMainFrameProcess(), WTFMove(data), WTFMove(completionHandler)); -+ }); -+ } else { -+ decidePolicyForNavigationActionAsyncShared(protectedLegacyMainFrameProcess(), WTFMove(data), WTFMove(completionHandler)); -+ } - } - - void WebPageProxy::decidePolicyForNavigationActionAsyncShared(Ref&& process, NavigationActionData&& data, CompletionHandler&& completionHandler) -@@ -7631,6 +7825,7 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w +@@ -7692,6 +7879,7 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w if (RefPtr page = originatingFrameInfo->page()) openerAppInitiatedState = page->lastNavigationWasAppInitiated(); @@ -17803,7 +17846,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 auto completionHandler = [ this, protectedThis = Ref { *this }, -@@ -7703,6 +7898,7 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w +@@ -7764,6 +7952,7 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w void WebPageProxy::showPage() { m_uiClient->showPage(this); @@ -17811,7 +17854,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } bool WebPageProxy::hasOpenedPage() const -@@ -7813,6 +8009,10 @@ void WebPageProxy::closePage() +@@ -7874,6 +8063,10 @@ void WebPageProxy::closePage() if (isClosed()) return; @@ -17822,7 +17865,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 WEBPAGEPROXY_RELEASE_LOG(Process, "closePage:"); protectedPageClient()->clearAllEditCommands(); m_uiClient->close(this); -@@ -7849,6 +8049,8 @@ void WebPageProxy::runJavaScriptAlert(IPC::Connection& connection, FrameIdentifi +@@ -7910,6 +8103,8 @@ void WebPageProxy::runJavaScriptAlert(IPC::Connection& connection, FrameIdentifi } runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), message, [reply = WTFMove(reply)](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler&& completion) mutable { @@ -17831,7 +17874,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 page.m_uiClient->runJavaScriptAlert(page, message, frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)]() mutable { reply(); completion(); -@@ -7870,6 +8072,8 @@ void WebPageProxy::runJavaScriptConfirm(IPC::Connection& connection, FrameIdenti +@@ -7931,6 +8126,8 @@ void WebPageProxy::runJavaScriptConfirm(IPC::Connection& connection, FrameIdenti if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->willShowJavaScriptDialog(*this); } @@ -17840,7 +17883,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), message, [reply = WTFMove(reply)](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler&& completion) mutable { page.m_uiClient->runJavaScriptConfirm(page, message, frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)](bool result) mutable { -@@ -7893,6 +8097,8 @@ void WebPageProxy::runJavaScriptPrompt(IPC::Connection& connection, FrameIdentif +@@ -7954,6 +8151,8 @@ void WebPageProxy::runJavaScriptPrompt(IPC::Connection& connection, FrameIdentif if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->willShowJavaScriptDialog(*this); } @@ -17849,7 +17892,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), message, [reply = WTFMove(reply), defaultValue](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler&& completion) mutable { page.m_uiClient->runJavaScriptPrompt(page, message, defaultValue, frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)](auto& result) mutable { -@@ -8009,6 +8215,8 @@ void WebPageProxy::runBeforeUnloadConfirmPanel(IPC::Connection& connection, Fram +@@ -8070,6 +8269,8 @@ void WebPageProxy::runBeforeUnloadConfirmPanel(IPC::Connection& connection, Fram return; } } @@ -17858,7 +17901,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 // Since runBeforeUnloadConfirmPanel() can spin a nested run loop we need to turn off the responsiveness timer and the tryClose timer. protectedLegacyMainFrameProcess()->stopResponsivenessTimer(); -@@ -8501,6 +8709,11 @@ void WebPageProxy::resourceLoadDidCompleteWithError(ResourceLoadInfo&& loadInfo, +@@ -8564,6 +8765,11 @@ void WebPageProxy::resourceLoadDidCompleteWithError(ResourceLoadInfo&& loadInfo, } #if ENABLE(FULLSCREEN_API) @@ -17870,7 +17913,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 WebFullScreenManagerProxy* WebPageProxy::fullScreenManager() { return m_fullScreenManager.get(); -@@ -8606,6 +8819,17 @@ void WebPageProxy::requestDOMPasteAccess(DOMPasteAccessCategory pasteAccessCateg +@@ -8669,6 +8875,17 @@ void WebPageProxy::requestDOMPasteAccess(DOMPasteAccessCategory pasteAccessCateg } } @@ -17888,7 +17931,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 m_pageClient->requestDOMPasteAccess(pasteAccessCategory, requiresInteraction, elementRect, originIdentifier, WTFMove(completionHandler)); } -@@ -9461,6 +9685,8 @@ void WebPageProxy::mouseEventHandlingCompleted(std::optional event +@@ -9550,6 +9767,8 @@ void WebPageProxy::mouseEventHandlingCompleted(std::optional event if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->mouseEventsFlushedForPage(*this); didFinishProcessingAllPendingMouseEvents(); @@ -17897,7 +17940,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } } -@@ -9495,6 +9721,7 @@ void WebPageProxy::keyEventHandlingCompleted(std::optional eventTy +@@ -9584,6 +9803,7 @@ void WebPageProxy::keyEventHandlingCompleted(std::optional eventTy if (!canProcessMoreKeyEvents) { if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->keyboardEventsFlushedForPage(*this); @@ -17905,7 +17948,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 } } -@@ -9906,7 +10133,10 @@ void WebPageProxy::dispatchProcessDidTerminate(ProcessTerminationReason reason) +@@ -10000,7 +10220,10 @@ void WebPageProxy::dispatchProcessDidTerminate(ProcessTerminationReason reason) { WEBPAGEPROXY_RELEASE_LOG_ERROR(Loading, "dispatchProcessDidTerminate: reason=%" PUBLIC_LOG_STRING, processTerminationReasonToString(reason).characters()); @@ -17917,7 +17960,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 if (m_loaderClient) handledByClient = reason != ProcessTerminationReason::RequestedByClient && m_loaderClient->processDidCrash(*this); else -@@ -10292,6 +10522,7 @@ bool WebPageProxy::useGPUProcessForDOMRenderingEnabled() const +@@ -10390,6 +10613,7 @@ bool WebPageProxy::useGPUProcessForDOMRenderingEnabled() const WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& process, DrawingAreaProxy& drawingArea, std::optional&& remotePageParameters, bool isProcessSwap, RefPtr&& websitePolicies, std::optional&& mainFrameIdentifier) { @@ -17925,7 +17968,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 WebPageCreationParameters parameters; parameters.processDisplayName = configuration().processDisplayName(); -@@ -10516,6 +10747,8 @@ WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& proc +@@ -10618,6 +10842,8 @@ WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& proc parameters.httpsUpgradeEnabled = preferences().upgradeKnownHostsToHTTPSEnabled() ? m_configuration->httpsUpgradeEnabled() : false; @@ -17934,7 +17977,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 #if PLATFORM(IOS) || PLATFORM(VISION) // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. parameters.allowsDeprecatedSynchronousXMLHttpRequestDuringUnload = allowsDeprecatedSynchronousXMLHttpRequestDuringUnload(); -@@ -10648,8 +10881,42 @@ void WebPageProxy::gamepadsRecentlyAccessed() +@@ -10778,8 +11004,42 @@ void WebPageProxy::allowGamepadAccess() #endif // ENABLE(GAMEPAD) @@ -17977,7 +18020,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 if (negotiatedLegacyTLS == NegotiatedLegacyTLS::Yes) { m_navigationClient->shouldAllowLegacyTLS(*this, authenticationChallenge.get(), [this, protectedThis = Ref { *this }, authenticationChallenge] (bool shouldAllowLegacyTLS) { if (shouldAllowLegacyTLS) -@@ -10744,6 +11011,12 @@ void WebPageProxy::requestGeolocationPermissionForFrame(IPC::Connection& connect +@@ -10874,6 +11134,12 @@ void WebPageProxy::requestGeolocationPermissionForFrame(IPC::Connection& connect request->deny(); }; @@ -17990,7 +18033,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 // FIXME: Once iOS migrates to the new WKUIDelegate SPI, clean this up // and make it one UIClient call that calls the completionHandler with false // if there is no delegate instead of returning the completionHandler -@@ -10806,6 +11079,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi +@@ -10936,6 +11202,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi shouldChangeDeniedToPrompt = false; if (sessionID().isEphemeral()) { @@ -18003,7 +18046,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 completionHandler(shouldChangeDeniedToPrompt ? PermissionState::Prompt : PermissionState::Denied); return; } -@@ -10820,6 +11099,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi +@@ -10950,6 +11222,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi return; } @@ -18017,7 +18060,7 @@ index f96981fa78e5d08faeaa261cf4ccb64e1c9241f8..a112a30315ed7708ea140c876d10ce14 completionHandler(shouldChangeDeniedToPrompt ? PermissionState::Prompt : PermissionState::Denied); return; diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h -index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237a938de91 100644 +index f0894c9608a72aeb87c5f5e76db60c2d740a7706..5d3b23739725b914f7eabfa6fdf62eb50a43950b 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.h +++ b/Source/WebKit/UIProcess/WebPageProxy.h @@ -26,6 +26,7 @@ @@ -18057,7 +18100,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 class FloatRect; class FloatSize; class FontAttributeChanges; -@@ -446,6 +462,7 @@ class WebExtensionController; +@@ -448,6 +464,7 @@ class WebExtensionController; class WebFramePolicyListenerProxy; class WebFrameProxy; class WebFullScreenManagerProxy; @@ -18065,7 +18108,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 class WebInspectorUIProxy; class WebKeyboardEvent; class WebMouseEvent; -@@ -674,6 +691,8 @@ public: +@@ -669,6 +686,8 @@ public: void setControlledByAutomation(bool); WebPageInspectorController& inspectorController() { return *m_inspectorController; } @@ -18074,7 +18117,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 #if PLATFORM(IOS_FAMILY) void showInspectorIndication(); -@@ -707,6 +726,7 @@ public: +@@ -702,6 +721,7 @@ public: bool hasSleepDisabler() const; #if ENABLE(FULLSCREEN_API) @@ -18082,7 +18125,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 WebFullScreenManagerProxy* fullScreenManager(); API::FullscreenClient& fullscreenClient() const { return *m_fullscreenClient; } -@@ -795,6 +815,12 @@ public: +@@ -790,6 +810,12 @@ public: void setPageLoadStateObserver(std::unique_ptr&&); @@ -18095,15 +18138,15 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 void initializeWebPage(); void setDrawingArea(std::unique_ptr&&); -@@ -821,6 +847,7 @@ public: +@@ -817,6 +843,7 @@ public: void addPlatformLoadParameters(WebProcessProxy&, LoadParameters&); - RefPtr loadRequest(WebCore::ResourceRequest&&); - RefPtr loadRequest(WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, API::Object* userData = nullptr); + RefPtr loadRequest(WebCore::ResourceRequest&&, IsPerformingHTTPFallback = IsPerformingHTTPFallback::No); + RefPtr loadRequest(WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, API::Object* userData = nullptr, IsPerformingHTTPFallback = IsPerformingHTTPFallback::No); + RefPtr loadRequestForInspector(WebCore::ResourceRequest&&, WebFrameProxy*); RefPtr loadFile(const String& fileURL, const String& resourceDirectoryURL, bool isAppInitiated = true, API::Object* userData = nullptr); RefPtr loadData(std::span, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData = nullptr); RefPtr loadData(std::span, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, WebCore::ShouldOpenExternalURLsPolicy); -@@ -888,6 +915,7 @@ public: +@@ -884,6 +911,7 @@ public: PageClient& pageClient() const; Ref protectedPageClient() const; RefPtr optionalProtectedPageClient() const; @@ -18111,7 +18154,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 void setViewNeedsDisplay(const WebCore::Region&); void requestScroll(const WebCore::FloatPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin, WebCore::ScrollIsAnimated); -@@ -1414,6 +1442,7 @@ public: +@@ -1412,6 +1440,7 @@ public: #endif void pageScaleFactorDidChange(IPC::Connection&, double); @@ -18119,7 +18162,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 void pluginScaleFactorDidChange(IPC::Connection&, double); void pluginZoomFactorDidChange(IPC::Connection&, double); -@@ -1498,14 +1527,20 @@ public: +@@ -1496,14 +1525,20 @@ public: void didStartDrag(); void dragCancelled(); void setDragCaretRect(const WebCore::IntRect&); @@ -18141,7 +18184,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 #endif void processDidBecomeUnresponsive(); -@@ -1739,6 +1774,7 @@ public: +@@ -1737,6 +1772,7 @@ public: void setViewportSizeForCSSViewportUnits(const WebCore::FloatSize&); WebCore::FloatSize viewportSizeForCSSViewportUnits() const; @@ -18149,16 +18192,17 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 void didReceiveAuthenticationChallengeProxy(Ref&&, NegotiatedLegacyTLS); void negotiatedLegacyTLS(); void didNegotiateModernTLS(const URL&); -@@ -1771,6 +1807,8 @@ public: - +@@ -1770,6 +1806,9 @@ public: #if PLATFORM(COCOA) || PLATFORM(GTK) RefPtr takeViewSnapshot(std::optional&&); + RefPtr takeViewSnapshot(std::optional&&, ForceSoftwareCapturingViewportSnapshot); +#elif PLATFORM(WPE) + RefPtr takeViewSnapshot(std::optional&&) { return nullptr; } ++ RefPtr takeViewSnapshot(std::optional&&, ForceSoftwareCapturingViewportSnapshot) { return nullptr; } #endif void wrapCryptoKey(Vector&&, CompletionHandler>&&)>&&); -@@ -2672,6 +2710,7 @@ private: +@@ -2676,6 +2715,7 @@ private: RefPtr launchProcessForReload(); void requestNotificationPermission(const String& originString, CompletionHandler&&); @@ -18166,7 +18210,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 void didChangeContentSize(const WebCore::IntSize&); void didChangeIntrinsicContentSize(const WebCore::IntSize&); -@@ -3191,8 +3230,10 @@ private: +@@ -3196,8 +3236,10 @@ private: String m_overrideContentSecurityPolicy; RefPtr m_inspector; @@ -18177,7 +18221,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 std::unique_ptr m_fullScreenManager; std::unique_ptr m_fullscreenClient; #endif -@@ -3384,6 +3425,22 @@ private: +@@ -3389,6 +3431,22 @@ private: std::optional m_currentDragOperation; bool m_currentDragIsOverFileInput { false }; unsigned m_currentDragNumberOfFilesToBeAccepted { 0 }; @@ -18200,7 +18244,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 #endif bool m_mainFrameHasHorizontalScrollbar { false }; -@@ -3555,6 +3612,10 @@ private: +@@ -3560,6 +3618,10 @@ private: RefPtr messageBody; }; Vector m_pendingInjectedBundleMessages; @@ -18212,7 +18256,7 @@ index 1c4d43a5025741a37708e99c398f03836de80182..df2e31590e9936525cb5037ea4456237 #if PLATFORM(IOS_FAMILY) && ENABLE(DEVICE_ORIENTATION) std::unique_ptr m_webDeviceOrientationUpdateProviderProxy; diff --git a/Source/WebKit/UIProcess/WebPageProxy.messages.in b/Source/WebKit/UIProcess/WebPageProxy.messages.in -index 488af1f4066de0b8adaa595cb2f4028fbce4b177..529a1b8d2f2b0dd9face3ce38cf68758ce381d2f 100644 +index 445e6ec6cef315615499ef3d80e7490e142e33b2..ac73b9c7ec5380a0922a6622f8b3ebc983665332 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.messages.in +++ b/Source/WebKit/UIProcess/WebPageProxy.messages.in @@ -29,6 +29,7 @@ messages -> WebPageProxy { @@ -18248,7 +18292,7 @@ index 488af1f4066de0b8adaa595cb2f4028fbce4b177..529a1b8d2f2b0dd9face3ce38cf68758 DidHandleDragStartRequest(bool started) DidHandleAdditionalDragItemsRequest(bool added) diff --git a/Source/WebKit/UIProcess/WebProcessCache.cpp b/Source/WebKit/UIProcess/WebProcessCache.cpp -index c909cd634d6acd72695de8372866691269ad6a04..ff5b37e3b4a17eab4bd3f8e9a2a6ef8460110052 100644 +index aaf99c896c32e6d886c1cc4d463a5979587f6d11..840d239c3eef0cbc020c98fdef57a2a57cddfd65 100644 --- a/Source/WebKit/UIProcess/WebProcessCache.cpp +++ b/Source/WebKit/UIProcess/WebProcessCache.cpp @@ -88,6 +88,10 @@ bool WebProcessCache::canCacheProcess(WebProcessProxy& process) const @@ -18263,7 +18307,7 @@ index c909cd634d6acd72695de8372866691269ad6a04..ff5b37e3b4a17eab4bd3f8e9a2a6ef84 } diff --git a/Source/WebKit/UIProcess/WebProcessPool.cpp b/Source/WebKit/UIProcess/WebProcessPool.cpp -index c3ff67230baf4dec44f5d7232a8351ec1f77b79c..3fed70aa8f568cd9a3a4fa460ed565658405f63f 100644 +index a2b18816d8f78c04ac44a72090ef7a20fa9c5082..20d02d9cce0bea10a8f026b60c5109eb56031e33 100644 --- a/Source/WebKit/UIProcess/WebProcessPool.cpp +++ b/Source/WebKit/UIProcess/WebProcessPool.cpp @@ -426,10 +426,10 @@ void WebProcessPool::setAutomationClient(std::unique_ptr& @@ -18291,7 +18335,7 @@ index c3ff67230baf4dec44f5d7232a8351ec1f77b79c..3fed70aa8f568cd9a3a4fa460ed56565 void WebProcessPool::fullKeyboardAccessModeChanged(bool fullKeyboardAccessEnabled) { -@@ -925,7 +926,7 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa +@@ -911,7 +912,7 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa #endif parameters.cacheModel = LegacyGlobalSettings::singleton().cacheModel(); @@ -18301,10 +18345,10 @@ index c3ff67230baf4dec44f5d7232a8351ec1f77b79c..3fed70aa8f568cd9a3a4fa460ed56565 parameters.urlSchemesRegisteredAsEmptyDocument = copyToVector(m_schemesToRegisterAsEmptyDocument); diff --git a/Source/WebKit/UIProcess/WebProcessProxy.cpp b/Source/WebKit/UIProcess/WebProcessProxy.cpp -index 9411228a77d83f24384a0b9cf57f44351b48d75e..3510caa0b246b9794977f4fc99efddc9de1aceee 100644 +index 1e5dbc576d4e79e5cab593ff9feba893784d9a26..b5fa55a4619cadd950d17ac8ff57b927ebbae98a 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.cpp +++ b/Source/WebKit/UIProcess/WebProcessProxy.cpp -@@ -189,6 +189,11 @@ Vector> WebProcessProxy::allProcesses() +@@ -185,6 +185,11 @@ Vector> WebProcessProxy::allProcesses() }); } @@ -18316,7 +18360,7 @@ index 9411228a77d83f24384a0b9cf57f44351b48d75e..3510caa0b246b9794977f4fc99efddc9 RefPtr WebProcessProxy::processForIdentifier(ProcessIdentifier identifier) { return allProcessMap().get(identifier); -@@ -562,6 +567,26 @@ void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOpt +@@ -541,6 +546,26 @@ void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOpt if (WebKit::isInspectorProcessPool(processPool())) launchOptions.extraInitializationData.add("inspector-process"_s, "1"_s); @@ -18344,7 +18388,7 @@ index 9411228a77d83f24384a0b9cf57f44351b48d75e..3510caa0b246b9794977f4fc99efddc9 if (isPrewarmed()) diff --git a/Source/WebKit/UIProcess/WebProcessProxy.h b/Source/WebKit/UIProcess/WebProcessProxy.h -index 7ac34410441b471cfe9bc3d9a96a7f082283813d..7a39d5f45d7b422330b8cd444ebe78cfa145c692 100644 +index b575feaff2252c1de2421038368bf22e818ff1c3..5927617a280bc1663896944e8fe57dacda21aa8e 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.h +++ b/Source/WebKit/UIProcess/WebProcessProxy.h @@ -178,6 +178,7 @@ public: @@ -18355,8 +18399,21 @@ index 7ac34410441b471cfe9bc3d9a96a7f082283813d..7a39d5f45d7b422330b8cd444ebe78cf void initializeWebProcess(WebProcessCreationParameters&&); +diff --git a/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm b/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm +index a36b8f78e0570ab44a9d908c52a75815433282f5..dde5ed6e8e54b2095ac77780a5f73233ca91ddbc 100644 +--- a/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm ++++ b/Source/WebKit/UIProcess/WebsiteData/Cocoa/WebsiteDataStoreCocoa.mm +@@ -228,7 +228,7 @@ std::optional WebsiteDataStore::useNetworkLoader() + + [[maybe_unused]] const auto isSafari = + #if PLATFORM(MAC) +- MacApplication::isSafari(); ++ WebCore::MacApplication::isSafari(); + #elif PLATFORM(IOS_FAMILY) + WebCore::IOSApplication::isMobileSafari() || WebCore::IOSApplication::isSafariViewService(); + #else diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp -index 3311783c3e193b7b3e6f4b1aeddc40560308859a..ceb002bdce785ac39ed86c4f809813c32148bfc8 100644 +index 47fd107d304bdea638572b4fa15f5e7aad7be221..060f1d96972861ec8a59ec9d857769942c1773b3 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp @@ -306,7 +306,8 @@ SOAuthorizationCoordinator& WebsiteDataStore::soAuthorizationCoordinator(const W @@ -18369,7 +18426,7 @@ index 3311783c3e193b7b3e6f4b1aeddc40560308859a..ceb002bdce785ac39ed86c4f809813c3 if (sessionID.isEphemeral()) { // Reuse a previous persistent session network process for ephemeral sessions. for (auto& dataStore : allDataStores().values()) { -@@ -2279,6 +2280,12 @@ void WebsiteDataStore::originDirectoryForTesting(WebCore::ClientOrigin&& origin, +@@ -2295,6 +2296,12 @@ void WebsiteDataStore::originDirectoryForTesting(WebCore::ClientOrigin&& origin, protectedNetworkProcess()->websiteDataOriginDirectoryForTesting(m_sessionID, WTFMove(origin), type, WTFMove(completionHandler)); } @@ -18383,7 +18440,7 @@ index 3311783c3e193b7b3e6f4b1aeddc40560308859a..ceb002bdce785ac39ed86c4f809813c3 void WebsiteDataStore::hasAppBoundSession(CompletionHandler&& completionHandler) const { diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h -index 165885137ca032390c6b55baacc30a8c4c423eb5..930ceeca56587636993f9a0c0e0a21d0ebab3835 100644 +index 849d9901ab75a80f2e919ec67c5c7b1de76e6bb4..85772ed50988e4b16cfbe975f8be725cb3b4cc6e 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h @@ -97,6 +97,7 @@ class DeviceIdHashSaltStorage; @@ -18444,7 +18501,7 @@ index 165885137ca032390c6b55baacc30a8c4c423eb5..930ceeca56587636993f9a0c0e0a21d0 void resetQuota(CompletionHandler&&); void resetStoragePersistedState(CompletionHandler&&); #if PLATFORM(IOS_FAMILY) -@@ -564,9 +582,11 @@ private: +@@ -568,9 +586,11 @@ private: WebCore::CurlProxySettings m_proxySettings; #endif @@ -18457,7 +18514,7 @@ index 165885137ca032390c6b55baacc30a8c4c423eb5..930ceeca56587636993f9a0c0e0a21d0 WebCore::SoupNetworkProxySettings m_networkProxySettings; String m_cookiePersistentStoragePath; SoupCookiePersistentStorageType m_cookiePersistentStorageType { SoupCookiePersistentStorageType::SQLite }; -@@ -593,6 +613,10 @@ private: +@@ -597,6 +617,10 @@ private: RefPtr m_cookieStore; RefPtr m_networkProcess; @@ -18806,17 +18863,17 @@ index b02c70d85fe1a93899640a8b909b0cf734d28b18..b1dc8e89eb265be81e083bf337109561 virtual void unrealize() { }; virtual int renderHostFileDescriptor() { return -1; } diff --git a/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.cpp b/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.cpp -index 66329bbc7f97ca577842dd93e28d4143b16e69dc..cd10bd6a29e64a1356e5a14a3a6339c4ca43327e 100644 +index 85f211458d14702ca123c7926fadff016e1e5c87..668209bc4d8e2673f1abb1793ccf6d553a8a7da7 100644 --- a/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.cpp +++ b/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.cpp -@@ -675,4 +675,30 @@ RendererBufferFormat AcceleratedBackingStoreDMABuf::bufferFormat() const +@@ -686,4 +686,30 @@ RendererBufferFormat AcceleratedBackingStoreDMABuf::bufferFormat() const return buffer ? buffer->format() : RendererBufferFormat { }; } +// Playwright begin +cairo_surface_t* AcceleratedBackingStoreDMABuf::surface() +{ -+ RefPtr buffer = m_renderer.buffer(); ++ RefPtr buffer = m_committedBuffer.get(); + if (!buffer) + return nullptr; + @@ -18841,10 +18898,10 @@ index 66329bbc7f97ca577842dd93e28d4143b16e69dc..cd10bd6a29e64a1356e5a14a3a6339c4 + } // namespace WebKit diff --git a/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.h b/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.h -index 68d4bb329c2e116fe720c98d3e38b6672c88e389..a4a36dd35ee9330f8b9edfb09f607a12dbac7c0b 100644 +index ddf0af2e1f27561386fc734a300c18261d249a2e..7272fabe8b10e4ec1ccabf68cb7ce3dc671cc11b 100644 --- a/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.h +++ b/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.h -@@ -90,6 +90,7 @@ private: +@@ -92,6 +92,7 @@ private: #else bool paint(cairo_t*, const WebCore::IntRect&) override; #endif @@ -18852,9 +18909,9 @@ index 68d4bb329c2e116fe720c98d3e38b6672c88e389..a4a36dd35ee9330f8b9edfb09f607a12 void unrealize() override; void update(const LayerTreeContext&) override; RendererBufferFormat bufferFormat() const override; -@@ -246,6 +247,9 @@ private: +@@ -240,6 +241,9 @@ private: RefPtr m_committedBuffer; - std::optional m_pendingDamageRegion; + WebCore::Region m_pendingDamageRegion; HashMap> m_buffers; +// Playwright begin + RefPtr m_flippedSurface; @@ -19152,7 +19209,7 @@ index 2a17b59c9be6ecc76b0ec0a16d9f4866dffa0bf4..0d5c58a88b0e5197254d0eb5bd6eee04 m_primarySelectionOwner = frame; } diff --git a/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm b/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm -index f5290e15ec912a36766509dd2414444a8c97e06d..2563926ff72e0b8bcb35e250141549a69578a52f 100644 +index 971ef8a8fccf3a4f3b9f40daece51ecd5e98dac8..67d19358c322c120ba40069af47695d089b0f90d 100644 --- a/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm +++ b/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm @@ -503,6 +503,8 @@ IntRect PageClientImpl::rootViewToAccessibilityScreen(const IntRect& rect) @@ -19374,7 +19431,7 @@ index 0000000000000000000000000000000000000000..721826c8c98fc85b68a4f45deaee69c1 + +#endif diff --git a/Source/WebKit/UIProcess/mac/PageClientImplMac.h b/Source/WebKit/UIProcess/mac/PageClientImplMac.h -index 4c99b44dbdd37e73c349ea8d5a3422075d2eff65..7c6dc33bf047706f13332c726a7e2364d834f7ae 100644 +index d41785036b86264bb2cecfdf6e9ee898caff55fa..4889dd4ddd0945760f457719e5b2917a2cd88ec7 100644 --- a/Source/WebKit/UIProcess/mac/PageClientImplMac.h +++ b/Source/WebKit/UIProcess/mac/PageClientImplMac.h @@ -54,6 +54,8 @@ class PageClientImpl final : public PageClientImplCocoa @@ -19394,9 +19451,9 @@ index 4c99b44dbdd37e73c349ea8d5a3422075d2eff65..7c6dc33bf047706f13332c726a7e2364 + RetainPtr takeSnapshotForAutomation() override; +// Paywright end RefPtr takeViewSnapshot(std::optional&&) override; + RefPtr takeViewSnapshot(std::optional&&, ForceSoftwareCapturingViewportSnapshot) override; void wheelEventWasNotHandledByWebCore(const NativeWebWheelEvent&) override; - #if ENABLE(MAC_GESTURE_EVENTS) -@@ -224,6 +229,10 @@ private: +@@ -225,6 +230,10 @@ private: void beganExitFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame) override; #endif @@ -19408,7 +19465,7 @@ index 4c99b44dbdd37e73c349ea8d5a3422075d2eff65..7c6dc33bf047706f13332c726a7e2364 void navigationGestureWillEnd(bool willNavigate, WebBackForwardListItem&) override; void navigationGestureDidEnd(bool willNavigate, WebBackForwardListItem&) override; diff --git a/Source/WebKit/UIProcess/mac/PageClientImplMac.mm b/Source/WebKit/UIProcess/mac/PageClientImplMac.mm -index 0116071f4359eac661cc6de71eee2d43ce2d56e0..c93c40e2064e15ccecf08a147f4189e98810a6d3 100644 +index f07db2bb97f3ef841743b320644d1abfe1550071..759cadff7141386874e931d212fbbcd3b2a4544c 100644 --- a/Source/WebKit/UIProcess/mac/PageClientImplMac.mm +++ b/Source/WebKit/UIProcess/mac/PageClientImplMac.mm @@ -110,6 +110,13 @@ namespace WebKit { @@ -19496,7 +19553,7 @@ index 0116071f4359eac661cc6de71eee2d43ce2d56e0..c93c40e2064e15ccecf08a147f4189e9 RefPtr PageClientImpl::takeViewSnapshot(std::optional&&) { return m_impl->takeViewSnapshot(); -@@ -829,6 +856,13 @@ void PageClientImpl::beganExitFullScreen(const IntRect& initialFrame, const IntR +@@ -834,6 +861,13 @@ void PageClientImpl::beganExitFullScreen(const IntRect& initialFrame, const IntR #endif // ENABLE(FULLSCREEN_API) @@ -19510,7 +19567,7 @@ index 0116071f4359eac661cc6de71eee2d43ce2d56e0..c93c40e2064e15ccecf08a147f4189e9 void PageClientImpl::navigationGestureDidBegin() { m_impl->dismissContentRelativeChildWindowsWithAnimation(true); -@@ -1017,6 +1051,9 @@ void PageClientImpl::requestScrollToRect(const WebCore::FloatRect& targetRect, c +@@ -1022,6 +1056,9 @@ void PageClientImpl::requestScrollToRect(const WebCore::FloatRect& targetRect, c bool PageClientImpl::windowIsFrontWindowUnderMouse(const NativeWebMouseEvent& event) { @@ -19538,18 +19595,6 @@ index 21c925bafb662dbe961baaad7f25bf4296236d76..5496a33c558a00a5ba96d10223e600aa } +#endif -diff --git a/Source/WebKit/UIProcess/mac/WKImmediateActionController.mm b/Source/WebKit/UIProcess/mac/WKImmediateActionController.mm -index 63c423a74cf983ab7a0be49f0376d227c49724e1..5818c786d5fb0fb4a60c549b68ec989656223e5c 100644 ---- a/Source/WebKit/UIProcess/mac/WKImmediateActionController.mm -+++ b/Source/WebKit/UIProcess/mac/WKImmediateActionController.mm -@@ -31,6 +31,7 @@ - #import "APIHitTestResult.h" - #import "MessageSenderInlines.h" - #import "WKNSURLExtras.h" -+#import "WebFrameProxy.h" - #import "WebPageMessages.h" - #import "WebPageProxy.h" - #import "WebPageProxyMessages.h" diff --git a/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.h b/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.h index e34faa8ae2933154efdbf0492a2f17af7a46f83b..54b509837bb767ac3ab28d1d7059462ca7a1170b 100644 --- a/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.h @@ -19766,10 +19811,10 @@ index 0000000000000000000000000000000000000000..4ec25daff6a0c75e378eb25b2f2638e2 + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/mac/WebViewImpl.h b/Source/WebKit/UIProcess/mac/WebViewImpl.h -index 91ae400e51861e82b75baa27b7206984c4d9bd7e..1661bf45ac3c038fb4b78b5f2adc8c9866904bf7 100644 +index 70af75657e9db68616a0504c1e83b8b5add4d183..74da3138861a68d2f96f65692dacd976ed811558 100644 --- a/Source/WebKit/UIProcess/mac/WebViewImpl.h +++ b/Source/WebKit/UIProcess/mac/WebViewImpl.h -@@ -534,6 +534,9 @@ public: +@@ -535,6 +535,9 @@ public: void provideDataForPasteboard(NSPasteboard *, NSString *type); NSArray *namesOfPromisedFilesDroppedAtDestination(NSURL *dropDestination); @@ -19777,10 +19822,10 @@ index 91ae400e51861e82b75baa27b7206984c4d9bd7e..1661bf45ac3c038fb4b78b5f2adc8c98 + RetainPtr takeSnapshotForAutomation(); +// Paywright end RefPtr takeViewSnapshot(); + RefPtr takeViewSnapshot(ForceSoftwareCapturingViewportSnapshot); void saveBackForwardSnapshotForCurrentItem(); - void saveBackForwardSnapshotForItem(WebBackForwardListItem&); diff --git a/Source/WebKit/UIProcess/mac/WebViewImpl.mm b/Source/WebKit/UIProcess/mac/WebViewImpl.mm -index 03e696c423afe8eb7bc69e8e4214213ba564644c..01d886dfc4d6c4902ff9f77fe946792ab33abf00 100644 +index ce26b2abd67c28d0b562d8ba6d6df161f2677513..4f6b7ae5aad058e9b5378824576aa849341ea86a 100644 --- a/Source/WebKit/UIProcess/mac/WebViewImpl.mm +++ b/Source/WebKit/UIProcess/mac/WebViewImpl.mm @@ -2407,6 +2407,11 @@ WebCore::DestinationColorSpace WebViewImpl::colorSpace() @@ -19795,7 +19840,7 @@ index 03e696c423afe8eb7bc69e8e4214213ba564644c..01d886dfc4d6c4902ff9f77fe946792a ASSERT(m_colorSpace); return WebCore::DestinationColorSpace { [m_colorSpace CGColorSpace] }; -@@ -4524,6 +4529,18 @@ ALLOW_DEPRECATED_DECLARATIONS_BEGIN +@@ -4533,6 +4538,17 @@ ALLOW_DEPRECATED_DECLARATIONS_BEGIN ALLOW_DEPRECATED_DECLARATIONS_END } @@ -19806,14 +19851,13 @@ index 03e696c423afe8eb7bc69e8e4214213ba564644c..01d886dfc4d6c4902ff9f77fe946792a + CGSWindowID windowID = (CGSWindowID)window.windowNumber; + if (!windowID || !window.isVisible) + return nullptr; -+ -+ return takeWindowSnapshot(windowID, true); ++ return takeWindowSnapshot(windowID, true, ForceSoftwareCapturingViewportSnapshot::Yes); +} +// Paywright end + RefPtr WebViewImpl::takeViewSnapshot() { - NSWindow *window = [m_view window]; + return takeViewSnapshot(ForceSoftwareCapturingViewportSnapshot::No); diff --git a/Source/WebKit/UIProcess/win/InspectorPlaywrightAgentClientWin.cpp b/Source/WebKit/UIProcess/win/InspectorPlaywrightAgentClientWin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dd7fe0604188bb025f361f1c44685e38bbf935ca @@ -20629,10 +20673,10 @@ index 2b64d1b5b013d53b18b7757fe3b3f3d9a0501571..e8f28808f5ef0532319a4462fd285c07 ${GLIB_GIO_LIBRARIES} ${GLIB_GOBJECT_LIBRARIES} diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj -index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f3229076088a93 100644 +index 646a31824197a9354c3540d7c4cd7ec5bd837b83..796f8caad19b511e8aca49b41bf24079163a021f 100644 --- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj +++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj -@@ -1547,6 +1547,7 @@ +@@ -1572,6 +1572,7 @@ 5CABDC8722C40FED001EDE8E /* APIMessageListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CABDC8322C40FA7001EDE8E /* APIMessageListener.h */; }; 5CADDE05215046BD0067D309 /* WKWebProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C74300E21500492004BFA17 /* WKWebProcess.h */; settings = {ATTRIBUTES = (Private, ); }; }; 5CAECB6627465AE400AB78D0 /* UnifiedSource115.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5CAECB5E27465AE300AB78D0 /* UnifiedSource115.cpp */; }; @@ -20640,7 +20684,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 5CAF7AA726F93AB00003F19E /* adattributiond.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5CAF7AA526F93A950003F19E /* adattributiond.cpp */; }; 5CAFDE452130846300B1F7E1 /* _WKInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CAFDE422130843500B1F7E1 /* _WKInspector.h */; settings = {ATTRIBUTES = (Private, ); }; }; 5CAFDE472130846A00B1F7E1 /* _WKInspectorInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CAFDE442130843600B1F7E1 /* _WKInspectorInternal.h */; }; -@@ -2379,6 +2380,18 @@ +@@ -2403,6 +2404,18 @@ DF0C5F28252ECB8E00D921DB /* WKDownload.h in Headers */ = {isa = PBXBuildFile; fileRef = DF0C5F24252ECB8D00D921DB /* WKDownload.h */; settings = {ATTRIBUTES = (Public, ); }; }; DF0C5F2A252ECB8E00D921DB /* WKDownloadDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = DF0C5F26252ECB8E00D921DB /* WKDownloadDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; DF0C5F2B252ED44000D921DB /* WKDownloadInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = DF0C5F25252ECB8E00D921DB /* WKDownloadInternal.h */; }; @@ -20659,7 +20703,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 DF462E0F23F22F5500EFF35F /* WKHTTPCookieStorePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DF462E0E23F22F5300EFF35F /* WKHTTPCookieStorePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; DF462E1223F338BE00EFF35F /* WKContentWorldPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DF462E1123F338AD00EFF35F /* WKContentWorldPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; DF7A231C291B088D00B98DF3 /* WKSnapshotConfigurationPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DF7A231B291B088D00B98DF3 /* WKSnapshotConfigurationPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; -@@ -2467,6 +2480,8 @@ +@@ -2491,6 +2504,8 @@ E5BEF6822130C48000F31111 /* WebDataListSuggestionsDropdownIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = E5BEF6802130C47F00F31111 /* WebDataListSuggestionsDropdownIOS.h */; }; E5CB07DC20E1678F0022C183 /* WKFormColorControl.h in Headers */ = {isa = PBXBuildFile; fileRef = E5CB07DA20E1678F0022C183 /* WKFormColorControl.h */; }; E5CBA76427A318E100DF7858 /* UnifiedSource120.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA75F27A3187800DF7858 /* UnifiedSource120.cpp */; }; @@ -20668,7 +20712,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 E5CBA76527A318E100DF7858 /* UnifiedSource118.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA76127A3187900DF7858 /* UnifiedSource118.cpp */; }; E5CBA76627A318E100DF7858 /* UnifiedSource116.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA76327A3187B00DF7858 /* UnifiedSource116.cpp */; }; E5CBA76727A318E100DF7858 /* UnifiedSource119.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA76027A3187900DF7858 /* UnifiedSource119.cpp */; }; -@@ -2487,6 +2502,9 @@ +@@ -2511,6 +2526,9 @@ EBA8D3B627A5E33F00CB7900 /* MockPushServiceConnection.mm in Sources */ = {isa = PBXBuildFile; fileRef = EBA8D3B027A5E33F00CB7900 /* MockPushServiceConnection.mm */; }; EBA8D3B727A5E33F00CB7900 /* PushServiceConnection.mm in Sources */ = {isa = PBXBuildFile; fileRef = EBA8D3B127A5E33F00CB7900 /* PushServiceConnection.mm */; }; ED82A7F2128C6FAF004477B3 /* WKBundlePageOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A22F0FF1289FCD90085E74F /* WKBundlePageOverlay.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -20677,8 +20721,8 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 + F3867F0A24607D4E008F0F31 /* InspectorScreencastAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = F3867F0424607D2B008F0F31 /* InspectorScreencastAgent.h */; }; F409BA181E6E64BC009DA28E /* WKDragDestinationAction.h in Headers */ = {isa = PBXBuildFile; fileRef = F409BA171E6E64B3009DA28E /* WKDragDestinationAction.h */; settings = {ATTRIBUTES = (Private, ); }; }; F40C3B712AB401C5007A3567 /* WKDatePickerPopoverController.h in Headers */ = {isa = PBXBuildFile; fileRef = F40C3B6F2AB40167007A3567 /* WKDatePickerPopoverController.h */; }; - F41795A62AC61B78007F5F12 /* CompactContextMenuPresenter.h in Headers */ = {isa = PBXBuildFile; fileRef = F41795A42AC619A2007F5F12 /* CompactContextMenuPresenter.h */; }; -@@ -6230,6 +6248,7 @@ + F416F1C02C5C3E360085D8DD /* WKScrollViewTrackingTapGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = F416F1BE2C5C3E360085D8DD /* WKScrollViewTrackingTapGestureRecognizer.h */; }; +@@ -6291,6 +6309,7 @@ 5CABDC8522C40FCC001EDE8E /* WKMessageListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKMessageListener.h; sourceTree = ""; }; 5CABE07A28F60E8A00D83FD9 /* WebPushMessage.serialization.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = WebPushMessage.serialization.in; sourceTree = ""; }; 5CADDE0D2151AA010067D309 /* AuthenticationChallengeDisposition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthenticationChallengeDisposition.h; sourceTree = ""; }; @@ -20686,7 +20730,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 5CAECB5E27465AE300AB78D0 /* UnifiedSource115.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource115.cpp; sourceTree = ""; }; 5CAF7AA426F93A750003F19E /* adattributiond */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = adattributiond; sourceTree = BUILT_PRODUCTS_DIR; }; 5CAF7AA526F93A950003F19E /* adattributiond.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = adattributiond.cpp; sourceTree = ""; }; -@@ -7930,6 +7949,19 @@ +@@ -7988,6 +8007,19 @@ DF0C5F24252ECB8D00D921DB /* WKDownload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDownload.h; sourceTree = ""; }; DF0C5F25252ECB8E00D921DB /* WKDownloadInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDownloadInternal.h; sourceTree = ""; }; DF0C5F26252ECB8E00D921DB /* WKDownloadDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDownloadDelegate.h; sourceTree = ""; }; @@ -20706,7 +20750,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 DF462E0E23F22F5300EFF35F /* WKHTTPCookieStorePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKHTTPCookieStorePrivate.h; sourceTree = ""; }; DF462E1123F338AD00EFF35F /* WKContentWorldPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKContentWorldPrivate.h; sourceTree = ""; }; DF58C6311371AC5800F9A37C /* NativeWebWheelEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeWebWheelEvent.h; sourceTree = ""; }; -@@ -8082,6 +8114,8 @@ +@@ -8140,6 +8172,8 @@ E5CBA76127A3187900DF7858 /* UnifiedSource118.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource118.cpp; sourceTree = ""; }; E5CBA76227A3187900DF7858 /* UnifiedSource117.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource117.cpp; sourceTree = ""; }; E5CBA76327A3187B00DF7858 /* UnifiedSource116.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource116.cpp; sourceTree = ""; }; @@ -20715,7 +20759,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 E5DEFA6726F8F42600AB68DB /* PhotosUISPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhotosUISPI.h; sourceTree = ""; }; EB0D312D275AE13300863D8F /* com.apple.webkit.webpushd.mac.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.webkit.webpushd.mac.plist; sourceTree = ""; }; EB0D312E275AE13300863D8F /* com.apple.webkit.webpushd.ios.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.webkit.webpushd.ios.plist; sourceTree = ""; }; -@@ -8111,6 +8145,14 @@ +@@ -8169,6 +8203,14 @@ ECA680D31E6904B500731D20 /* ExtraPrivateSymbolsForTAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExtraPrivateSymbolsForTAPI.h; sourceTree = ""; }; ECBFC1DB1E6A4D66000300C7 /* ExtraPublicSymbolsForTAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExtraPublicSymbolsForTAPI.h; sourceTree = ""; }; F036978715F4BF0500C3A80E /* WebColorPicker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebColorPicker.cpp; sourceTree = ""; }; @@ -20730,7 +20774,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 F409BA171E6E64B3009DA28E /* WKDragDestinationAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDragDestinationAction.h; sourceTree = ""; }; F40C3B6F2AB40167007A3567 /* WKDatePickerPopoverController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WKDatePickerPopoverController.h; path = ios/forms/WKDatePickerPopoverController.h; sourceTree = ""; }; F40C3B702AB40167007A3567 /* WKDatePickerPopoverController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = WKDatePickerPopoverController.mm; path = ios/forms/WKDatePickerPopoverController.mm; sourceTree = ""; }; -@@ -8415,6 +8457,7 @@ +@@ -8486,6 +8528,7 @@ 3766F9EE189A1241003CF19B /* JavaScriptCore.framework in Frameworks */, 3766F9F1189A1254003CF19B /* libicucore.dylib in Frameworks */, 7B9FC5BB28A5233B007570E7 /* libWebKitPlatform.a in Frameworks */, @@ -20738,7 +20782,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 3766F9EF189A1244003CF19B /* QuartzCore.framework in Frameworks */, 37694525184FC6B600CDE21F /* Security.framework in Frameworks */, 37BEC4DD1948FC6A008B4286 /* WebCore.framework in Frameworks */, -@@ -11296,6 +11339,7 @@ +@@ -11397,6 +11440,7 @@ 99788ACA1F421DCA00C08000 /* _WKAutomationSessionConfiguration.mm */, 990D28A81C6404B000986977 /* _WKAutomationSessionDelegate.h */, 990D28AF1C65203900986977 /* _WKAutomationSessionInternal.h */, @@ -20746,7 +20790,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 5C4609E222430E4C009943C2 /* _WKContentRuleListAction.h */, 5C4609E322430E4D009943C2 /* _WKContentRuleListAction.mm */, 5C4609E422430E4D009943C2 /* _WKContentRuleListActionInternal.h */, -@@ -12603,6 +12647,7 @@ +@@ -12714,6 +12758,7 @@ E34B110C27C46BC6006D2F2E /* libWebCoreTestShim.dylib */, E34B110F27C46D09006D2F2E /* libWebCoreTestSupport.dylib */, DDE992F4278D06D900F60D26 /* libWebKitAdditions.a */, @@ -20754,7 +20798,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 57A9FF15252C6AEF006A2040 /* libWTF.a */, 5750F32A2032D4E500389347 /* LocalAuthentication.framework */, 570DAAB0230273D200E8FC04 /* NearField.framework */, -@@ -13173,6 +13218,12 @@ +@@ -13286,6 +13331,12 @@ children = ( 9197940423DBC4BB00257892 /* InspectorBrowserAgent.cpp */, 9197940323DBC4BB00257892 /* InspectorBrowserAgent.h */, @@ -20767,7 +20811,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 ); path = Agents; sourceTree = ""; -@@ -13181,6 +13232,7 @@ +@@ -13294,6 +13345,7 @@ isa = PBXGroup; children = ( A5D3504D1D78F0D2005124A9 /* RemoteWebInspectorUIProxyMac.mm */, @@ -20775,15 +20819,15 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 1CA8B935127C774E00576C2B /* WebInspectorUIProxyMac.mm */, 99A7ACE326012919006D57FD /* WKInspectorResourceURLSchemeHandler.h */, 99A7ACE42601291A006D57FD /* WKInspectorResourceURLSchemeHandler.mm */, -@@ -13898,6 +13950,7 @@ +@@ -14013,6 +14065,7 @@ E1513C65166EABB200149FCB /* AuxiliaryProcessProxy.h */, 46A2B6061E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.cpp */, 46A2B6071E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.h */, + D71A944B237239FB002C4D9E /* BrowserInspectorPipe.h */, 5C6D69352AC3935D0099BDAF /* BrowsingContextGroup.cpp */, 5C6D69362AC3935D0099BDAF /* BrowsingContextGroup.h */, - 07297F9C1C1711EA003F0735 /* DeviceIdHashSaltStorage.cpp */, -@@ -13921,6 +13974,8 @@ + 5CA98549210BEB5A0057EB6B /* BrowsingWarning.h */, +@@ -14037,6 +14090,8 @@ BC06F43912DBCCFB002D78DE /* GeolocationPermissionRequestProxy.cpp */, BC06F43812DBCCFB002D78DE /* GeolocationPermissionRequestProxy.h */, 2DD5A72A1EBF09A7009BA597 /* HiddenPageThrottlingAutoIncreasesCounter.h */, @@ -20792,7 +20836,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 5CEABA2B2333251400797797 /* LegacyGlobalSettings.cpp */, 5CEABA2A2333247700797797 /* LegacyGlobalSettings.h */, 31607F3819627002009B87DA /* LegacySessionStateCoding.h */, -@@ -13954,6 +14009,7 @@ +@@ -14070,6 +14125,7 @@ 1A0C227D2451130A00ED614D /* QuickLookThumbnailingSoftLink.mm */, 1AEE57232409F142002005D6 /* QuickLookThumbnailLoader.h */, 1AEE57242409F142002005D6 /* QuickLookThumbnailLoader.mm */, @@ -20800,7 +20844,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 5CCB54DC2A4FEA6A0005FAA8 /* RemotePageDrawingAreaProxy.cpp */, 5CCB54DB2A4FEA6A0005FAA8 /* RemotePageDrawingAreaProxy.h */, 5C907E9A294D507100B3402D /* RemotePageProxy.cpp */, -@@ -14056,6 +14112,8 @@ +@@ -14170,6 +14226,8 @@ BC7B6204129A0A6700D174A4 /* WebPageGroup.h */, 2D9EA3101A96D9EB002D2807 /* WebPageInjectedBundleClient.cpp */, 2D9EA30E1A96CBFF002D2807 /* WebPageInjectedBundleClient.h */, @@ -20809,7 +20853,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 BC111B0B112F5E4F00337BAB /* WebPageProxy.cpp */, BC032DCB10F4389F0058C15A /* WebPageProxy.h */, BCBD38FA125BAB9A00D2C29F /* WebPageProxy.messages.in */, -@@ -14224,6 +14282,7 @@ +@@ -14338,6 +14396,7 @@ BC646C1911DD399F006455B0 /* WKBackForwardListItemRef.h */, BC646C1611DD399F006455B0 /* WKBackForwardListRef.cpp */, BC646C1711DD399F006455B0 /* WKBackForwardListRef.h */, @@ -20817,17 +20861,17 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 BCB9E24A1120E15C00A137E0 /* WKContext.cpp */, BCB9E2491120E15C00A137E0 /* WKContext.h */, 1AE52F9319201F6B00A1FA37 /* WKContextConfigurationRef.cpp */, -@@ -14805,6 +14864,9 @@ +@@ -14915,6 +14974,9 @@ + 07EF07592745A8160066EA04 /* DisplayCaptureSessionManager.h */, + 07EF07582745A8160066EA04 /* DisplayCaptureSessionManager.mm */, 7AFA6F682A9F57C50055322A /* DisplayLinkMac.cpp */, - 31ABA79C215AF9E000C90E31 /* HighPerformanceGPUManager.h */, - 31ABA79D215AF9E000C90E31 /* HighPerformanceGPUManager.mm */, + D71A94302370E025002C4D9E /* InspectorPlaywrightAgentClientMac.h */, + D7EB04E62372A73B00F744CE /* InspectorPlaywrightAgentClientMac.mm */, + D79902AF236E9404005D6F7E /* InspectorTargetProxyMac.mm */, 1AFDE65B1954E8D500C48FFA /* LegacySessionStateCoding.cpp */, 0FCB4E5818BBE3D9000FCFC9 /* PageClientImplMac.h */, 0FCB4E5918BBE3D9000FCFC9 /* PageClientImplMac.mm */, -@@ -14828,6 +14890,8 @@ +@@ -14938,6 +15000,8 @@ E568B92120A3AC6A00E3C856 /* WebDataListSuggestionsDropdownMac.mm */, E55CD20124D09F1F0042DB9C /* WebDateTimePickerMac.h */, E55CD20224D09F1F0042DB9C /* WebDateTimePickerMac.mm */, @@ -20836,7 +20880,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 BC857E8512B71EBB00EDEB2E /* WebPageProxyMac.mm */, BC5750951268F3C6006F0F12 /* WebPopupMenuProxyMac.h */, BC5750961268F3C6006F0F12 /* WebPopupMenuProxyMac.mm */, -@@ -15857,6 +15921,7 @@ +@@ -15969,6 +16033,7 @@ 99788ACB1F421DDA00C08000 /* _WKAutomationSessionConfiguration.h in Headers */, 990D28AC1C6420CF00986977 /* _WKAutomationSessionDelegate.h in Headers */, 990D28B11C65208D00986977 /* _WKAutomationSessionInternal.h in Headers */, @@ -20844,15 +20888,15 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 5C4609E7224317B4009943C2 /* _WKContentRuleListAction.h in Headers */, 5C4609E8224317BB009943C2 /* _WKContentRuleListActionInternal.h in Headers */, 1A5704F81BE01FF400874AF1 /* _WKContextMenuElementInfo.h in Headers */, -@@ -16168,6 +16233,7 @@ +@@ -16265,6 +16330,7 @@ E170876C16D6CA6900F99226 /* BlobRegistryProxy.h in Headers */, 4F601432155C5AA2001FBDE0 /* BlockingResponseMap.h in Headers */, 1A5705111BE410E600874AF1 /* BlockSPI.h in Headers */, + D71A944C237239FB002C4D9E /* BrowserInspectorPipe.h in Headers */, + 5CA9854A210BEB640057EB6B /* BrowsingWarning.h in Headers */, A7E69BCC2B2117A100D43D3F /* BufferAndBackendInfo.h in Headers */, BC3065FA1259344E00E71278 /* CacheModel.h in Headers */, - 935BF7FC2936BF1A00B41326 /* CacheStorageCache.h in Headers */, -@@ -16348,7 +16414,11 @@ +@@ -16445,7 +16511,11 @@ BC14DF77120B5B7900826C0C /* InjectedBundleScriptWorld.h in Headers */, CE550E152283752200D28791 /* InsertTextOptions.h in Headers */, 9197940523DBC4BB00257892 /* InspectorBrowserAgent.h in Headers */, @@ -20864,15 +20908,15 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 A5E391FD2183C1F800C8FB31 /* InspectorTargetProxy.h in Headers */, C5BCE5DF1C50766A00CDE3FA /* InteractionInformationAtPosition.h in Headers */, 2D4D2C811DF60BF3002EB10C /* InteractionInformationRequest.h in Headers */, -@@ -16610,6 +16680,7 @@ - CDAC20C923FC2F750021DEE3 /* RemoteCDMInstanceSessionIdentifier.h in Headers */, +@@ -16706,6 +16776,7 @@ + 0F6E7C532C4C386800F1DB85 /* RemoteDisplayListRecorderMessages.h in Headers */, F451C0FE2703B263002BA03B /* RemoteDisplayListRecorderProxy.h in Headers */, A78A5FE42B0EB39E005036D3 /* RemoteImageBufferSetIdentifier.h in Headers */, + D71A943A2370F061002C4D9E /* RemoteInspectorPipe.h in Headers */, 2D47B56D1810714E003A3AEE /* RemoteLayerBackingStore.h in Headers */, 2DDF731518E95060004F5A66 /* RemoteLayerBackingStoreCollection.h in Headers */, 1AB16AEA164B3A8800290D62 /* RemoteLayerTreeContext.h in Headers */, -@@ -16664,6 +16735,7 @@ +@@ -16759,6 +16830,7 @@ E1E552C516AE065F004ED653 /* SandboxInitializationParameters.h in Headers */, E36FF00327F36FBD004BE21A /* SandboxStateVariables.h in Headers */, 7BAB111025DD02B3008FC479 /* ScopedActiveMessageReceiveQueue.h in Headers */, @@ -20880,7 +20924,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 463BB93A2B9D08D80098C5C3 /* ScriptMessageHandlerIdentifier.h in Headers */, E4D54D0421F1D72D007E3C36 /* ScrollingTreeFrameScrollingNodeRemoteIOS.h in Headers */, 0F931C1C18C5711900DBA7C3 /* ScrollingTreeOverflowScrollingNodeIOS.h in Headers */, -@@ -17011,6 +17083,8 @@ +@@ -17108,6 +17180,8 @@ 939EF87029D112EE00F23AEE /* WebPageInlines.h in Headers */, 9197940823DBC4CB00257892 /* WebPageInspectorAgentBase.h in Headers */, A513F5402154A5D700662841 /* WebPageInspectorController.h in Headers */, @@ -20889,7 +20933,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 A543E30C215C8A8D00279CD9 /* WebPageInspectorTarget.h in Headers */, A543E30D215C8A9000279CD9 /* WebPageInspectorTargetController.h in Headers */, A543E307215AD13700279CD9 /* WebPageInspectorTargetFrontendChannel.h in Headers */, -@@ -19422,6 +19496,8 @@ +@@ -19549,6 +19623,8 @@ 522F792928D50EBB0069B45B /* HidService.mm in Sources */, 2749F6442146561B008380BF /* InjectedBundleNodeHandle.cpp in Sources */, 2749F6452146561E008380BF /* InjectedBundleRangeHandle.cpp in Sources */, @@ -20898,7 +20942,7 @@ index 3cf46d518a1920a5c6c8e9537f71b1b0b25b00a0..3c7e8c09640d82630864f41569f32290 1CC94E532AC92F190045F269 /* JSWebExtensionAPIAction.mm in Sources */, 1C2B4D4B2A819D0D00C528A1 /* JSWebExtensionAPIAlarms.mm in Sources */, 1C8ECFEA2AFC7DCB007BAA62 /* JSWebExtensionAPICommands.mm in Sources */, -@@ -19864,6 +19940,8 @@ +@@ -19994,6 +20070,8 @@ E3816B3D27E2463A005EAFC0 /* WebMockContentFilterManager.cpp in Sources */, 31BA924D148831260062EDB5 /* WebNotificationManagerMessageReceiver.cpp in Sources */, 2DF6FE52212E110900469030 /* WebPage.cpp in Sources */, @@ -21119,7 +21163,7 @@ index e314c2987e348a0abee8b655caff3a1c3b3c4564..882746d581bd8db6f2fad5944f09ee9f auto permissionHandlers = m_requestsPerOrigin.take(securityOrigin); diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp -index e22824697734a6fc65de8bdcf37afee5d5b1d508..1385b96bef6ab891d359e022974c77fc734ff18d 100644 +index 32407cfa8d0448e781c7255900558cee1d33f517..755a40c173a2b6a171dda45c21c4bc6867d070d9 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp @@ -472,6 +472,8 @@ void WebChromeClient::addMessageToConsole(MessageSource source, MessageLevel lev @@ -21367,10 +21411,10 @@ index c0dd11d1a720907b1e2d863302a483eea1d39765..a4ad1b5acc545d98aea99c58fc5a5ca3 void DrawingAreaCoordinatedGraphics::scheduleDisplay() diff --git a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp -index d81997b98996b258f43ce13496ec06c6dc06d467..16e191b5ed4cf0e3b223fb9547699a05ede2f6d6 100644 +index dd57fd13ef1f45cd17192d6161f78b26cac68450..4723d7affc4a15b0cbb67f07dbfb35def9277465 100644 --- a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp +++ b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp -@@ -204,8 +204,16 @@ void LayerTreeHost::scrollNonCompositedContents(const IntRect& rect) +@@ -202,8 +202,16 @@ void LayerTreeHost::scrollNonCompositedContents(const IntRect& rect) m_scrolledSinceLastFrame = true; auto* frameView = m_webPage.localMainFrameView(); @@ -21387,7 +21431,7 @@ index d81997b98996b258f43ce13496ec06c6dc06d467..16e191b5ed4cf0e3b223fb9547699a05 m_viewportController.didScroll(rect.location()); didChangeViewport(); -@@ -325,6 +333,10 @@ void LayerTreeHost::didChangeViewport() +@@ -324,6 +332,10 @@ void LayerTreeHost::didChangeViewport() if (!view->useFixedLayout()) view->notifyScrollPositionChanged(m_lastScrollPosition); @@ -21495,10 +21539,10 @@ index b6e5283f51db82b60091320df44ef0bbf20c33c6..0d82fbb98b93e760cecf5fa7a41327d0 WebCookieJar(); diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.cpp b/Source/WebKit/WebProcess/WebPage/WebPage.cpp -index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db966dc2af 100644 +index ecf0f78a6a8eca46a535c512f6271bb0feb1806c..935fb837c97e2e9981a24375a8931e077c8e3e33 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.cpp +++ b/Source/WebKit/WebProcess/WebPage/WebPage.cpp -@@ -233,6 +233,7 @@ +@@ -234,6 +234,7 @@ #include #include #include @@ -21506,7 +21550,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db #include #include #include -@@ -1053,6 +1054,9 @@ WebPage::WebPage(PageIdentifier pageID, WebPageCreationParameters&& parameters) +@@ -1060,6 +1061,9 @@ WebPage::WebPage(PageIdentifier pageID, WebPageCreationParameters&& parameters) #endif #endif // HAVE(SANDBOX_STATE_FLAGS) @@ -21516,7 +21560,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db updateThrottleState(); #if ENABLE(ACCESSIBILITY_ANIMATION_CONTROL) updateImageAnimationEnabled(); -@@ -2018,6 +2022,22 @@ void WebPage::loadDidCommitInAnotherProcess(WebCore::FrameIdentifier frameID, st +@@ -2025,6 +2029,22 @@ void WebPage::loadDidCommitInAnotherProcess(WebCore::FrameIdentifier frameID, st frame->loadDidCommitInAnotherProcess(layerHostingContextIdentifier); } @@ -21539,7 +21583,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db void WebPage::loadRequest(LoadParameters&& loadParameters) { WEBPAGE_RELEASE_LOG(Loading, "loadRequest: navigationID=%" PRIu64 ", shouldTreatAsContinuingLoad=%u, lastNavigationWasAppInitiated=%d, existingNetworkResourceLoadIdentifierToResume=%" PRIu64, loadParameters.navigationID, static_cast(loadParameters.shouldTreatAsContinuingLoad), loadParameters.request.isAppInitiated(), valueOrDefault(loadParameters.existingNetworkResourceLoadIdentifierToResume).toUInt64()); -@@ -2200,7 +2220,9 @@ void WebPage::stopLoading() +@@ -2209,7 +2229,9 @@ void WebPage::stopLoading() void WebPage::stopLoadingDueToProcessSwap() { SetForScope isStoppingLoadingDueToProcessSwap(m_isStoppingLoadingDueToProcessSwap, true); @@ -21549,7 +21593,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db } bool WebPage::defersLoading() const -@@ -2301,17 +2323,14 @@ void WebPage::setSize(const WebCore::IntSize& viewSize) +@@ -2310,17 +2332,14 @@ void WebPage::setSize(const WebCore::IntSize& viewSize) view->resize(viewSize); m_drawingArea->setNeedsDisplay(); @@ -21567,7 +21611,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArguments) { RefPtr localMainFrame = dynamicDowncast(m_page->mainFrame()); -@@ -2336,20 +2355,18 @@ void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArg +@@ -2345,20 +2364,18 @@ void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArg ViewportAttributes attr = computeViewportAttributes(viewportArguments, minimumLayoutFallbackWidth, deviceWidth, deviceHeight, 1, m_viewSize); @@ -21595,15 +21639,15 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db #if USE(COORDINATED_GRAPHICS) m_drawingArea->didChangeViewportAttributes(WTFMove(attr)); -@@ -2357,7 +2374,6 @@ void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArg +@@ -2366,7 +2383,6 @@ void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArg send(Messages::WebPageProxy::DidChangeViewportProperties(attr)); #endif } -#endif - void WebPage::scrollMainFrameIfNotAtMaxScrollPosition(const IntSize& scrollOffset) + void WebPage::drawRect(GraphicsContext& graphicsContext, const IntRect& rect) { -@@ -2659,6 +2675,7 @@ void WebPage::scaleView(double scale) +@@ -2644,6 +2660,7 @@ void WebPage::scaleView(double scale) } m_page->setViewScaleFactor(scale); @@ -21611,7 +21655,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db scalePage(pageScale, scrollPositionAtNewScale); } -@@ -2838,18 +2855,14 @@ void WebPage::viewportPropertiesDidChange(const ViewportArguments& viewportArgum +@@ -2823,18 +2840,14 @@ void WebPage::viewportPropertiesDidChange(const ViewportArguments& viewportArgum viewportConfigurationChanged(); #endif @@ -21631,7 +21675,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db } #if !PLATFORM(IOS_FAMILY) -@@ -3571,6 +3584,13 @@ void WebPage::setLastKnownMousePosition(WebCore::FrameIdentifier frameID, IntPoi +@@ -3556,6 +3569,13 @@ void WebPage::setLastKnownMousePosition(WebCore::FrameIdentifier frameID, IntPoi frame->coreLocalFrame()->eventHandler().setLastKnownMousePosition(eventPoint, globalPoint); } @@ -21645,7 +21689,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db void WebPage::flushDeferredDidReceiveMouseEvent() { if (auto info = std::exchange(m_deferredDidReceiveMouseEvent, std::nullopt)) -@@ -3855,6 +3875,97 @@ void WebPage::touchEvent(const WebTouchEvent& touchEvent, CompletionHandlermainFrame().frameID(), touchEvent, m_page.get()).wasHandled(); @@ -21681,7 +21725,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db + WebPlatformTouchPoint::State state = WebPlatformTouchPoint::State::Released; + touchPoints.append(WebPlatformTouchPoint(id, state, screenPosition, position, radius, rotationAngle, force)); + -+ WebTouchEvent touchEvent({WebEventType::TouchEnd, eventModifiers, WallTime::now()}, WTFMove(touchPoints)); ++ WebTouchEvent touchEvent({WebEventType::TouchEnd, eventModifiers, WallTime::now()}, WTFMove(touchPoints), {}, {}); + + CurrentEvent currentEvent(touchEvent); + handled = handleTouchEvent(m_page->mainFrame().frameID(), touchEvent, m_page.get()).wasHandled() || handled; @@ -21743,7 +21787,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db #endif void WebPage::cancelPointer(WebCore::PointerID pointerId, const WebCore::IntPoint& documentPoint) -@@ -3943,6 +4054,11 @@ void WebPage::sendMessageToTargetBackend(const String& targetId, const String& m +@@ -3928,6 +4039,11 @@ void WebPage::sendMessageToTargetBackend(const String& targetId, const String& m m_inspectorTargetController->sendMessageToTargetBackend(targetId, message); } @@ -21755,7 +21799,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db void WebPage::insertNewlineInQuotedContent() { RefPtr frame = m_page->checkedFocusController()->focusedOrMainFrame(); -@@ -4178,6 +4294,7 @@ void WebPage::didCompletePageTransition() +@@ -4163,6 +4279,7 @@ void WebPage::didCompletePageTransition() void WebPage::show() { send(Messages::WebPageProxy::ShowPage()); @@ -21763,7 +21807,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db } void WebPage::setIsTakingSnapshotsForApplicationSuspension(bool isTakingSnapshotsForApplicationSuspension) -@@ -5378,7 +5495,7 @@ NotificationPermissionRequestManager* WebPage::notificationPermissionRequestMana +@@ -5315,7 +5432,7 @@ NotificationPermissionRequestManager* WebPage::notificationPermissionRequestMana #if ENABLE(DRAG_SUPPORT) @@ -21772,7 +21816,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db void WebPage::performDragControllerAction(DragControllerAction action, const IntPoint& clientPosition, const IntPoint& globalPosition, OptionSet draggingSourceOperationMask, SelectionData&& selectionData, OptionSet flags, CompletionHandler, DragHandlingMethod, bool, unsigned, IntRect, IntRect, std::optional)>&& completionHandler) { if (!m_page) -@@ -7748,6 +7865,10 @@ void WebPage::didCommitLoad(WebFrame* frame) +@@ -7702,6 +7819,10 @@ void WebPage::didCommitLoad(WebFrame* frame) #endif flushDeferredDidReceiveMouseEvent(); @@ -21783,7 +21827,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db } void WebPage::didFinishDocumentLoad(WebFrame& frame) -@@ -8028,6 +8149,9 @@ Ref WebPage::createDocumentLoader(LocalFrame& frame, const Resou +@@ -7983,6 +8104,9 @@ Ref WebPage::createDocumentLoader(LocalFrame& frame, const Resou WebsitePoliciesData::applyToDocumentLoader(WTFMove(*m_pendingWebsitePolicies), documentLoader); m_pendingWebsitePolicies = std::nullopt; } @@ -21794,7 +21838,7 @@ index 8ed6c5c3f4bb342435f1ac6bb40020ba0fdd9364..2bbf3db029ab95cfda418fa39f2654db return documentLoader; diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h -index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fbfe0533ee 100644 +index 1aeb8babedd2e3cd1524e910735564eb33f0aee3..abd8773409d4f2919d9477976b8e7fdb36a46d64 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.h +++ b/Source/WebKit/WebProcess/WebPage/WebPage.h @@ -71,6 +71,7 @@ @@ -21805,7 +21849,7 @@ index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fb #include #include #include -@@ -1173,11 +1174,11 @@ public: +@@ -1171,11 +1172,11 @@ public: void clearSelection(); void restoreSelectionInFocusedEditableElement(); @@ -21819,7 +21863,7 @@ index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fb void performDragControllerAction(std::optional, DragControllerAction, WebCore::DragData&&, CompletionHandler, WebCore::DragHandlingMethod, bool, unsigned, WebCore::IntRect, WebCore::IntRect, std::optional)>&&); void performDragOperation(WebCore::DragData&&, SandboxExtension::Handle&&, Vector&&, CompletionHandler&&); #endif -@@ -1192,6 +1193,9 @@ public: +@@ -1190,6 +1191,9 @@ public: void didStartDrag(); void dragCancelled(); OptionSet allowedDragSourceActions() const { return m_allowedDragSourceActions; } @@ -21829,7 +21873,7 @@ index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fb #endif void beginPrinting(WebCore::FrameIdentifier, const PrintInfo&); -@@ -1267,8 +1271,11 @@ public: +@@ -1265,8 +1269,11 @@ public: void gestureEvent(WebCore::FrameIdentifier, const WebGestureEvent&, CompletionHandler, bool, std::optional)>&&); #endif @@ -21850,7 +21894,7 @@ index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fb void insertNewlineInQuotedContent(); -@@ -1921,6 +1929,7 @@ private: +@@ -1932,6 +1940,7 @@ private: void createProvisionalFrame(ProvisionalFrameCreationParameters&&, WebCore::FrameIdentifier); void destroyProvisionalFrame(WebCore::FrameIdentifier); void loadDidCommitInAnotherProcess(WebCore::FrameIdentifier, std::optional); @@ -21858,7 +21902,7 @@ index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fb void loadRequest(LoadParameters&&); [[noreturn]] void loadRequestWaitingForProcessLaunch(LoadParameters&&, URL&&, WebPageProxyIdentifier, bool); void loadData(LoadParameters&&); -@@ -1962,6 +1971,7 @@ private: +@@ -1973,6 +1982,7 @@ private: void updatePotentialTapSecurityOrigin(const WebTouchEvent&, bool wasHandled); #elif ENABLE(TOUCH_EVENTS) void touchEvent(const WebTouchEvent&, CompletionHandler, bool)>&&); @@ -21866,7 +21910,7 @@ index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fb #endif void cancelPointer(WebCore::PointerID, const WebCore::IntPoint&); -@@ -2108,9 +2118,7 @@ private: +@@ -2118,9 +2128,7 @@ private: void addLayerForFindOverlay(CompletionHandler&&); void removeLayerForFindOverlay(CompletionHandler&&); @@ -21876,7 +21920,7 @@ index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fb void didChangeSelectedIndexForActivePopupMenu(int32_t newIndex); void setTextForActivePopupMenu(int32_t index); -@@ -2713,6 +2721,7 @@ private: +@@ -2723,6 +2731,7 @@ private: UserActivity m_userActivity; uint64_t m_pendingNavigationID { 0 }; @@ -21885,10 +21929,10 @@ index dade474046440584b2579c3c0e72d7daf9aafc68..35be816bd0ba41b6f6c31e4dec1906fb bool m_mainFrameProgressCompleted { false }; diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in -index 148cbc222395ba878b51a233331e45e0e2d1604f..0bb93d61e68587cbd27e24fbef0eb875783ff8ff 100644 +index b6eb40da55551bade5a33be56b4efa39d63446a9..543573bd1963d21aa209635524ae4fc1ccd32dde 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in +++ b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in -@@ -51,10 +51,13 @@ messages -> WebPage LegacyReceiver { +@@ -53,10 +53,13 @@ messages -> WebPage LegacyReceiver { MouseEvent(WebCore::FrameIdentifier frameID, WebKit::WebMouseEvent event, std::optional> sandboxExtensions) SetLastKnownMousePosition(WebCore::FrameIdentifier frameID, WebCore::IntPoint eventPoint, WebCore::IntPoint globalPoint); @@ -21903,7 +21947,7 @@ index 148cbc222395ba878b51a233331e45e0e2d1604f..0bb93d61e68587cbd27e24fbef0eb875 SetOverrideViewportArguments(std::optional arguments) DynamicViewportSizeUpdate(struct WebKit::DynamicViewportSizeUpdate target) -@@ -144,6 +147,7 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType +@@ -146,6 +149,7 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType ConnectInspector(String targetId, Inspector::FrontendChannel::ConnectionType connectionType) DisconnectInspector(String targetId) SendMessageToTargetBackend(String targetId, String message) @@ -21911,7 +21955,7 @@ index 148cbc222395ba878b51a233331e45e0e2d1604f..0bb93d61e68587cbd27e24fbef0eb875 #if ENABLE(REMOTE_INSPECTOR) SetIndicating(bool indicating); -@@ -154,6 +158,7 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType +@@ -156,6 +160,7 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType #endif #if !ENABLE(IOS_TOUCH_EVENTS) && ENABLE(TOUCH_EVENTS) TouchEvent(WebKit::WebTouchEvent event) -> (std::optional eventType, bool handled) @@ -21919,7 +21963,7 @@ index 148cbc222395ba878b51a233331e45e0e2d1604f..0bb93d61e68587cbd27e24fbef0eb875 #endif CancelPointer(WebCore::PointerID pointerId, WebCore::IntPoint documentPoint) -@@ -187,6 +192,7 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType +@@ -189,6 +194,7 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType CreateProvisionalFrame(struct WebKit::ProvisionalFrameCreationParameters creationParameters, WebCore::FrameIdentifier frameID) DestroyProvisionalFrame(WebCore::FrameIdentifier frameID); LoadDidCommitInAnotherProcess(WebCore::FrameIdentifier frameID, std::optional layerHostingContextIdentifier) @@ -21927,7 +21971,7 @@ index 148cbc222395ba878b51a233331e45e0e2d1604f..0bb93d61e68587cbd27e24fbef0eb875 LoadRequestWaitingForProcessLaunch(struct WebKit::LoadParameters loadParameters, URL resourceDirectoryURL, WebKit::WebPageProxyIdentifier pageID, bool checkAssumedReadAccessToResourceURL) LoadData(struct WebKit::LoadParameters loadParameters) LoadSimulatedRequestAndResponse(struct WebKit::LoadParameters loadParameters, WebCore::ResourceResponse simulatedResponse) -@@ -351,10 +357,10 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType +@@ -353,10 +359,10 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType RemoveLayerForFindOverlay() -> () # Drag and drop. @@ -21940,7 +21984,7 @@ index 148cbc222395ba878b51a233331e45e0e2d1604f..0bb93d61e68587cbd27e24fbef0eb875 PerformDragControllerAction(std::optional frameID, enum:uint8_t WebKit::DragControllerAction action, WebCore::DragData dragData) -> (std::optional dragOperation, enum:uint8_t WebCore::DragHandlingMethod dragHandlingMethod, bool mouseIsOverFileInput, unsigned numberOfItemsToBeAccepted, WebCore::IntRect insertionRect, WebCore::IntRect editableElementRect, struct std::optional remoteUserInputEventData) PerformDragOperation(WebCore::DragData dragData, WebKit::SandboxExtensionHandle sandboxExtensionHandle, Vector sandboxExtensionsForUpload) -> (bool handled) #endif -@@ -364,6 +370,10 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType +@@ -366,6 +372,10 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType DragCancelled() #endif @@ -21952,7 +21996,7 @@ index 148cbc222395ba878b51a233331e45e0e2d1604f..0bb93d61e68587cbd27e24fbef0eb875 RequestDragStart(WebCore::IntPoint clientPosition, WebCore::IntPoint globalPosition, OptionSet allowedActionsMask) RequestAdditionalItemsForDragSession(WebCore::IntPoint clientPosition, WebCore::IntPoint globalPosition, OptionSet allowedActionsMask) diff --git a/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm b/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm -index 678294ec1a7b6a05ed91730a723210a66af2f918..aafa6cfaff690bcbf6a69870576844f8894b8613 100644 +index ab66a4946761a0219f7baede1ffeefb2c83a0e23..b4332b26fae11cd3ace28df86122e52dfe168727 100644 --- a/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm +++ b/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm @@ -802,21 +802,37 @@ String WebPage::platformUserAgent(const URL&) const @@ -22044,10 +22088,10 @@ index f17f5d719d892309ed9c7093384945866b5117b9..1dba47bbf0dbd0362548423a74b38034 } diff --git a/Source/WebKit/WebProcess/WebProcess.cpp b/Source/WebKit/WebProcess/WebProcess.cpp -index df3c9d0ebbc3665c3ec4fcac259bee402ef19f45..9ff1d77c609c6b483eb3b9c620ef5742f5477069 100644 +index 7dac4718f46e365ce73a78f3c3a70a7170d74a6b..11568fefb3c9b5b54870f9fb1dd9142649cf2d29 100644 --- a/Source/WebKit/WebProcess/WebProcess.cpp +++ b/Source/WebKit/WebProcess/WebProcess.cpp -@@ -88,6 +88,7 @@ +@@ -89,6 +89,7 @@ #include "WebsiteData.h" #include "WebsiteDataStoreParameters.h" #include "WebsiteDataType.h" @@ -22055,7 +22099,7 @@ index df3c9d0ebbc3665c3ec4fcac259bee402ef19f45..9ff1d77c609c6b483eb3b9c620ef5742 #include #include #include -@@ -364,6 +365,14 @@ void WebProcess::initializeProcess(const AuxiliaryProcessInitializationParameter +@@ -365,6 +366,14 @@ void WebProcess::initializeProcess(const AuxiliaryProcessInitializationParameter { JSC::Options::AllowUnfinalizedAccessScope scope; JSC::Options::allowNonSPTagging() = false; @@ -22070,7 +22114,7 @@ index df3c9d0ebbc3665c3ec4fcac259bee402ef19f45..9ff1d77c609c6b483eb3b9c620ef5742 JSC::Options::notifyOptionsChanged(); } -@@ -371,6 +380,8 @@ void WebProcess::initializeProcess(const AuxiliaryProcessInitializationParameter +@@ -372,6 +381,8 @@ void WebProcess::initializeProcess(const AuxiliaryProcessInitializationParameter platformInitializeProcess(parameters); updateCPULimit(); @@ -22079,21 +22123,6 @@ index df3c9d0ebbc3665c3ec4fcac259bee402ef19f45..9ff1d77c609c6b483eb3b9c620ef5742 } void WebProcess::initializeConnection(IPC::Connection* connection) -diff --git a/Source/WebKit/WebProcess/win/WebProcessMainWin.cpp b/Source/WebKit/WebProcess/win/WebProcessMainWin.cpp -index 8987c3964a9308f2454759de7f8972215a3ae416..bcac0afeb94ed8123d1f9fb0b932c8497d157b49 100644 ---- a/Source/WebKit/WebProcess/win/WebProcessMainWin.cpp -+++ b/Source/WebKit/WebProcess/win/WebProcessMainWin.cpp -@@ -42,7 +42,9 @@ public: - bool platformInitialize() override - { - if (SetProcessDpiAwarenessContextPtr()) -- SetProcessDpiAwarenessContextPtr()(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); -+ // Playwright begin -+ SetProcessDpiAwarenessContextPtr()(DPI_AWARENESS_CONTEXT_UNAWARE); -+ // Playwright end - else - SetProcessDPIAware(); - return true; diff --git a/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm b/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm index d694170887445e2eed73340666bc4847aef4f9a6..6ced22d0953a39582285201e0946d68f954644ad 100644 --- a/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm @@ -22108,10 +22137,10 @@ index d694170887445e2eed73340666bc4847aef4f9a6..6ced22d0953a39582285201e0946d68f - (void)touch:(WebEvent *)event { diff --git a/Source/WebKitLegacy/mac/WebView/WebView.mm b/Source/WebKitLegacy/mac/WebView/WebView.mm -index e36b84e6c861fcef5102d881a037c2b414de0469..1a216224bf7b8fadba7033d785520d30050865c8 100644 +index 66f631f8ccec43cb2d43843708370b4d7be950d3..9b99f45f670a37d223377718dc7b146d81c22608 100644 --- a/Source/WebKitLegacy/mac/WebView/WebView.mm +++ b/Source/WebKitLegacy/mac/WebView/WebView.mm -@@ -3981,7 +3981,7 @@ + (void)_doNotStartObservingNetworkReachability +@@ -3978,7 +3978,7 @@ + (void)_doNotStartObservingNetworkReachability } #endif // PLATFORM(IOS_FAMILY) @@ -22120,7 +22149,7 @@ index e36b84e6c861fcef5102d881a037c2b414de0469..1a216224bf7b8fadba7033d785520d30 - (NSArray *)_touchEventRegions { -@@ -4023,7 +4023,7 @@ - (NSArray *)_touchEventRegions +@@ -4020,7 +4020,7 @@ - (NSArray *)_touchEventRegions }).autorelease(); } @@ -22161,12 +22190,12 @@ index 0000000000000000000000000000000000000000..dd6a53e2d57318489b7e49dd7373706d + LIBVPX_LIBRARIES +) diff --git a/Source/cmake/OptionsGTK.cmake b/Source/cmake/OptionsGTK.cmake -index 7528273e7832716ef8cb900f89728d4003130d92..32826feac62d3d4132df79febd01b8a8b9ce16ff 100644 +index d2800f8a5bcdbd7521d47a591acfccb7d964bbaa..b761bbc3137ebc27d97aefdbf85ec4efc327bb37 100644 --- a/Source/cmake/OptionsGTK.cmake +++ b/Source/cmake/OptionsGTK.cmake -@@ -11,8 +11,13 @@ if (${CMAKE_VERSION} VERSION_LESS "3.20" AND NOT ${CMAKE_GENERATOR} STREQUAL "Ni - message(FATAL_ERROR "Building with Makefiles requires CMake 3.20 or newer. Either enable Ninja by passing -GNinja, or upgrade CMake.") - endif () +@@ -5,8 +5,13 @@ WEBKIT_OPTION_BEGIN() + + SET_PROJECT_VERSION(2 45 6) +set(ENABLE_WEBKIT_LEGACY OFF) + @@ -22178,7 +22207,7 @@ index 7528273e7832716ef8cb900f89728d4003130d92..32826feac62d3d4132df79febd01b8a8 find_package(Cairo 1.16.0 REQUIRED) find_package(LibGcrypt 1.7.0 REQUIRED) find_package(Libtasn1 REQUIRED) -@@ -29,6 +34,10 @@ find_package(ZLIB REQUIRED) +@@ -23,6 +28,10 @@ find_package(ZLIB REQUIRED) find_package(WebP REQUIRED COMPONENTS demux) find_package(ATSPI 2.5.3) @@ -22189,7 +22218,7 @@ index 7528273e7832716ef8cb900f89728d4003130d92..32826feac62d3d4132df79febd01b8a8 include(GStreamerDefinitions) include(FindGLibCompileResources) -@@ -88,14 +97,14 @@ endif () +@@ -82,14 +91,14 @@ endif () # without approval from a GTK reviewer. There must be strong reason to support # changing the value of the option. WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DRAG_SUPPORT PUBLIC ON) @@ -22207,7 +22236,7 @@ index 7528273e7832716ef8cb900f89728d4003130d92..32826feac62d3d4132df79febd01b8a8 WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_LCMS PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_JPEGXL PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_WOFF2 PUBLIC ON) -@@ -119,7 +128,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_INPUT_TYPE_DATETIMELOCAL PRIVATE ON) +@@ -113,7 +122,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_INPUT_TYPE_DATETIMELOCAL PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_INPUT_TYPE_MONTH PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_INPUT_TYPE_TIME PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_INPUT_TYPE_WEEK PRIVATE ON) @@ -22216,7 +22245,7 @@ index 7528273e7832716ef8cb900f89728d4003130d92..32826feac62d3d4132df79febd01b8a8 WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_SESSION PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_SESSION_PLAYLIST PRIVATE OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_STREAM PRIVATE ON) -@@ -131,7 +140,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NETWORK_CACHE_SPECULATIVE_REVALIDATION P +@@ -125,7 +134,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NETWORK_CACHE_SPECULATIVE_REVALIDATION P WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NETWORK_CACHE_STALE_WHILE_REVALIDATE PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_OFFSCREEN_CANVAS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_OFFSCREEN_CANVAS_IN_WORKERS PRIVATE ON) @@ -22225,7 +22254,7 @@ index 7528273e7832716ef8cb900f89728d4003130d92..32826feac62d3d4132df79febd01b8a8 WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PERIODIC_MEMORY_MONITOR PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_POINTER_LOCK PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_SHAREABLE_RESOURCE PRIVATE ON) -@@ -149,6 +158,14 @@ else () +@@ -143,6 +152,14 @@ else () WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_SKIA PRIVATE OFF) endif () @@ -22241,19 +22270,19 @@ index 7528273e7832716ef8cb900f89728d4003130d92..32826feac62d3d4132df79febd01b8a8 # Finalize the value for all options. Do not attempt to use an option before diff --git a/Source/cmake/OptionsWPE.cmake b/Source/cmake/OptionsWPE.cmake -index a70706b67e0313e084eed2d1682ef85b296c80a6..21a610a7c0ddd838ed152bc1de4440ae4e6ff7ea 100644 +index 15000fb45706e5ed73137d21db2705645bc05f33..23889c60d34262f4eb9119e42b0685af2d9ecff1 100644 --- a/Source/cmake/OptionsWPE.cmake +++ b/Source/cmake/OptionsWPE.cmake -@@ -9,6 +9,8 @@ if (${CMAKE_VERSION} VERSION_LESS "3.20" AND NOT ${CMAKE_GENERATOR} STREQUAL "Ni - message(FATAL_ERROR "Building with Makefiles requires CMake 3.20 or newer. Either enable Ninja by passing -GNinja, or upgrade CMake.") - endif () +@@ -3,6 +3,8 @@ include(VersioningUtils) + + SET_PROJECT_VERSION(2 45 3) +set(ENABLE_WEBKIT_LEGACY OFF) + set(USER_AGENT_BRANDING "" CACHE STRING "Branding to add to user agent string") find_package(HarfBuzz 1.4.2 REQUIRED COMPONENTS ICU) -@@ -27,6 +29,9 @@ find_package(WebP REQUIRED COMPONENTS demux) +@@ -21,6 +23,9 @@ find_package(WebP REQUIRED COMPONENTS demux) find_package(WPE REQUIRED) find_package(ZLIB REQUIRED) @@ -22263,7 +22292,7 @@ index a70706b67e0313e084eed2d1682ef85b296c80a6..21a610a7c0ddd838ed152bc1de4440ae WEBKIT_OPTION_BEGIN() SET_AND_EXPOSE_TO_BUILD(ENABLE_DEVELOPER_MODE ${DEVELOPER_MODE}) -@@ -38,11 +43,11 @@ include(FindGLibCompileResources) +@@ -32,11 +37,11 @@ include(FindGLibCompileResources) # without approval from a WPE reviewer. There must be strong reason to support # changing the value of the option. WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_ENCRYPTED_MEDIA PUBLIC ${ENABLE_EXPERIMENTAL_FEATURES}) @@ -22277,7 +22306,7 @@ index a70706b67e0313e084eed2d1682ef85b296c80a6..21a610a7c0ddd838ed152bc1de4440ae WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_LCMS PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_JPEGXL PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_WOFF2 PUBLIC ON) -@@ -58,7 +63,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DARK_MODE_CSS PRIVATE ON) +@@ -52,7 +57,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DARK_MODE_CSS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_FTPDIR PRIVATE OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_GPU_PROCESS PRIVATE OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_CONTROLS_CONTEXT_MENUS PRIVATE ON) @@ -22286,7 +22315,7 @@ index a70706b67e0313e084eed2d1682ef85b296c80a6..21a610a7c0ddd838ed152bc1de4440ae WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_SESSION PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_SESSION_PLAYLIST PRIVATE OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_STREAM PRIVATE ON) -@@ -72,7 +77,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_OFFSCREEN_CANVAS_IN_WORKERS PRIVATE ON) +@@ -66,7 +71,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_OFFSCREEN_CANVAS_IN_WORKERS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PERIODIC_MEMORY_MONITOR PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_SHAREABLE_RESOURCE PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_SPEECH_SYNTHESIS PRIVATE ${ENABLE_EXPERIMENTAL_FEATURES}) @@ -22295,7 +22324,7 @@ index a70706b67e0313e084eed2d1682ef85b296c80a6..21a610a7c0ddd838ed152bc1de4440ae WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_TOUCH_EVENTS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_VARIATION_FONTS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_CODECS PRIVATE ON) -@@ -91,6 +96,23 @@ if (WPE_VERSION VERSION_GREATER_EQUAL 1.13.90) +@@ -85,6 +90,23 @@ if (WPE_VERSION VERSION_GREATER_EQUAL 1.13.90) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_GAMEPAD PUBLIC ON) endif () @@ -22319,7 +22348,7 @@ index a70706b67e0313e084eed2d1682ef85b296c80a6..21a610a7c0ddd838ed152bc1de4440ae # Public options specific to the WPE port. Do not add any options here unless # there is a strong reason we should support changing the value of the option, # and the option is not relevant to other WebKit ports. -@@ -100,7 +122,7 @@ WEBKIT_OPTION_DEFINE(ENABLE_JOURNALD_LOG "Whether to enable journald logging" PU +@@ -94,7 +116,7 @@ WEBKIT_OPTION_DEFINE(ENABLE_JOURNALD_LOG "Whether to enable journald logging" PU WEBKIT_OPTION_DEFINE(ENABLE_WPE_PLATFORM_DRM "Whether to enable support for DRM platform" PUBLIC ON) WEBKIT_OPTION_DEFINE(ENABLE_WPE_PLATFORM_HEADLESS "Whether to enable support for headless platform" PUBLIC ON) WEBKIT_OPTION_DEFINE(ENABLE_WPE_PLATFORM_WAYLAND "Whether to enable support for Wayland platform" PUBLIC ON) @@ -22329,7 +22358,7 @@ index a70706b67e0313e084eed2d1682ef85b296c80a6..21a610a7c0ddd838ed152bc1de4440ae WEBKIT_OPTION_DEFINE(USE_ATK "Whether to enable usage of ATK." PUBLIC ON) WEBKIT_OPTION_DEFINE(USE_GBM "Whether to enable usage of GBM." PUBLIC ON) diff --git a/Source/cmake/OptionsWin.cmake b/Source/cmake/OptionsWin.cmake -index 75b07257a0f116511fafc947e64f3f98f9086a82..5e65d3e455c9fb1f03551c4561e74247952a68e4 100644 +index b45a4ed539b8081010e3ed75f57943a07b37cb1c..65487ff4f95441d870b098e56392c760fa1649d8 100644 --- a/Source/cmake/OptionsWin.cmake +++ b/Source/cmake/OptionsWin.cmake @@ -67,6 +67,29 @@ find_package(ZLIB 1.2.11 REQUIRED) @@ -22378,7 +22407,7 @@ index 75b07257a0f116511fafc947e64f3f98f9086a82..5e65d3e455c9fb1f03551c4561e74247 set(USE_ANGLE_EGL ON) diff --git a/Source/cmake/WebKitCompilerFlags.cmake b/Source/cmake/WebKitCompilerFlags.cmake -index edac4df57e4da23a7cdf234eefc3879c35c607c2..df575fa5d0c8574a8b248362167e160704bab41c 100644 +index edac4df57e4da23a7cdf234eefc3879c35c607c2..c80f8e1c80715cadfb8e64d055ef70c3a3da9f7c 100644 --- a/Source/cmake/WebKitCompilerFlags.cmake +++ b/Source/cmake/WebKitCompilerFlags.cmake @@ -122,7 +122,7 @@ macro(WEBKIT_ADD_TARGET_CXX_FLAGS _target) @@ -22390,6 +22419,26 @@ index edac4df57e4da23a7cdf234eefc3879c35c607c2..df575fa5d0c8574a8b248362167e1607 if (DEVELOPER_MODE AND DEVELOPER_MODE_FATAL_WARNINGS) if (MSVC) set(FATAL_WARNINGS_FLAG /WX) +@@ -460,6 +460,19 @@ int main() { + unset(CMAKE_REQUIRED_FLAGS) + endif () + ++if (NOT WTF_PLATFORM_COCOA) ++ set(FLOAT16_TEST_SOURCE " ++int main() { ++ _Float16 f; ++ ++ f += static_cast<_Float16>(1.0); ++ ++ return 0; ++} ++ ") ++ check_cxx_source_compiles("${FLOAT16_TEST_SOURCE}" HAVE_FLOAT16) ++endif () ++ + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND WTF_CPU_MIPS) + # Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78176. + # This only manifests when executing 32-bit code on a 64-bit diff --git a/Tools/DumpRenderTree/DerivedSources.make b/Tools/DumpRenderTree/DerivedSources.make index 576835410df6deac60f0158f1d2d1ef1e5f4c78d..9b492cfe5fef8de340a80f2af70a7d68672ef2e4 100644 --- a/Tools/DumpRenderTree/DerivedSources.make @@ -22405,10 +22454,29 @@ index 576835410df6deac60f0158f1d2d1ef1e5f4c78d..9b492cfe5fef8de340a80f2af70a7d68 WEB_PREFERENCES_GENERATED_FILES = \ TestOptionsGeneratedWebKitLegacyKeyMapping.cpp \ diff --git a/Tools/MiniBrowser/gtk/BrowserTab.c b/Tools/MiniBrowser/gtk/BrowserTab.c -index 61616b96e2f4e21aa6d098445e0f1a933e512a9c..33732da18013679a869ff8eb2b44543413f7cf0f 100644 +index 6e6c4a5abecb12c6b30e90a93c539b9b9ff27e04..851b4b9ef8da2d4277313503734138f73cdba292 100644 --- a/Tools/MiniBrowser/gtk/BrowserTab.c +++ b/Tools/MiniBrowser/gtk/BrowserTab.c -@@ -123,13 +123,16 @@ static gboolean decidePolicy(WebKitWebView *webView, WebKitPolicyDecision *decis +@@ -117,19 +117,34 @@ static void isLoadingChanged(WebKitWebView *webView, GParamSpec *paramSpec, Brow + } + } + ++static gboolean response_policy_decision_can_show(WebKitResponsePolicyDecision *responseDecision) ++{ ++ if (webkit_response_policy_decision_is_mime_type_supported(responseDecision)) ++ return TRUE; ++ const gchar* mimeType = webkit_uri_response_get_mime_type(webkit_response_policy_decision_get_response(responseDecision)); ++ if (!mimeType || mimeType[0] == '\0') ++ return FALSE; ++ // https://bugs.webkit.org/show_bug.cgi?id=277204 / Ubuntu 24.04 / glib 2.76+ or higher ++ if (g_ascii_strcasecmp(mimeType, "application/x-zerosize") == 0) ++ return TRUE; ++ return FALSE; ++} ++ + static gboolean decidePolicy(WebKitWebView *webView, WebKitPolicyDecision *decision, WebKitPolicyDecisionType decisionType, BrowserTab *tab) + { + if (decisionType != WEBKIT_POLICY_DECISION_TYPE_RESPONSE) return FALSE; WebKitResponsePolicyDecision *responseDecision = WEBKIT_RESPONSE_POLICY_DECISION(decision); @@ -22419,8 +22487,7 @@ index 61616b96e2f4e21aa6d098445e0f1a933e512a9c..33732da18013679a869ff8eb2b445434 return FALSE; - webkit_policy_decision_download(decision); -+ const gchar* mimeType = webkit_uri_response_get_mime_type(webkit_response_policy_decision_get_response(responseDecision)); -+ if (!webkit_response_policy_decision_is_mime_type_supported(responseDecision) && mimeType && mimeType[0] != '\0') { ++ if (!response_policy_decision_can_show(responseDecision)) { + webkit_policy_decision_download(decision); + return TRUE; + } @@ -22429,7 +22496,7 @@ index 61616b96e2f4e21aa6d098445e0f1a933e512a9c..33732da18013679a869ff8eb2b445434 return TRUE; } -@@ -159,6 +162,11 @@ static void loadChanged(WebKitWebView *webView, WebKitLoadEvent loadEvent, Brows +@@ -180,6 +195,11 @@ static void loadChanged(WebKitWebView *webView, WebKitLoadEvent loadEvent, Brows #endif } @@ -22441,7 +22508,7 @@ index 61616b96e2f4e21aa6d098445e0f1a933e512a9c..33732da18013679a869ff8eb2b445434 static GtkWidget *createInfoBarQuestionMessage(const char *title, const char *text) { GtkWidget *dialog = gtk_info_bar_new_with_buttons("No", GTK_RESPONSE_NO, "Yes", GTK_RESPONSE_YES, NULL); -@@ -720,6 +728,7 @@ static void browserTabConstructed(GObject *gObject) +@@ -741,6 +761,7 @@ static void browserTabConstructed(GObject *gObject) g_signal_connect(tab->webView, "notify::is-loading", G_CALLBACK(isLoadingChanged), tab); g_signal_connect(tab->webView, "decide-policy", G_CALLBACK(decidePolicy), tab); g_signal_connect(tab->webView, "load-changed", G_CALLBACK(loadChanged), tab); @@ -22449,7 +22516,7 @@ index 61616b96e2f4e21aa6d098445e0f1a933e512a9c..33732da18013679a869ff8eb2b445434 g_signal_connect(tab->webView, "load-failed-with-tls-errors", G_CALLBACK(loadFailedWithTLSerrors), tab); g_signal_connect(tab->webView, "permission-request", G_CALLBACK(decidePermissionRequest), tab); g_signal_connect(tab->webView, "run-color-chooser", G_CALLBACK(runColorChooserCallback), tab); -@@ -772,6 +781,9 @@ static char *getInternalURI(const char *uri) +@@ -793,6 +814,9 @@ static char *getInternalURI(const char *uri) if (g_str_has_prefix(uri, "about:") && !g_str_equal(uri, "about:blank")) return g_strconcat(BROWSER_ABOUT_SCHEME, uri + strlen ("about"), NULL); @@ -22571,7 +22638,7 @@ index 1fd07efb828b85b6d8def6c6cd92a0c11debfe1b..da9fac7975d477857ead2adb1d67108d typedef struct _BrowserWindow BrowserWindow; diff --git a/Tools/MiniBrowser/gtk/main.c b/Tools/MiniBrowser/gtk/main.c -index 0dd4a639e69f52e674c6bbe257e35daabd25d077..1c0af52edd19d19d30136158f25ede2618bb386a 100644 +index 8433f5360dc4a5f43b68b67192fb3d9bf5064cf1..9fa8f53e90fe5a32be1c8e7a9daa64047640e9f6 100644 --- a/Tools/MiniBrowser/gtk/main.c +++ b/Tools/MiniBrowser/gtk/main.c @@ -75,9 +75,14 @@ static char* timeZone; @@ -22738,7 +22805,7 @@ index 0dd4a639e69f52e674c6bbe257e35daabd25d077..1c0af52edd19d19d30136158f25ede26 webkit_web_context_register_uri_scheme(webContext, BROWSER_ABOUT_SCHEME, (WebKitURISchemeRequestCallback)aboutURISchemeRequestCallback, NULL, NULL); -@@ -970,9 +1062,7 @@ static void activate(GApplication *application, WebKitSettings *webkitSettings) +@@ -965,9 +1057,7 @@ static void activate(GApplication *application, WebKitSettings *webkitSettings) if (exitAfterLoad) exitAfterWebViewLoadFinishes(webView, application); } @@ -22749,7 +22816,7 @@ index 0dd4a639e69f52e674c6bbe257e35daabd25d077..1c0af52edd19d19d30136158f25ede26 } } else { WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager, defaultWebsitePolicies); -@@ -1022,7 +1112,7 @@ int main(int argc, char *argv[]) +@@ -1017,7 +1107,7 @@ int main(int argc, char *argv[]) g_option_context_add_group(context, gst_init_get_option_group()); #endif @@ -22758,7 +22825,7 @@ index 0dd4a639e69f52e674c6bbe257e35daabd25d077..1c0af52edd19d19d30136158f25ede26 webkit_settings_set_enable_developer_extras(webkitSettings, TRUE); webkit_settings_set_enable_webgl(webkitSettings, TRUE); webkit_settings_set_enable_media_stream(webkitSettings, TRUE); -@@ -1074,9 +1164,11 @@ int main(int argc, char *argv[]) +@@ -1069,9 +1159,11 @@ int main(int argc, char *argv[]) } GtkApplication *application = gtk_application_new("org.webkitgtk.MiniBrowser", G_APPLICATION_NON_UNIQUE); @@ -22771,7 +22838,7 @@ index 0dd4a639e69f52e674c6bbe257e35daabd25d077..1c0af52edd19d19d30136158f25ede26 g_clear_object(&interfaceSettings); diff --git a/Tools/MiniBrowser/wpe/main.cpp b/Tools/MiniBrowser/wpe/main.cpp -index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d35bd3c0ff 100644 +index 61760f3b78402bb79f68a4cbd20c5b96c038596c..7effb73d8b2f233bd7cb9aa27227af674eaee3f6 100644 --- a/Tools/MiniBrowser/wpe/main.cpp +++ b/Tools/MiniBrowser/wpe/main.cpp @@ -49,6 +49,9 @@ static gboolean headlessMode; @@ -22794,7 +22861,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, nullptr, "[URL]" }, { nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr } }; -@@ -276,15 +282,38 @@ static void filterSavedCallback(WebKitUserContentFilterStore *store, GAsyncResul +@@ -283,15 +289,38 @@ static void filterSavedCallback(WebKitUserContentFilterStore *store, GAsyncResul g_main_loop_quit(data->mainLoop); } @@ -22835,7 +22902,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 { auto backend = createViewBackend(defaultWindowWidthLegacyAPI, defaultWindowHeightLegacyAPI); WebKitWebViewBackend* viewBackend = nullptr; -@@ -299,12 +328,27 @@ static WebKitWebView* createWebView(WebKitWebView* webView, WebKitNavigationActi +@@ -306,12 +335,27 @@ static WebKitWebView* createWebView(WebKitWebView* webView, WebKitNavigationActi }, backend.release()); } @@ -22869,7 +22936,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 #if ENABLE_WPE_PLATFORM if (auto* wpeView = webkit_web_view_get_wpe_view(newWebView)) { -@@ -319,6 +363,10 @@ static WebKitWebView* createWebView(WebKitWebView* webView, WebKitNavigationActi +@@ -326,6 +370,10 @@ static WebKitWebView* createWebView(WebKitWebView* webView, WebKitNavigationActi g_hash_table_add(openViews, newWebView); @@ -22880,7 +22947,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 return newWebView; } -@@ -376,13 +424,89 @@ static WebKitFeature* findFeature(WebKitFeatureList* featureList, const char* id +@@ -383,13 +431,101 @@ static WebKitFeature* findFeature(WebKitFeatureList* featureList, const char* id return nullptr; } @@ -22889,6 +22956,19 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 + return createWebViewImpl(webView, nullptr, user_data); +} + ++inline bool response_policy_decision_can_show(WebKitResponsePolicyDecision* responseDecision) ++{ ++ if (webkit_response_policy_decision_is_mime_type_supported(responseDecision)) ++ return true; ++ const gchar* mimeType = webkit_uri_response_get_mime_type(webkit_response_policy_decision_get_response(responseDecision)); ++ if (!mimeType || mimeType[0] == '\0') ++ return false; ++ // https://bugs.webkit.org/show_bug.cgi?id=277204 / Ubuntu 24.04 / glib 2.76+ or higher ++ if (g_ascii_strcasecmp(mimeType, "application/x-zerosize") == 0) ++ return true; ++ return false; ++} ++ +static gboolean webViewDecidePolicy(WebKitWebView *webView, WebKitPolicyDecision *decision, WebKitPolicyDecisionType decisionType, gpointer user_data) +{ + if (decisionType == WEBKIT_POLICY_DECISION_TYPE_RESPONSE) { @@ -22896,8 +22976,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 + if (!webkit_response_policy_decision_is_main_frame_main_resource(responseDecision)) + return FALSE; + -+ const gchar* mimeType = webkit_uri_response_get_mime_type(webkit_response_policy_decision_get_response(responseDecision)); -+ if (!webkit_response_policy_decision_is_mime_type_supported(responseDecision) && mimeType && mimeType[0] != '\0') { ++ if (!response_policy_decision_can_show(responseDecision)) { + webkit_policy_decision_download(decision); + return TRUE; + } @@ -22971,7 +23050,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 webkit_network_session_set_itp_enabled(networkSession, enableITP); if (proxy) { -@@ -409,10 +533,18 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -416,10 +552,18 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* webkit_cookie_manager_set_persistent_storage(cookieManager, cookiesFile, storageType); } } @@ -22992,7 +23071,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 webkit_website_data_manager_set_itp_enabled(manager, enableITP); if (proxy) { -@@ -443,6 +575,7 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -450,6 +594,7 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* } #endif @@ -23000,7 +23079,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 WebKitUserContentManager* userContentManager = nullptr; if (contentFilter) { GFile* contentFilterFile = g_file_new_for_commandline_arg(contentFilter); -@@ -521,6 +654,15 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -528,6 +673,15 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* "autoplay", WEBKIT_AUTOPLAY_ALLOW, nullptr); @@ -23016,7 +23095,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 auto* webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, "backend", viewBackend, "web-context", webContext, -@@ -565,8 +707,6 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -572,8 +726,6 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* } #endif @@ -23025,7 +23104,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 g_signal_connect(webContext, "automation-started", G_CALLBACK(automationStartedCallback), webView); g_signal_connect(webView, "permission-request", G_CALLBACK(decidePermissionRequest), nullptr); g_signal_connect(webView, "create", G_CALLBACK(createWebView), application); -@@ -578,16 +718,11 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -585,16 +737,11 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* webkit_web_view_set_background_color(webView, &color); if (uriArguments) { @@ -23047,7 +23126,7 @@ index d340d90d8d6650b8499cf5593df481e06a3178a3..8534bc30f863e4eafdb1cc7b205034d3 webkit_web_view_load_uri(webView, "https://wpewebkit.org"); g_object_unref(webContext); -@@ -683,8 +818,14 @@ int main(int argc, char *argv[]) +@@ -690,8 +837,14 @@ int main(int argc, char *argv[]) } } @@ -23075,7 +23154,7 @@ index 1067b31bc989748dfcc5502209d36d001b9b239e..7629263fb8bc93dca6dfc01c75eed8d2 + add_subdirectory(Playwright/win) +endif () diff --git a/Tools/Scripts/build-webkit b/Tools/Scripts/build-webkit -index 39f91de9501cf38331ce567d30c9cefa25b28510..af2eeec87bb43f9ddfc6d583c35a8e7a87d5127c 100755 +index 470dcf1f4440f3db350fc416150a2e792a2777c7..31cbc0d22e63d054cf6edcc220a1c6b407cc1f24 100755 --- a/Tools/Scripts/build-webkit +++ b/Tools/Scripts/build-webkit @@ -273,7 +273,7 @@ if (isAppleCocoaWebKit()) { @@ -23103,7 +23182,7 @@ index 9e53f459e444b9c10fc5248f0e8059df6c1e0041..c17c875a7dd3ca05c4489578ab32378b "${WebKitTestRunner_DIR}/InjectedBundle/Bindings/AccessibilityController.idl" "${WebKitTestRunner_DIR}/InjectedBundle/Bindings/AccessibilityTextMarker.idl" diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp -index 076ad1f7005f732f8529955cfe3b6a8dd5212949..dee6f57eb5fff12de9cdaad230e894b0b9b15b88 100644 +index c0cf0ce74772ae0a4bd20f7e0f48f94e0c689e52..4efa6437372b6ba4afd6a3e246ba118832337307 100644 --- a/Tools/WebKitTestRunner/TestController.cpp +++ b/Tools/WebKitTestRunner/TestController.cpp @@ -964,6 +964,7 @@ void TestController::createWebViewWithOptions(const TestOptions& options) @@ -23115,12 +23194,12 @@ index 076ad1f7005f732f8529955cfe3b6a8dd5212949..dee6f57eb5fff12de9cdaad230e894b0 decidePolicyForMediaKeySystemPermissionRequest, queryPermission, diff --git a/Tools/WebKitTestRunner/mac/EventSenderProxy.mm b/Tools/WebKitTestRunner/mac/EventSenderProxy.mm -index c7c925b66ea04d8f7a44fb7410ad45acb74f022f..b4738697e5ecb5c947d2bc1864a0f3907d9a3795 100644 +index c1381b06b378a5121be926b1dfda3e5509bcd051..773e5882b6f2269201ca49433d0b69866493118d 100644 --- a/Tools/WebKitTestRunner/mac/EventSenderProxy.mm +++ b/Tools/WebKitTestRunner/mac/EventSenderProxy.mm -@@ -950,4 +950,51 @@ void EventSenderProxy::scaleGestureEnd(double scale) - - #endif // ENABLE(MAC_GESTURE_EVENTS) +@@ -961,4 +961,51 @@ void EventSenderProxy::waitForPendingMouseEvents() + } + } +#if ENABLE(TOUCH_EVENTS) +void EventSenderProxy::addTouchPoint(int, int) diff --git a/docs/src/accessibility-testing-java.md b/docs/src/accessibility-testing-java.md index d62bac64fa..13f9c4e3e4 100644 --- a/docs/src/accessibility-testing-java.md +++ b/docs/src/accessibility-testing-java.md @@ -70,22 +70,24 @@ For example, you can use [`AxeBuilder.include()`](https://github.com/dequelabs/a `AxeBuilder.analyze()` will scan the page *in its current state* when you call it. To scan parts of a page that are revealed based on UI interactions, use [Locators](./locators.md) to interact with the page before invoking `analyze()`: ```java -@Test -void navigationMenuFlyoutShouldNotHaveAutomaticallyDetectableAccessibilityViolations() throws Exception { - page.navigate("https://your-site.com/"); +public class HomepageTests { + @Test + void navigationMenuFlyoutShouldNotHaveAutomaticallyDetectableAccessibilityViolations() throws Exception { + page.navigate("https://your-site.com/"); - page.locator("button[aria-label=\"Navigation Menu\"]").click(); + page.locator("button[aria-label=\"Navigation Menu\"]").click(); - // It is important to waitFor() the page to be in the desired - // state *before* running analyze(). Otherwise, axe might not - // find all the elements your test expects it to scan. - page.locator("#navigation-menu-flyout").waitFor(); + // It is important to waitFor() the page to be in the desired + // state *before* running analyze(). Otherwise, axe might not + // find all the elements your test expects it to scan. + page.locator("#navigation-menu-flyout").waitFor(); - AxeResults accessibilityScanResults = new AxeBuilder(page) - .include(Arrays.asList("#navigation-menu-flyout")) - .analyze(); + AxeResults accessibilityScanResults = new AxeBuilder(page) + .include(Arrays.asList("#navigation-menu-flyout")) + .analyze(); - assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations()); + assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations()); + } } ``` @@ -158,38 +160,40 @@ This approach avoids the downsides of using `AxeBuilder.exclude()` at the cost o Here is an example of using fingerprints based on only rule IDs and "target" selectors pointing to each violation: ```java -@Test -shouldOnlyHaveAccessibilityViolationsMatchingKnownFingerprints() throws Exception { - page.navigate("https://your-site.com/"); +public class HomepageTests { + @Test + shouldOnlyHaveAccessibilityViolationsMatchingKnownFingerprints() throws Exception { + page.navigate("https://your-site.com/"); - AxeResults accessibilityScanResults = new AxeBuilder(page).analyze(); + AxeResults accessibilityScanResults = new AxeBuilder(page).analyze(); - List violationFingerprints = fingerprintsFromScanResults(accessibilityScanResults); + List violationFingerprints = fingerprintsFromScanResults(accessibilityScanResults); - assertEquals(Arrays.asList( - new ViolationFingerprint("aria-roles", "[span[role=\"invalid\"]]"), - new ViolationFingerprint("color-contrast", "[li:nth-child(2) > span]"), - new ViolationFingerprint("label", "[input]") - ), violationFingerprints); -} + assertEquals(Arrays.asList( + new ViolationFingerprint("aria-roles", "[span[role=\"invalid\"]]"), + new ViolationFingerprint("color-contrast", "[li:nth-child(2) > span]"), + new ViolationFingerprint("label", "[input]") + ), violationFingerprints); + } -// You can make your "fingerprint" as specific as you like. This one considers a violation to be -// "the same" if it corresponds the same Axe rule on the same element. -// -// Using a record type makes it easy to compare fingerprints with assertEquals -public record ViolationFingerprint(String ruleId, String target) { } + // You can make your "fingerprint" as specific as you like. This one considers a violation to be + // "the same" if it corresponds the same Axe rule on the same element. + // + // Using a record type makes it easy to compare fingerprints with assertEquals + public record ViolationFingerprint(String ruleId, String target) { } -public List fingerprintsFromScanResults(AxeResults results) { - return results.getViolations().stream() - // Each violation refers to one rule and multiple "nodes" which violate it - .flatMap(violation -> violation.getNodes().stream() - .map(node -> new ViolationFingerprint( - violation.getId(), - // Each node contains a "target", which is a CSS selector that uniquely identifies it - // If the page involves iframes or shadow DOMs, it may be a chain of CSS selectors - node.getTarget().toString() - ))) - .collect(Collectors.toList()); + public List fingerprintsFromScanResults(AxeResults results) { + return results.getViolations().stream() + // Each violation refers to one rule and multiple "nodes" which violate it + .flatMap(violation -> violation.getNodes().stream() + .map(node -> new ViolationFingerprint( + violation.getId(), + // Each node contains a "target", which is a CSS selector that uniquely identifies it + // If the page involves iframes or shadow DOMs, it may be a chain of CSS selectors + node.getTarget().toString() + ))) + .collect(Collectors.toList()); + } } ``` @@ -208,11 +212,11 @@ This example fixture creates an `AxeBuilder` object which is pre-configured with ```java class AxeTestFixtures extends TestFixtures { - AxeBuilder makeAxeBuilder() { - return new AxeBuilder(page) - .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']) - .exclude('#commonly-reused-element-with-known-issue'); - } + AxeBuilder makeAxeBuilder() { + return new AxeBuilder(page) + .withTags(new String[]{"wcag2a", "wcag2aa", "wcag21a", "wcag21aa"}) + .exclude("#commonly-reused-element-with-known-issue"); + } } ``` @@ -229,7 +233,7 @@ public class HomepageTests extends AxeTestFixtures { AxeResults accessibilityScanResults = makeAxeBuilder() // Automatically uses the shared AxeBuilder configuration, // but supports additional test-specific configuration too - .include('#specific-element-under-test') + .include("#specific-element-under-test") .analyze(); assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations()); diff --git a/docs/src/api-testing-java.md b/docs/src/api-testing-java.md index 987aad5de8..e8020e12ce 100644 --- a/docs/src/api-testing-java.md +++ b/docs/src/api-testing-java.md @@ -194,6 +194,7 @@ public class TestGitHubAPI { These tests assume that repository exists. You probably want to create a new one before running tests and delete it afterwards. Use `@BeforeAll` and `@AfterAll` hooks for that. ```java +public class TestGitHubAPI { // ... void createTestRepository() { @@ -223,6 +224,7 @@ These tests assume that repository exists. You probably want to create a new one disposeAPIRequestContext(); closePlaywright(); } +} ``` ### Complete test example @@ -381,18 +383,20 @@ The following test creates a new issue via API and then navigates to the list of project to check that it appears at the top of the list. The check is performed using [LocatorAssertions]. ```java -@Test -void lastCreatedIssueShouldBeFirstInTheList() { - Map data = new HashMap<>(); - data.put("title", "[Feature] request 1"); - data.put("body", "Feature description"); - APIResponse newIssue = request.post("/repos/" + USER + "/" + REPO + "/issues", - RequestOptions.create().setData(data)); - assertTrue(newIssue.ok()); +public class TestGitHubAPI { + @Test + void lastCreatedIssueShouldBeFirstInTheList() { + Map data = new HashMap<>(); + data.put("title", "[Feature] request 1"); + data.put("body", "Feature description"); + APIResponse newIssue = request.post("/repos/" + USER + "/" + REPO + "/issues", + RequestOptions.create().setData(data)); + assertTrue(newIssue.ok()); - page.navigate("https://github.com/" + USER + "/" + REPO + "/issues"); - Locator firstIssue = page.locator("a[data-hovercard-type='issue']").first(); - assertThat(firstIssue).hasText("[Feature] request 1"); + page.navigate("https://github.com/" + USER + "/" + REPO + "/issues"); + Locator firstIssue = page.locator("a[data-hovercard-type='issue']").first(); + assertThat(firstIssue).hasText("[Feature] request 1"); + } } ``` @@ -402,18 +406,20 @@ The following test creates a new issue via user interface in the browser and the it was created: ```java -@Test -void lastCreatedIssueShouldBeOnTheServer() { - page.navigate("https://github.com/" + USER + "/" + REPO + "/issues"); - page.locator("text=New Issue").click(); - page.locator("[aria-label='Title']").fill("Bug report 1"); - page.locator("[aria-label='Comment body']").fill("Bug description"); - page.locator("text=Submit new issue").click(); - String issueId = page.url().substring(page.url().lastIndexOf('/')); +public class TestGitHubAPI { + @Test + void lastCreatedIssueShouldBeOnTheServer() { + page.navigate("https://github.com/" + USER + "/" + REPO + "/issues"); + page.locator("text=New Issue").click(); + page.locator("[aria-label='Title']").fill("Bug report 1"); + page.locator("[aria-label='Comment body']").fill("Bug description"); + page.locator("text=Submit new issue").click(); + String issueId = page.url().substring(page.url().lastIndexOf('/')); - APIResponse newIssue = request.get("https://github.com/" + USER + "/" + REPO + "/issues/" + issueId); - assertThat(newIssue).isOK(); - assertTrue(newIssue.text().contains("Bug report 1")); + APIResponse newIssue = request.get("https://github.com/" + USER + "/" + REPO + "/issues/" + issueId); + assertThat(newIssue).isOK(); + assertTrue(newIssue.text().contains("Bug report 1")); + } } ``` diff --git a/docs/src/api/class-androiddevice.md b/docs/src/api/class-androiddevice.md index cff6c2e5de..729b36f922 100644 --- a/docs/src/api/class-androiddevice.md +++ b/docs/src/api/class-androiddevice.md @@ -177,7 +177,9 @@ Launches a process in the shell on the device and returns a socket to communicat ### param: AndroidDevice.open.command * since: v1.9 -- `command` <[string]> Shell command to execute. +- `command` <[string]> + +Shell command to execute. ## async method: AndroidDevice.pinchClose * since: v1.9 @@ -445,7 +447,7 @@ Either a predicate that receives an event or an options object. Optional. * since: v1.9 - returns: <[AndroidWebView]> -This method waits until [AndroidWebView] matching the [`option: selector`] is opened and returns it. If there is already an open [AndroidWebView] matching the [`option: selector`], returns immediately. +This method waits until [AndroidWebView] matching the [`param: selector`] is opened and returns it. If there is already an open [AndroidWebView] matching the [`param: selector`], returns immediately. ### param: AndroidDevice.webView.selector * since: v1.9 diff --git a/docs/src/api/class-apirequestcontext.md b/docs/src/api/class-apirequestcontext.md index f56087d1bd..40742e2a1f 100644 --- a/docs/src/api/class-apirequestcontext.md +++ b/docs/src/api/class-apirequestcontext.md @@ -247,7 +247,7 @@ var data = new Dictionary() { await Request.FetchAsync("https://example.com/api/createBook", new() { Method = "post", DataObject = data }); ``` -The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` encoding. Use [FormData] to construct request body and pass it to the request as [`option: multipart`] parameter: +The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` encoding, by specifiying the `multipart` parameter: ```js const form = new FormData(); @@ -300,6 +300,7 @@ multipart.Set("fileField", file); await Request.FetchAsync("https://example.com/api/uploadScript", new() { Method = "post", Multipart = multipart }); ``` + ### param: APIRequestContext.fetch.urlOrRequest * since: v1.16 - `urlOrRequest` <[string]|[Request]> diff --git a/docs/src/api/class-apiresponseassertions.md b/docs/src/api/class-apiresponseassertions.md index 6fce864d03..9584365d88 100644 --- a/docs/src/api/class-apiresponseassertions.md +++ b/docs/src/api/class-apiresponseassertions.md @@ -14,15 +14,15 @@ test('navigates to login', async ({ page }) => { ``` ```java -... +// ... import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; public class TestPage { - ... + // ... @Test void navigatesToLoginPage() { - ... - APIResponse response = page.request().get('https://playwright.dev'); + // ... + APIResponse response = page.request().get("https://playwright.dev"); assertThat(response).isOK(); } } diff --git a/docs/src/api/class-browser.md b/docs/src/api/class-browser.md index 59cf4c99c0..4dabfc52e4 100644 --- a/docs/src/api/class-browser.md +++ b/docs/src/api/class-browser.md @@ -18,15 +18,15 @@ const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'. import com.microsoft.playwright.*; public class Example { - public static void main(String[] args) { - try (Playwright playwright = Playwright.create()) { - BrowserType firefox = playwright.firefox() - Browser browser = firefox.launch(); - Page page = browser.newPage(); - page.navigate('https://example.com'); - browser.close(); - } - } + public static void main(String[] args) { + try (Playwright playwright = Playwright.create()) { + BrowserType firefox = playwright.firefox(); + Browser browser = firefox.launch(); + Page page = browser.newPage(); + page.navigate("https://example.com"); + browser.close(); + } + } } ``` @@ -202,7 +202,7 @@ Browser browser = playwright.firefox().launch(); // Or 'chromium' or 'webkit'. BrowserContext context = browser.newContext(); // Create a new page in a pristine context. Page page = context.newPage(); -page.navigate('https://example.com'); +page.navigate("https://example.com"); // Graceful close up everything context.close(); @@ -331,7 +331,7 @@ await browser.stopTracing(); ```java browser.startTracing(page, new Browser.StartTracingOptions() .setPath(Paths.get("trace.json"))); -page.goto('https://www.google.com'); +page.navigate("https://www.google.com"); browser.stopTracing(); ``` diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index 43396f4957..8d6c57a3e3 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -655,7 +655,7 @@ import com.microsoft.playwright.*; public class Example { public static void main(String[] args) { try (Playwright playwright = Playwright.create()) { - BrowserType webkit = playwright.webkit() + BrowserType webkit = playwright.webkit(); Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); BrowserContext context = browser.newContext(); context.exposeBinding("pageURL", (source, args) -> source.page().url()); @@ -813,8 +813,9 @@ import java.util.Base64; public class Example { public static void main(String[] args) { try (Playwright playwright = Playwright.create()) { - BrowserType webkit = playwright.webkit() + BrowserType webkit = playwright.webkit(); Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); + BrowserContext context = browser.newContext(); context.exposeFunction("sha256", args -> { String text = (String) args[0]; MessageDigest crypto; @@ -1198,7 +1199,7 @@ Enabling routing disables http cache. - `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]> A glob pattern, regex pattern or predicate receiving [URL] to match while routing. -When a [`option: baseURL`] via the context options was provided and the passed URL is a path, +When a [`option: Browser.newContext.baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. ### param: BrowserContext.route.handler @@ -1266,6 +1267,99 @@ When set to `minimal`, only record information necessary for routing from HAR. T Optional setting to control resource content management. If `attach` is specified, resources are persisted as separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file. + +## async method: BrowserContext.routeWebSocket +* since: v1.48 + +This method allows to modify websocket connections that are made by any page in the browser context. + +Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this method before creating any pages. + +**Usage** + +Below is an example of a simple handler that blocks some websocket messages. +See [WebSocketRoute] for more details and examples. + +```js +await context.routeWebSocket('/ws', async ws => { + ws.routeSend(message => { + if (message === 'to-be-blocked') + return; + ws.send(message); + }); + await ws.connect(); +}); +``` + +```java +context.routeWebSocket("/ws", ws -> { + ws.routeSend(message -> { + if ("to-be-blocked".equals(message)) + return; + ws.send(message); + }); + ws.connect(); +}); +``` + +```python async +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "to-be-blocked": + return + ws.send(message) + +async def handler(ws: WebSocketRoute): + ws.route_send(lambda message: message_handler(ws, message)) + await ws.connect() + +await context.route_web_socket("/ws", handler) +``` + +```python sync +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "to-be-blocked": + return + ws.send(message) + +def handler(ws: WebSocketRoute): + ws.route_send(lambda message: message_handler(ws, message)) + ws.connect() + +context.route_web_socket("/ws", handler) +``` + +```csharp +await context.RouteWebSocketAsync("/ws", async ws => { + ws.RouteSend(message => { + if (message == "to-be-blocked") + return; + ws.Send(message); + }); + await ws.ConnectAsync(); +}); +``` + +### param: BrowserContext.routeWebSocket.url +* since: v1.48 +- `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]> + +Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the [`option: Browser.newContext.baseURL`] context option. + +### param: BrowserContext.routeWebSocket.handler +* since: v1.48 +* langs: js, python +- `handler` <[function]\([WebSocketRoute]\): [Promise|any]> + +Handler function to route the WebSocket. + +### param: BrowserContext.routeWebSocket.handler +* since: v1.48 +* langs: csharp, java +- `handler` <[function]\([WebSocketRoute]\)> + +Handler function to route the WebSocket. + + ## method: BrowserContext.serviceWorkers * since: v1.11 * langs: js, python diff --git a/docs/src/api/class-consolemessage.md b/docs/src/api/class-consolemessage.md index 2268ee3ed1..347838ae42 100644 --- a/docs/src/api/class-consolemessage.md +++ b/docs/src/api/class-consolemessage.md @@ -44,8 +44,8 @@ ConsoleMessage msg = page.waitForConsoleMessage(() -> { }); // Deconstruct console.log arguments -msg.args().get(0).jsonValue() // hello -msg.args().get(1).jsonValue() // 42 +msg.args().get(0).jsonValue(); // hello +msg.args().get(1).jsonValue(); // 42 ``` ```python async diff --git a/docs/src/api/class-formdata.md b/docs/src/api/class-formdata.md index 5ca85b361d..c5f4bcb51d 100644 --- a/docs/src/api/class-formdata.md +++ b/docs/src/api/class-formdata.md @@ -6,7 +6,7 @@ The [FormData] is used create form data that is sent via [APIRequestContext]. ```java import com.microsoft.playwright.options.FormData; -... +// ... FormData form = FormData.create() .set("firstName", "John") .set("lastName", "Doe") @@ -28,7 +28,7 @@ the new value onto the end of the existing set of values. ```java import com.microsoft.playwright.options.FormData; -... +// ... FormData form = FormData.create() // Only name and value are set. .append("firstName", "John") @@ -100,7 +100,7 @@ Sets a field on the form. File values can be passed either as `Path` or as `File ```java import com.microsoft.playwright.options.FormData; -... +// ... FormData form = FormData.create() // Only name and value are set. .set("firstName", "John") diff --git a/docs/src/api/class-frame.md b/docs/src/api/class-frame.md index f3f308622f..e286c63bfa 100644 --- a/docs/src/api/class-frame.md +++ b/docs/src/api/class-frame.md @@ -280,7 +280,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.click.timeout = %%-input-timeout-js-%% * since: v1.8 -### option: Frame.click.trial = %%-input-trial-%% +### option: Frame.click.trial = %%-input-trial-with-modifiers-%% * since: v1.11 ## async method: Frame.content @@ -341,7 +341,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.dblclick.timeout = %%-input-timeout-js-%% * since: v1.8 -### option: Frame.dblclick.trial = %%-input-trial-%% +### option: Frame.dblclick.trial = %%-input-trial-with-modifiers-%% * since: v1.11 ## async method: Frame.dispatchEvent @@ -1153,7 +1153,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.hover.timeout = %%-input-timeout-js-%% * since: v1.8 -### option: Frame.hover.trial = %%-input-trial-%% +### option: Frame.hover.trial = %%-input-trial-with-modifiers-%% * since: v1.11 ### option: Frame.hover.noWaitAfter = %%-input-no-wait-after-removed-%% @@ -1304,7 +1304,7 @@ Returns whether the element is [enabled](../actionability.md#enabled). * discouraged: Use locator-based [`method: Locator.isHidden`] instead. Read more about [locators](../locators.md). - returns: <[boolean]> -Returns whether the element is hidden, the opposite of [visible](../actionability.md#visible). [`option: selector`] that does not match any elements is considered hidden. +Returns whether the element is hidden, the opposite of [visible](../actionability.md#visible). [`param: selector`] that does not match any elements is considered hidden. ### param: Frame.isHidden.selector = %%-input-selector-%% * since: v1.8 @@ -1322,7 +1322,7 @@ Returns whether the element is hidden, the opposite of [visible](../actionabilit * discouraged: Use locator-based [`method: Locator.isVisible`] instead. Read more about [locators](../locators.md). - returns: <[boolean]> -Returns whether the element is [visible](../actionability.md#visible). [`option: selector`] that does not match any elements is considered not visible. +Returns whether the element is [visible](../actionability.md#visible). [`param: selector`] that does not match any elements is considered not visible. ### param: Frame.isVisible.selector = %%-input-selector-%% * since: v1.8 @@ -1703,7 +1703,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.tap.timeout = %%-input-timeout-js-%% * since: v1.8 -### option: Frame.tap.trial = %%-input-trial-%% +### option: Frame.tap.trial = %%-input-trial-with-modifiers-%% * since: v1.11 ## async method: Frame.textContent diff --git a/docs/src/api/class-framelocator.md b/docs/src/api/class-framelocator.md index 9851a35f96..7b6fb8f1bc 100644 --- a/docs/src/api/class-framelocator.md +++ b/docs/src/api/class-framelocator.md @@ -1,30 +1,30 @@ # class: FrameLocator * since: v1.17 -FrameLocator represents a view to the `iframe` on the page. It captures the logic sufficient to retrieve the `iframe` and locate elements in that iframe. FrameLocator can be created with either [`method: Page.frameLocator`] or [`method: Locator.frameLocator`] method. +FrameLocator represents a view to the `iframe` on the page. It captures the logic sufficient to retrieve the `iframe` and locate elements in that iframe. FrameLocator can be created with either [`method: Locator.contentFrame`], [`method: Page.frameLocator`] or [`method: Locator.frameLocator`] method. ```js -const locator = page.frameLocator('#my-frame').getByText('Submit'); +const locator = page.locator('#my-frame').contentFrame().getByText('Submit'); await locator.click(); ``` ```java -Locator locator = page.frameLocator("#my-frame").getByText("Submit"); +Locator locator = page.locator("#my-frame").contentFrame().getByText("Submit"); locator.click(); ``` ```python async -locator = page.frame_locator("#my-frame").get_by_text("Submit") +locator = page.locator("#my-frame").content_frame.get_by_text("Submit") await locator.click() ``` ```python sync -locator = page.frame_locator("my-frame").get_by_text("Submit") +locator = page.locator("my-frame").content_frame.get_by_text("Submit") locator.click() ``` ```csharp -var locator = page.FrameLocator("#my-frame").GetByText("Submit"); +var locator = page.Locator("#my-frame").ContentFrame.GetByText("Submit"); await locator.ClickAsync(); ``` @@ -34,42 +34,42 @@ Frame locators are strict. This means that all operations on frame locators will ```js // Throws if there are several frames in DOM: -await page.frameLocator('.result-frame').getByRole('button').click(); +await page.locator('.result-frame').contentFrame().getByRole('button').click(); // Works because we explicitly tell locator to pick the first frame: -await page.frameLocator('.result-frame').first().getByRole('button').click(); +await page.locator('.result-frame').contentFrame().first().getByRole('button').click(); ``` ```python async # Throws if there are several frames in DOM: -await page.frame_locator('.result-frame').get_by_role('button').click() +await page.locator('.result-frame').content_frame.get_by_role('button').click() # Works because we explicitly tell locator to pick the first frame: -await page.frame_locator('.result-frame').first.get_by_role('button').click() +await page.locator('.result-frame').first.content_frame.get_by_role('button').click() ``` ```python sync # Throws if there are several frames in DOM: -page.frame_locator('.result-frame').get_by_role('button').click() +page.locator('.result-frame').content_frame.get_by_role('button').click() # Works because we explicitly tell locator to pick the first frame: -page.frame_locator('.result-frame').first.get_by_role('button').click() +page.locator('.result-frame').first.content_frame.get_by_role('button').click() ``` ```java // Throws if there are several frames in DOM: -page.frame_locator(".result-frame").getByRole(AriaRole.BUTTON).click(); +page.locator(".result-frame").contentFrame().getByRole(AriaRole.BUTTON).click(); // Works because we explicitly tell locator to pick the first frame: -page.frame_locator(".result-frame").first().getByRole(AriaRole.BUTTON).click(); +page.locator(".result-frame").first().contentFrame().getByRole(AriaRole.BUTTON).click(); ``` ```csharp // Throws if there are several frames in DOM: -await page.FrameLocator(".result-frame").GetByRole(AriaRole.Button).ClickAsync(); +await page.Locator(".result-frame").ContentFrame.GetByRole(AriaRole.Button).ClickAsync(); // Works because we explicitly tell locator to pick the first frame: -await page.FrameLocator(".result-frame").First.getByRole(AriaRole.Button).ClickAsync(); +await page.Locator(".result-frame").First.ContentFrame.getByRole(AriaRole.Button).ClickAsync(); ``` **Converting Locator to FrameLocator** @@ -82,6 +82,7 @@ If you have a [FrameLocator] object it can be converted to [Locator] pointing to ## method: FrameLocator.first +* deprecated: Use [`method: Locator.first`] followed by [`method: Locator.contentFrame`] instead. * since: v1.17 - returns: <[FrameLocator]> @@ -171,6 +172,7 @@ in that iframe. ### option: FrameLocator.getByTitle.exact = %%-locator-get-by-text-exact-%% ## method: FrameLocator.last +* deprecated: Use [`method: Locator.last`] followed by [`method: Locator.contentFrame`] instead. * since: v1.17 - returns: <[FrameLocator]> @@ -195,6 +197,7 @@ Returns locator to the last matching frame. * since: v1.33 ## method: FrameLocator.nth +* deprecated: Use [`method: Locator.nth`] followed by [`method: Locator.contentFrame`] instead. * since: v1.17 - returns: <[FrameLocator]> @@ -217,37 +220,36 @@ For a reverse operation, use [`method: Locator.contentFrame`]. **Usage** ```js -const frameLocator = page.frameLocator('iframe[name="embedded"]'); +const frameLocator = page.locator('iframe[name="embedded"]').contentFrame(); // ... const locator = frameLocator.owner(); await expect(locator).toBeVisible(); ``` ```java -FrameLocator frameLocator = page.frameLocator("iframe[name=\"embedded\"]"); +FrameLocator frameLocator = page.locator("iframe[name=\"embedded\"]").contentFrame(); // ... Locator locator = frameLocator.owner(); assertThat(locator).isVisible(); ``` ```python async -frame_locator = page.frame_locator("iframe[name=\"embedded\"]") +frame_locator = page.locator("iframe[name=\"embedded\"]").content_frame # ... locator = frame_locator.owner await expect(locator).to_be_visible() ``` ```python sync -frame_locator = page.frame_locator("iframe[name=\"embedded\"]") +frame_locator = page.locator("iframe[name=\"embedded\"]").content_frame # ... locator = frame_locator.owner expect(locator).to_be_visible() ``` ```csharp -var frameLocator = Page.FrameLocator("iframe[name=\"embedded\"]"); +var frameLocator = Page.Locator("iframe[name=\"embedded\"]").ContentFrame; // ... var locator = frameLocator.Owner; await Expect(locator).ToBeVisibleAsync(); ``` - diff --git a/docs/src/api/class-keyboard.md b/docs/src/api/class-keyboard.md index 64a539cfbe..f86c4ad11f 100644 --- a/docs/src/api/class-keyboard.md +++ b/docs/src/api/class-keyboard.md @@ -257,7 +257,7 @@ await browser.close(); Page page = browser.newPage(); page.navigate("https://keycode.info"); page.keyboard().press("A"); -page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")); +page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"))); page.keyboard().press("ArrowLeft"); page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png"))); page.keyboard().press("Shift+O"); diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index 88658b5494..8f0b53e16d 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -38,7 +38,7 @@ for li in page.get_by_role('listitem').all(): ``` ```java -for (Locator li : page.getByRole('listitem').all()) +for (Locator li : page.getByRole("listitem").all()) li.click(); ``` @@ -54,7 +54,7 @@ foreach (var li in await page.GetByRole("listitem").AllAsync()) Returns an array of `node.innerText` values for all matching nodes. :::warning[Asserting text] -If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] with [`option: useInnerText`] option to avoid flakiness. See [assertions guide](../test-assertions.md) for more details. +If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] with [`option: LocatorAssertions.toHaveText.useInnerText`] option to avoid flakiness. See [assertions guide](../test-assertions.md) for more details. ::: **Usage** @@ -433,7 +433,7 @@ await page.Locator("canvas").ClickAsync(new() { ### option: Locator.click.timeout = %%-input-timeout-js-%% * since: v1.14 -### option: Locator.click.trial = %%-input-trial-%% +### option: Locator.click.trial = %%-input-trial-with-modifiers-%% * since: v1.14 ## async method: Locator.count @@ -516,7 +516,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Locator.dblclick.timeout = %%-input-timeout-js-%% * since: v1.14 -### option: Locator.dblclick.trial = %%-input-trial-%% +### option: Locator.dblclick.trial = %%-input-trial-with-modifiers-%% * since: v1.14 ## async method: Locator.dispatchEvent @@ -1266,7 +1266,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Locator.hover.timeout = %%-input-timeout-js-%% * since: v1.14 -### option: Locator.hover.trial = %%-input-trial-%% +### option: Locator.hover.trial = %%-input-trial-with-modifiers-%% * since: v1.14 ### option: Locator.hover.noWaitAfter = %%-input-no-wait-after-removed-%% @@ -1291,7 +1291,7 @@ Returns the [`element.innerHTML`](https://developer.mozilla.org/en-US/docs/Web/A Returns the [`element.innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText). :::warning[Asserting text] -If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] with [`option: useInnerText`] option to avoid flakiness. See [assertions guide](../test-assertions.md) for more details. +If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] with [`option: LocatorAssertions.toHaveText.useInnerText`] option to avoid flakiness. See [assertions guide](../test-assertions.md) for more details. ::: ### option: Locator.innerText.timeout = %%-input-timeout-%% @@ -2331,7 +2331,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Locator.tap.timeout = %%-input-timeout-js-%% * since: v1.14 -### option: Locator.tap.trial = %%-input-trial-%% +### option: Locator.tap.trial = %%-input-trial-with-modifiers-%% * since: v1.14 ## async method: Locator.textContent diff --git a/docs/src/api/class-locatorassertions.md b/docs/src/api/class-locatorassertions.md index 1ff5e52119..bfff2b07ad 100644 --- a/docs/src/api/class-locatorassertions.md +++ b/docs/src/api/class-locatorassertions.md @@ -14,14 +14,14 @@ test('status becomes submitted', async ({ page }) => { ``` ```java -... +// ... import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; public class TestLocator { - ... + // ... @Test void statusBecomesSubmitted() { - ... + // ... page.getByRole(AriaRole.BUTTON).click(); assertThat(page.locator(".status")).hasText("Submitted"); } @@ -2048,7 +2048,7 @@ await expect(locator).toHaveValues([/R/, /G/]); ``` ```java -page.locator("id=favorite-colors").selectOption(["R", "G"]); +page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"}); assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") }); ``` diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index d36a96ee72..7a3be809c6 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -812,7 +812,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Page.click.timeout = %%-input-timeout-js-%% * since: v1.8 -### option: Page.click.trial = %%-input-trial-%% +### option: Page.click.trial = %%-input-trial-with-modifiers-%% * since: v1.11 ## async method: Page.close @@ -915,7 +915,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Page.dblclick.timeout = %%-input-timeout-js-%% * since: v1.8 -### option: Page.dblclick.trial = %%-input-trial-%% +### option: Page.dblclick.trial = %%-input-trial-with-modifiers-%% * since: v1.11 ## async method: Page.dispatchEvent @@ -1041,9 +1041,9 @@ await page.dragAndDrop('#source', '#target', { ``` ```java -page.dragAndDrop("#source", '#target'); +page.dragAndDrop("#source", "#target"); // or specify exact positions relative to the top-left corners of the elements: -page.dragAndDrop("#source", '#target', new Page.DragAndDropOptions() +page.dragAndDrop("#source", "#target", new Page.DragAndDropOptions() .setSourcePosition(34, 7).setTargetPosition(10, 20)); ``` @@ -1716,7 +1716,7 @@ public class Example { public static void main(String[] args) { try (Playwright playwright = Playwright.create()) { BrowserType webkit = playwright.webkit(); - Browser browser = webkit.launch({ headless: false }); + Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); BrowserContext context = browser.newContext(); Page page = context.newPage(); page.exposeBinding("pageURL", (source, args) -> source.page().url()); @@ -1886,26 +1886,27 @@ public class Example { public static void main(String[] args) { try (Playwright playwright = Playwright.create()) { BrowserType webkit = playwright.webkit(); - Browser browser = webkit.launch({ headless: false }); + Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); Page page = browser.newPage(); page.exposeFunction("sha256", args -> { - String text = (String) args[0]; - MessageDigest crypto; try { - crypto = MessageDigest.getInstance("SHA-256"); + String text = (String) args[0]; + MessageDigest crypto = MessageDigest.getInstance("SHA-256"); + byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(token); } catch (NoSuchAlgorithmException e) { return null; } - byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8)); - return Base64.getEncoder().encodeToString(token); }); - page.setContent("\n" + "\n" + - "
\n"); + "
" + ); page.click("button"); } } @@ -2106,7 +2107,7 @@ const frame = page.frame({ url: /.*domain.*/ }); ``` ```java -Frame frame = page.frameByUrl(Pattern.compile(".*domain.*"); +Frame frame = page.frameByUrl(Pattern.compile(".*domain.*")); ``` ```py @@ -2333,10 +2334,57 @@ last redirect. If cannot go forward, returns `null`. Navigate to the next page in history. -## async method: Page.forceGarbageCollection -* since: v1.47 +## async method: Page.requestGC +* since: v1.48 -Force the browser to perform garbage collection. +Request the page to perform garbage collection. Note that there is no guarantee that all unreachable objects will be collected. + +This is useful to help detect memory leaks. For example, if your page has a large object `'suspect'` that might be leaked, you can check that it does not leak by using a [`WeakRef`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef). + +```js +// 1. In your page, save a WeakRef for the "suspect". +await page.evaluate(() => globalThis.suspectWeakRef = new WeakRef(suspect)); +// 2. Request garbage collection. +await page.requestGC(); +// 3. Check that weak ref does not deref to the original object. +expect(await page.evaluate(() => !globalThis.suspectWeakRef.deref())).toBe(true); +``` + +```java +// 1. In your page, save a WeakRef for the "suspect". +page.evaluate("globalThis.suspectWeakRef = new WeakRef(suspect)"); +// 2. Request garbage collection. +page.requestGC(); +// 3. Check that weak ref does not deref to the original object. +assertTrue(page.evaluate("!globalThis.suspectWeakRef.deref()")); +``` + +```python async +# 1. In your page, save a WeakRef for the "suspect". +await page.evaluate("globalThis.suspectWeakRef = new WeakRef(suspect)") +# 2. Request garbage collection. +await page.request_gc() +# 3. Check that weak ref does not deref to the original object. +assert await page.evaluate("!globalThis.suspectWeakRef.deref()") +``` + +```python sync +# 1. In your page, save a WeakRef for the "suspect". +page.evaluate("globalThis.suspectWeakRef = new WeakRef(suspect)") +# 2. Request garbage collection. +page.request_gc() +# 3. Check that weak ref does not deref to the original object. +assert page.evaluate("!globalThis.suspectWeakRef.deref()") +``` + +```csharp +// 1. In your page, save a WeakRef for the "suspect". +await Page.EvaluateAsync("globalThis.suspectWeakRef = new WeakRef(suspect)"); +// 2. Request garbage collection. +await Page.RequestGCAsync(); +// 3. Check that weak ref does not deref to the original object. +Assert.True(await Page.EvaluateAsync("!globalThis.suspectWeakRef.deref()")); +``` ### option: Page.goForward.waitUntil = %%-navigation-wait-until-%% * since: v1.8 @@ -2382,7 +2430,7 @@ Headless mode doesn't support navigation to a PDF document. See the - `url` <[string]> URL to navigate page to. The url should include scheme, e.g. `https://`. -When a [`option: baseURL`] via the context options was provided and the passed URL is a path, +When a [`option: Browser.newContext.baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. ### option: Page.goto.waitUntil = %%-navigation-wait-until-%% @@ -2437,7 +2485,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Page.hover.timeout = %%-input-timeout-js-%% * since: v1.8 -### option: Page.hover.trial = %%-input-trial-%% +### option: Page.hover.trial = %%-input-trial-with-modifiers-%% * since: v1.11 ### option: Page.hover.noWaitAfter = %%-input-no-wait-after-removed-%% @@ -2589,7 +2637,7 @@ Returns whether the element is [enabled](../actionability.md#enabled). * discouraged: Use locator-based [`method: Locator.isHidden`] instead. Read more about [locators](../locators.md). - returns: <[boolean]> -Returns whether the element is hidden, the opposite of [visible](../actionability.md#visible). [`option: selector`] that does not match any elements is considered hidden. +Returns whether the element is hidden, the opposite of [visible](../actionability.md#visible). [`param: selector`] that does not match any elements is considered hidden. ### param: Page.isHidden.selector = %%-input-selector-%% * since: v1.8 @@ -2608,7 +2656,7 @@ Returns whether the element is hidden, the opposite of [visible](../actionabilit * discouraged: Use locator-based [`method: Locator.isVisible`] instead. Read more about [locators](../locators.md). - returns: <[boolean]> -Returns whether the element is [visible](../actionability.md#visible). [`option: selector`] that does not match any elements is considered not visible. +Returns whether the element is [visible](../actionability.md#visible). [`param: selector`] that does not match any elements is considered not visible. ### param: Page.isVisible.selector = %%-input-selector-%% * since: v1.8 @@ -2714,8 +2762,7 @@ User can inspect selectors or perform manual steps while paused. Resume will con the place it was paused. :::note -This method requires Playwright to be started in a headed mode, with a falsy [`option: headless`] value in -the [`method: BrowserType.launch`]. +This method requires Playwright to be started in a headed mode, with a falsy [`option: BrowserType.launch.headless`] option. ::: ## async method: Page.pdf @@ -3092,11 +3139,9 @@ Things to keep in mind: :::warning Running the handler will alter your page state mid-test. For example it will change the currently focused element and move the mouse. Make sure that actions that run after the handler are self-contained and do not rely on the focus and mouse state being unchanged. -
-
+ For example, consider a test that calls [`method: Locator.focus`] followed by [`method: Keyboard.press`]. If your handler clicks a button between these two actions, the focused element most likely will be wrong, and key press will happen on the unexpected element. Use [`method: Locator.press`] instead to avoid this problem. -
-
+ Another example is a series of mouse actions, where [`method: Mouse.move`] is followed by [`method: Mouse.down`]. Again, when the handler runs between these two actions, the mouse position will be wrong during the mouse down. Prefer self-contained actions like [`method: Locator.click`] that do not rely on the state being unchanged by a handler. ::: @@ -3117,12 +3162,12 @@ await page.getByRole('button', { name: 'Start here' }).click(); ```java // Setup the handler. -page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () => { +page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () -> { page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click(); }); // Write the test as usual. -page.goto("https://example.com"); +page.navigate("https://example.com"); page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); ``` @@ -3174,12 +3219,12 @@ await page.getByRole('button', { name: 'Start here' }).click(); ```java // Setup the handler. -page.addLocatorHandler(page.getByText("Confirm your security details")), () => { +page.addLocatorHandler(page.getByText("Confirm your security details"), () -> { page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click(); }); // Write the test as usual. -page.goto("https://example.com"); +page.navigate("https://example.com"); page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); ``` @@ -3231,12 +3276,12 @@ await page.getByRole('button', { name: 'Start here' }).click(); ```java // Setup the handler. -page.addLocatorHandler(page.locator("body")), () => { +page.addLocatorHandler(page.locator("body"), () -> { page.evaluate("window.removeObstructionsForTestIfNeeded()"); -}, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true)); +}, new Page.AddLocatorHandlerOptions().setNoWaitAfter(true)); // Write the test as usual. -page.goto("https://example.com"); +page.navigate("https://example.com"); page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); ``` @@ -3282,7 +3327,7 @@ await page.addLocatorHandler(page.getByLabel('Close'), async locator => { ``` ```java -page.addLocatorHandler(page.getByLabel("Close"), locator => { +page.addLocatorHandler(page.getByLabel("Close"), locator -> { locator.click(); }, new Page.AddLocatorHandlerOptions().setTimes(1)); ``` @@ -3564,7 +3609,7 @@ Enabling routing disables http cache. - `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]> A glob pattern, regex pattern or predicate receiving [URL] to match while routing. -When a [`option: baseURL`] via the context options was provided and the passed URL is a path, +When a [`option: Browser.newContext.baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. ### param: Page.route.handler @@ -3632,6 +3677,88 @@ When set to `minimal`, only record information necessary for routing from HAR. T Optional setting to control resource content management. If `attach` is specified, resources are persisted as separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file. + +## async method: Page.routeWebSocket +* since: v1.48 + +This method allows to modify websocket connections that are made by the page. + +Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this method before navigating the page. + +**Usage** + +Below is an example of a simple mock that responds to a single message. See [WebSocketRoute] for more details and examples. + +```js +await page.routeWebSocket('/ws', ws => { + ws.onMessage(message => { + if (message === 'request') + ws.send('response'); + }); +}); +``` + +```java +page.routeWebSocket("/ws", ws -> { + ws.onMessage(message -> { + if ("request".equals(message)) + ws.send("response"); + }); +}); +``` + +```python async +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + ws.send("response") + +def handler(ws: WebSocketRoute): + ws.on_message(lambda message: message_handler(ws, message)) + +await page.route_web_socket("/ws", handler) +``` + +```python sync +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + ws.send("response") + +def handler(ws: WebSocketRoute): + ws.on_message(lambda message: message_handler(ws, message)) + +page.route_web_socket("/ws", handler) +``` + +```csharp +await page.RouteWebSocketAsync("/ws", ws => { + ws.OnMessage(message => { + if (message == "request") + ws.Send("response"); + }); +}); +``` + +### param: Page.routeWebSocket.url +* since: v1.48 +- `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]> + +Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the [`option: Browser.newContext.baseURL`] context option. + +### param: Page.routeWebSocket.handler +* since: v1.48 +* langs: js, python +- `handler` <[function]\([WebSocketRoute]\): [Promise|any]> + +Handler function to route the WebSocket. + +### param: Page.routeWebSocket.handler +* since: v1.48 +* langs: csharp, java +- `handler` <[function]\([WebSocketRoute]\)> + +Handler function to route the WebSocket. + + ## async method: Page.screenshot * since: v1.8 - returns: <[Buffer]> @@ -3956,12 +4083,16 @@ await page.GotoAsync("https://www.microsoft.com"); ### param: Page.setViewportSize.width * since: v1.10 * langs: csharp, java -- `width` <[int]> page width in pixels. +- `width` <[int]> + +Page width in pixels. ### param: Page.setViewportSize.height * since: v1.10 * langs: csharp, java -- `height` <[int]> page height in pixels. +- `height` <[int]> + +Page height in pixels. ## async method: Page.tap * since: v1.8 @@ -3979,7 +4110,7 @@ When all steps combined have not finished during the specified [`option: timeout [TimeoutError]. Passing zero timeout disables this. :::note -[`method: Page.tap`] the method will throw if [`option: hasTouch`] option of the browser context is false. +[`method: Page.tap`] the method will throw if [`option: Browser.newContext.hasTouch`] option of the browser context is false. ::: ### param: Page.tap.selector = %%-input-selector-%% @@ -4006,7 +4137,7 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Page.tap.timeout = %%-input-timeout-js-%% * since: v1.8 -### option: Page.tap.trial = %%-input-trial-%% +### option: Page.tap.trial = %%-input-trial-with-modifiers-%% * since: v1.11 ## async method: Page.textContent @@ -4766,7 +4897,7 @@ await page.RunAndWaitForRequestAsync(async () => - `urlOrPredicate` <[string]|[RegExp]|[function]\([Request]\):[boolean]> Request URL string, regex or predicate receiving [Request] object. -When a [`option: baseURL`] via the context options was provided and the passed URL is a path, +When a [`option: Browser.newContext.baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. ### param: Page.waitForRequest.urlOrPredicate @@ -4910,7 +5041,7 @@ await page.RunAndWaitForResponseAsync(async () => - `urlOrPredicate` <[string]|[RegExp]|[function]\([Response]\):[boolean]> Request URL string, regex or predicate receiving [Response] object. -When a [`option: baseURL`] via the context options was provided and the passed URL is a path, +When a [`option: Browser.newContext.baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. ### param: Page.waitForResponse.urlOrPredicate @@ -4919,7 +5050,7 @@ it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/We - `urlOrPredicate` <[string]|[RegExp]|[function]\([Response]\):[boolean]|[Promise]<[boolean]>> Request URL string, regex or predicate receiving [Response] object. -When a [`option: baseURL`] via the context options was provided and the passed URL is a path, +When a [`option: Browser.newContext.baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. ### option: Page.waitForResponse.timeout diff --git a/docs/src/api/class-pageassertions.md b/docs/src/api/class-pageassertions.md index 5e56907656..8eefd41f02 100644 --- a/docs/src/api/class-pageassertions.md +++ b/docs/src/api/class-pageassertions.md @@ -14,14 +14,14 @@ test('navigates to login', async ({ page }) => { ``` ```java -... +// ... import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; public class TestPage { - ... + // ... @Test void navigatesToLoginPage() { - ... + // ... page.getByText("Sign in").click(); assertThat(page).hasURL(Pattern.compile(".*/login")); } diff --git a/docs/src/api/class-playwrightassertions.md b/docs/src/api/class-playwrightassertions.md index 7a960e34e1..aa4e0a42a4 100644 --- a/docs/src/api/class-playwrightassertions.md +++ b/docs/src/api/class-playwrightassertions.md @@ -35,14 +35,13 @@ def test_status_becomes_submitted(page: Page) -> None: ``` ```java -... import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; public class TestExample { - ... + // ... @Test void statusBecomesSubmitted() { - ... + // ... page.locator("#submit-button").click(); assertThat(page.locator(".status")).hasText("Submitted"); } diff --git a/docs/src/api/class-touchscreen.md b/docs/src/api/class-touchscreen.md index f37a12102c..bd6e1ed9f0 100644 --- a/docs/src/api/class-touchscreen.md +++ b/docs/src/api/class-touchscreen.md @@ -10,7 +10,7 @@ touchscreen can only be used in browser contexts that have been initialized with Dispatches a `touchstart` and `touchend` event with a single touch at the position ([`param: x`],[`param: y`]). :::note -[`method: Page.tap`] the method will throw if [`option: hasTouch`] option of the browser context is false. +[`method: Page.tap`] the method will throw if [`option: Browser.newContext.hasTouch`] option of the browser context is false. ::: ### param: Touchscreen.tap.x diff --git a/docs/src/api/class-tracing.md b/docs/src/api/class-tracing.md index 6e7541e4cb..065896925f 100644 --- a/docs/src/api/class-tracing.md +++ b/docs/src/api/class-tracing.md @@ -121,7 +121,7 @@ await context.Tracing.StopAsync(new() - `name` <[string]> If specified, intermediate trace files are going to be saved into the files with the -given name prefix inside the [`option: tracesDir`] folder specified in [`method: BrowserType.launch`]. +given name prefix inside the [`option: BrowserType.launch.tracesDir`] directory specified in [`method: BrowserType.launch`]. To specify the final trace zip file name, you need to pass `path` option to [`method: Tracing.stop`] instead. @@ -277,7 +277,7 @@ Trace name to be shown in the Trace Viewer. - `name` <[string]> If specified, intermediate trace files are going to be saved into the files with the -given name prefix inside the [`option: tracesDir`] folder specified in [`method: BrowserType.launch`]. +given name prefix inside the [`option: BrowserType.launch.tracesDir`] directory specified in [`method: BrowserType.launch`]. To specify the final trace zip file name, you need to pass `path` option to [`method: Tracing.stopChunk`] instead. diff --git a/docs/src/api/class-websocketroute.md b/docs/src/api/class-websocketroute.md new file mode 100644 index 0000000000..e5347b07b1 --- /dev/null +++ b/docs/src/api/class-websocketroute.md @@ -0,0 +1,315 @@ +# class: WebSocketRoute +* since: v1.48 + +Whenever a [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) route is set up with [`method: Page.routeWebSocket`] or [`method: BrowserContext.routeWebSocket`], the `WebSocketRoute` object allows to handle the WebSocket, like an actual server would do. + +**Mocking** + +By default, the routed WebSocket will not connect to the server. This way, you can mock entire communcation over the WebSocket. Here is an example that responds to a `"request"` with a `"response"`. + +```js +await page.routeWebSocket('/ws', ws => { + ws.onMessage(message => { + if (message === 'request') + ws.send('response'); + }); +}); +``` + +```java +page.routeWebSocket("/ws", ws -> { + ws.onMessage(message -> { + if ("request".equals(message)) + ws.send("response"); + }); +}); +``` + +```python async +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + ws.send("response") + +await page.route_web_socket("/ws", lambda ws: ws.on_message( + lambda message: message_handler(ws, message) +)) +``` + +```python sync +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + ws.send("response") + +page.route_web_socket("/ws", lambda ws: ws.on_message( + lambda message: message_handler(ws, message) +)) +``` + +```csharp +await page.RouteWebSocketAsync("/ws", ws => { + ws.OnMessage(message => { + if (message == "request") + ws.Send("response"); + }); +}); +``` + +Since we do not call [`method: WebSocketRoute.connectToServer`] inside the WebSocket route handler, Playwright assumes that WebSocket will be mocked, and opens the WebSocket inside the page automatically. + +**Intercepting** + +Alternatively, you may want to connect to the actual server, but intercept messages in-between and modify or block them. Calling [`method: WebSocketRoute.connectToServer`] returns a server-side `WebSocketRoute` instance that you can send messages to, or handle incoming messages. + +Below is an example that modifies some messages sent by the page to the server. Messages sent from the server to the page are left intact, relying on the default forwarding. + +```js +await page.routeWebSocket('/ws', ws => { + const server = ws.connectToServer(); + ws.onMessage(message => { + if (message === 'request') + server.send('request2'); + else + server.send(message); + }); +}); +``` + +```java +page.routeWebSocket("/ws", ws -> { + WebSocketRoute server = ws.connectToServer(); + ws.onMessage(message -> { + if ("request".equals(message)) + server.send("request2"); + else + server.send(message); + }); +}); +``` + +```python async +def message_handler(server: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + server.send("request2") + else: + server.send(message) + +def handler(ws: WebSocketRoute): + server = ws.connect_to_server() + ws.on_message(lambda message: message_handler(server, message)) + +await page.route_web_socket("/ws", handler) +``` + +```python sync +def message_handler(server: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + server.send("request2") + else: + server.send(message) + +def handler(ws: WebSocketRoute): + server = ws.connect_to_server() + ws.on_message(lambda message: message_handler(server, message)) + +page.route_web_socket("/ws", handler) +``` + +```csharp +await page.RouteWebSocketAsync("/ws", ws => { + var server = ws.ConnectToServer(); + ws.OnMessage(message => { + if (message == "request") + server.Send("request2"); + else + server.Send(message); + }); +}); +``` + +After connecting to the server, all **messages are forwarded** between the page and the server by default. + +However, if you call [`method: WebSocketRoute.onMessage`] on the original route, messages from the page to the server **will not be forwarded** anymore, but should instead be handled by the [`param: WebSocketRoute.onMessage.handler`]. + +Similarly, calling [`method: WebSocketRoute.onMessage`] on the server-side WebSocket will **stop forwarding messages** from the server to the page, and [`param: WebSocketRoute.onMessage.handler`] should take care of them. + + +The following example blocks some messages in both directions. Since it calls [`method: WebSocketRoute.onMessage`] in both directions, there is no automatic forwarding at all. + +```js +await page.routeWebSocket('/ws', ws => { + const server = ws.connectToServer(); + ws.onMessage(message => { + if (message !== 'blocked-from-the-page') + server.send(message); + }); + server.onMessage(message => { + if (message !== 'blocked-from-the-server') + ws.send(message); + }); +}); +``` + +```java +page.routeWebSocket("/ws", ws -> { + WebSocketRoute server = ws.connectToServer(); + ws.onMessage(message -> { + if (!"blocked-from-the-page".equals(message)) + server.send(message); + }); + server.onMessage(message -> { + if (!"blocked-from-the-server".equals(message)) + ws.send(message); + }); +}); +``` + +```python async +def ws_message_handler(server: WebSocketRoute, message: Union[str, bytes]): + if message != "blocked-from-the-page": + server.send(message) + +def server_message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message != "blocked-from-the-server": + ws.send(message) + +def handler(ws: WebSocketRoute): + server = ws.connect_to_server() + ws.on_message(lambda message: ws_message_handler(server, message)) + server.on_message(lambda message: server_message_handler(ws, message)) + +await page.route_web_socket("/ws", handler) +``` + +```python sync +def ws_message_handler(server: WebSocketRoute, message: Union[str, bytes]): + if message != "blocked-from-the-page": + server.send(message) + +def server_message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message != "blocked-from-the-server": + ws.send(message) + +def handler(ws: WebSocketRoute): + server = ws.connect_to_server() + ws.on_message(lambda message: ws_message_handler(server, message)) + server.on_message(lambda message: server_message_handler(ws, message)) + +page.route_web_socket("/ws", handler) +``` + +```csharp +await page.RouteWebSocketAsync("/ws", ws => { + var server = ws.ConnectToServer(); + ws.OnMessage(message => { + if (message != "blocked-from-the-page") + server.Send(message); + }); + server.OnMessage(message => { + if (message != "blocked-from-the-server") + ws.Send(message); + }); +}); +``` + + + +## async method: WebSocketRoute.close +* since: v1.48 + +Closes one side of the WebSocket connection. + +### option: WebSocketRoute.close.code +* since: v1.48 +- `code` <[int]> + +Optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code). + +### option: WebSocketRoute.close.reason +* since: v1.48 +- `reason` <[string]> + +Optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason). + + + +## method: WebSocketRoute.connectToServer +* since: v1.48 +- returns: <[WebSocketRoute]> + +By default, routed WebSocket does not connect to the server, so you can mock entire WebSocket communication. This method connects to the actual WebSocket server, and returns the server-side [WebSocketRoute] instance, giving the ability to send and receive messages from the server. + +Once connected to the server: +* Messages received from the server will be **automatically forwarded** to the WebSocket in the page, unless [`method: WebSocketRoute.onMessage`] is called on the server-side `WebSocketRoute`. +* Messages sent by the [`WebSocket.send()`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send) call in the page will be **automatically forwarded** to the server, unless [`method: WebSocketRoute.onMessage`] is called on the original `WebSocketRoute`. + +See examples at the top for more details. + + + +## method: WebSocketRoute.onClose +* since: v1.48 + +Allows to handle [`WebSocket.close`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close). + +By default, closing one side of the connection, either in the page or on the server, will close the other side. However, when [`method: WebSocketRoute.onClose`] handler is set up, the default forwarding of closure is disabled, and handler should take care of it. + +### param: WebSocketRoute.onClose.handler +* since: v1.48 +* langs: js, python +- `handler` <[function]\([number]|[undefined], [string]|[undefined]\): [Promise|any]> + +Function that will handle WebSocket closure. Received an optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason). + +### param: WebSocketRoute.onClose.handler +* since: v1.48 +* langs: java, csharp +- `handler` <[function]\([null]|[number], [null]|[string]\)> + +Function that will handle WebSocket closure. Received an optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason). + + +## async method: WebSocketRoute.onMessage +* since: v1.48 + +This method allows to handle messages that are sent by the WebSocket, either from the page or from the server. + +When called on the original WebSocket route, this method handles messages sent from the page. You can handle this messages by responding to them with [`method: WebSocketRoute.send`], forwarding them to the server-side connection returned by [`method: WebSocketRoute.connectToServer`] or do something else. + +Once this method is called, messages are not automatically forwarded to the server or to the page - you should do that manually by calling [`method: WebSocketRoute.send`]. See examples at the top for more details. + +Calling this method again will override the handler with a new one. + +### param: WebSocketRoute.onMessage.handler +* since: v1.48 +* langs: js, python +- `handler` <[function]\([string]\): [Promise|any]> + +Function that will handle messages. + +### param: WebSocketRoute.onMessage.handler +* since: v1.48 +* langs: csharp, java +- `handler` <[function]\([WebSocketFrame]\)> + +Function that will handle messages. + + + +## method: WebSocketRoute.send +* since: v1.48 + +Sends a message to the WebSocket. When called on the original WebSocket, sends the message to the page. When called on the result of [`method: WebSocketRoute.connectToServer`], sends the message to the server. See examples at the top for more details. + +### param: WebSocketRoute.send.message +* since: v1.48 +- `message` <[string]|[Buffer]> + +Message to send. + + + +## method: WebSocketRoute.url +* since: v1.48 +- returns: <[string]> + +URL of the WebSocket created in the page. diff --git a/docs/src/api/params.md b/docs/src/api/params.md index de930ee97e..462b225e6e 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -136,6 +136,11 @@ defaults to 1. See [UIEvent.detail]. When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults to `false`. Useful to wait until the element is ready for the action without performing it. +## input-trial-with-modifiers +- `trial` <[boolean]> + +When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys are pressed. + ## input-source-position - `sourcePosition` <[Object]> - `x` <[float]> @@ -694,7 +699,7 @@ Logger sink for Playwright logging. - `content` ?<[HarContentPolicy]<"omit"|"embed"|"attach">> Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persisted as separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file as per HAR specification. Defaults to `attach` for `.zip` output files and to `embed` for all other file extensions. - `path` <[path]> Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `content: 'attach'` is used by default. - `mode` ?<[HarMode]<"full"|"minimal">> When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`. - - `urlFilter` ?<[string]|[RegExp]> A glob or regex pattern to filter requests that are stored in the HAR. When a [`option: baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none. + - `urlFilter` ?<[string]|[RegExp]> A glob or regex pattern to filter requests that are stored in the HAR. When a [`option: Browser.newContext.baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none. Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into `recordHar.path` file. If not specified, the HAR is not recorded. Make sure to await [`method: BrowserContext.close`] for the HAR to be @@ -760,8 +765,6 @@ not recorded. Make sure to call [`method: BrowserContext.close`] for videos to b * langs: csharp, java, python - alias-python: record_video_size - `recordVideoSize` <[Object]> - If `viewport` is not configured explicitly the video size defaults to 800x450. Actual picture of each page will be - scaled down if necessary to fit the specified size. - `width` <[int]> Video frame width. - `height` <[int]> Video frame height. @@ -1041,7 +1044,7 @@ Close the browser process on SIGHUP. Defaults to `true`. Whether to run browser in headless mode. More details for [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the -[`option: devtools`] option is `true`. +[`option: BrowserType.launch.devtools`] option is `true`. ## js-python-browser-option-firefoxuserprefs * langs: js, python @@ -1485,19 +1488,19 @@ page.get_by_text(re.compile("^hello$", re.IGNORECASE)) ```java // Matches -page.getByText("world") +page.getByText("world"); // Matches first
-page.getByText("Hello world") +page.getByText("Hello world"); // Matches second
-page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) +page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); // Matches both
s -page.getByText(Pattern.compile("Hello")) +page.getByText(Pattern.compile("Hello")); // Matches second
-page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) +page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); ``` ```csharp diff --git a/docs/src/ci.md b/docs/src/ci.md index 745ecc9d50..6f6fc9a80e 100644 --- a/docs/src/ci.md +++ b/docs/src/ci.md @@ -85,7 +85,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: lts/* - name: Install dependencies run: npm ci - name: Install Playwright Browsers @@ -208,13 +208,13 @@ jobs: name: 'Playwright Tests' runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright:v%%VERSION%%-jammy + image: mcr.microsoft.com/playwright:v%%VERSION%%-noble options: --user 1001 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: lts/* - name: Install dependencies run: npm ci - name: Run your tests @@ -233,7 +233,7 @@ jobs: name: 'Playwright Tests' runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright/python:v%%VERSION%%-jammy + image: mcr.microsoft.com/playwright/python:v%%VERSION%%-noble options: --user 1001 steps: - uses: actions/checkout@v4 @@ -262,7 +262,7 @@ jobs: name: 'Playwright Tests' runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright/java:v%%VERSION%%-jammy + image: mcr.microsoft.com/playwright/java:v%%VERSION%%-noble options: --user 1001 steps: - uses: actions/checkout@v4 @@ -288,7 +288,7 @@ jobs: name: 'Playwright Tests' runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright/dotnet:v%%VERSION%%-jammy + image: mcr.microsoft.com/playwright/dotnet:v%%VERSION%%-noble options: --user 1001 steps: - uses: actions/checkout@v4 @@ -319,7 +319,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: lts/* - name: Install dependencies run: npm ci - name: Install Playwright @@ -415,7 +415,7 @@ Large test suites can take very long to execute. By executing a preliminary test This will give you a faster feedback loop and slightly lower CI consumption while working on Pull Requests. To detect test files affected by your changeset, `--only-changed` analyses your suites' dependency graph. This is a heuristic and might miss tests, so it's important that you always run the full test suite after the preliminary test run. -```yml js title=".github/workflows/playwright.yml" {20-23} +```yml js title=".github/workflows/playwright.yml" name: Playwright Tests on: push: @@ -434,7 +434,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: lts/* - name: Install dependencies run: npm ci - name: Install Playwright Browsers @@ -766,28 +766,28 @@ Running Playwright on CircleCI is very similar to running on GitHub Actions. In ```yml js executors: - pw-jammy-development: + pw-noble-development: docker: - image: mcr.microsoft.com/playwright:v%%VERSION%%-noble ``` ```yml python executors: - pw-jammy-development: + pw-noble-development: docker: - image: mcr.microsoft.com/playwright/python:v%%VERSION%%-noble ``` ```yml java executors: - pw-jammy-development: + pw-noble-development: docker: - image: mcr.microsoft.com/playwright/java:v%%VERSION%%-noble ``` ```yml csharp executors: - pw-jammy-development: + pw-noble-development: docker: - image: mcr.microsoft.com/playwright/dotnet:v%%VERSION%%-noble ``` @@ -801,7 +801,7 @@ Sharding in CircleCI is indexed with 0 which means that you will need to overrid ```yml playwright-job-name: - executor: pw-jammy-development + executor: pw-noble-development parallelism: 4 steps: - run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npx playwright test -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL} @@ -997,7 +997,7 @@ type: docker steps: - name: test - image: mcr.microsoft.com/playwright:v%%VERSION%%-jammy + image: mcr.microsoft.com/playwright:v%%VERSION%%-noble commands: - npx playwright test ``` diff --git a/docs/src/debug.md b/docs/src/debug.md index aff5077d3c..b812d0f670 100644 --- a/docs/src/debug.md +++ b/docs/src/debug.md @@ -451,7 +451,7 @@ will reset pre-configured user agent and device emulation. Playwright runs browsers in headless mode by default. To change this behavior, use `headless: false` as a launch option. -You can also use the [`option: slowMo`] option +You can also use the [`option: BrowserType.launch.slowMo`] option to slow down execution (by N milliseconds per operation) and follow along while debugging. ```js diff --git a/docs/src/dialogs.md b/docs/src/dialogs.md index 20037245ee..0aaa69aade 100644 --- a/docs/src/dialogs.md +++ b/docs/src/dialogs.md @@ -80,7 +80,7 @@ If there is no listener for [`event: Page.dialog`], all dialogs are automaticall ## beforeunload dialog -When [`method: Page.close`] is invoked with the truthy [`option: runBeforeUnload`] value, the page runs its unload handlers. This is the only case when [`method: Page.close`] does not wait for the page to actually close, because it might be that the page stays open in the end of the operation. +When [`method: Page.close`] is invoked with the truthy [`option: Page.close.runBeforeUnload`] value, the page runs its unload handlers. This is the only case when [`method: Page.close`] does not wait for the page to actually close, because it might be that the page stays open in the end of the operation. You can register a dialog handler to handle the `beforeunload` dialog yourself: diff --git a/docs/src/downloads.md b/docs/src/downloads.md index 9bce4f9d98..824b714d11 100644 --- a/docs/src/downloads.md +++ b/docs/src/downloads.md @@ -7,7 +7,7 @@ title: "Downloads" For every attachment downloaded by the page, [`event: Page.download`] event is emitted. All these attachments are downloaded into a temporary folder. You can obtain the download url, file name and payload stream using the [Download] object from the event. -You can specify where to persist downloaded files using the [`option: downloadsPath`] option in [`method: BrowserType.launch`]. +You can specify where to persist downloaded files using the [`option: BrowserType.launch.downloadsPath`] option in [`method: BrowserType.launch`]. :::note Downloaded files are deleted when the browser context that produced them is closed. diff --git a/docs/src/emulation.md b/docs/src/emulation.md index d1f7bc3400..12b44a49c8 100644 --- a/docs/src/emulation.md +++ b/docs/src/emulation.md @@ -188,7 +188,7 @@ page.setViewportSize(1600, 1200); // Emulate high-DPI BrowserContext context = browser.newContext(new Browser.NewContextOptions() .setViewportSize(2560, 1440) - .setDeviceScaleFactor(2); + .setDeviceScaleFactor(2)); ``` ```python async @@ -378,7 +378,7 @@ const context = await browser.newContext({ ```java BrowserContext context = browser.newContext(new Browser.NewContextOptions() - .setPermissions(Arrays.asList("notifications")); + .setPermissions(Arrays.asList("notifications"))); ``` ```python async diff --git a/docs/src/getting-started-vscode-js.md b/docs/src/getting-started-vscode-js.md index cf1c8f0844..d8a2ccfcf3 100644 --- a/docs/src/getting-started-vscode-js.md +++ b/docs/src/getting-started-vscode-js.md @@ -18,7 +18,7 @@ Get started by installing Playwright and generating a test to see it in action. ## Installation -Install the [VS Code extension from the marketplace](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) or from the extensions tab in VS Code. +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) diff --git a/docs/src/intro-java.md b/docs/src/intro-java.md index 424562ab83..4e1503f2a3 100644 --- a/docs/src/intro-java.md +++ b/docs/src/intro-java.md @@ -112,7 +112,7 @@ public class App { } ``` -By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `setHeadless(false)` flag while launching the browser. You can also use [`option: slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md). +By default, Playwright runs the browsers in headless mode. To see the browser UI, [`option: BrowserType.launch.headless`] option to `false`. You can also use [`option: BrowserType.launch.slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md). ```java playwright.firefox().launch(new BrowserType.LaunchOptions().setHeadless(false).setSlowMo(50)); diff --git a/docs/src/library-csharp.md b/docs/src/library-csharp.md index c659cbf1ee..db501645ea 100644 --- a/docs/src/library-csharp.md +++ b/docs/src/library-csharp.md @@ -48,7 +48,7 @@ Now run it. dotnet run ``` -By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `Headless = false` flag while launching the browser. You can also use [`option: slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md). +By default, Playwright runs the browsers in headless mode. To see the browser UI, set [`option: BrowserType.launch.headless`] option to `false`. You can also use [`option: BrowserType.launch.slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md). ```csharp await using var browser = await playwright.Firefox.LaunchAsync(new() diff --git a/docs/src/library-js.md b/docs/src/library-js.md index 9085290bba..46eadfced4 100644 --- a/docs/src/library-js.md +++ b/docs/src/library-js.md @@ -105,6 +105,7 @@ The key differences to note are as follows: | `import` from | `playwright` | `@playwright/test` | | Initialization | Explicitly need to:
  1. Pick a browser to use, e.g. `chromium`
  2. Launch browser with [`method: BrowserType.launch`]
  3. Create a context with [`method: Browser.newContext`], and pass any context options explicitly, e.g. `devices['iPhone 11']`
  4. Create a page with [`method: BrowserContext.newPage`]
| An isolated `page` and `context` are provided to each test out-of the box, along with other [built-in fixtures](./test-fixtures.md#built-in-fixtures). No explicit creation. If referenced by the test in its arguments, the Test Runner will create them for the test. (i.e. lazy-initialization) | | Assertions | No built-in Web-First Assertions | [Web-First assertions](./test-assertions.md) like:
  • [`method: PageAssertions.toHaveTitle`]
  • [`method: PageAssertions.toHaveScreenshot#1`]
which auto-wait and retry for the condition to be met.| +| Timeouts | Defaults to 30s for most operations. | Most operations don't time out, but every test has a timeout that makes it fail (30s by default). | | Cleanup | Explicitly need to:
  1. Close context with [`method: BrowserContext.close`]
  2. Close browser with [`method: Browser.close`]
| No explicit close of [built-in fixtures](./test-fixtures.md#built-in-fixtures); the Test Runner will take care of it. | Running | When using the Library, you run the code as a node script, possibly with some compilation first. | When using the Test Runner, you use the `npx playwright test` command. Along with your [config](./test-configuration.md), the Test Runner handles any compilation and choosing what to run and how to run it. | diff --git a/docs/src/library-python.md b/docs/src/library-python.md index 8391ea92ef..f9063ea13a 100644 --- a/docs/src/library-python.md +++ b/docs/src/library-python.md @@ -75,7 +75,7 @@ with sync_playwright() as p: browser.close() ``` -By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `headless=False` flag while launching the browser. You can also use [`option: slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md). +By default, Playwright runs the browsers in headless mode. To see the browser UI, set [`option: BrowserType.launch.headless`] option to `False`. You can also use [`option: BrowserType.launch.slowMo`] to slow down execution. Learn more in the debugging tools [section](./debug.md). ```py firefox.launch(headless=False, slow_mo=50) diff --git a/docs/src/locators.md b/docs/src/locators.md index 8ade71efb7..0aa918e53c 100644 --- a/docs/src/locators.md +++ b/docs/src/locators.md @@ -122,7 +122,7 @@ await locator.click(); ```java Locator locator = page.getByRole(AriaRole.BUTTON, - new Page.GetByRoleOptions().setName("Sign in")) + new Page.GetByRoleOptions().setName("Sign in")); locator.hover(); locator.click(); @@ -946,7 +946,7 @@ page.getByRole(AriaRole.LISTITEM) .setName("Product 2")))) .getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Add to cart")) - .click() + .click(); ``` ```python async @@ -987,7 +987,7 @@ assertThat(page .getByRole(AriaRole.LISTITEM) .filter(new Locator.FilterOptions() .setHas(page.GetByRole(AriaRole.HEADING, - new Page.GetByRoleOptions().setName("Product 2")))) + new Page.GetByRoleOptions().setName("Product 2"))))) .hasCount(1); ``` @@ -1033,7 +1033,7 @@ assertThat(page .filter(new Locator.FilterOptions() .setHas(page.GetByRole(AriaRole.LIST) .GetByRole(AriaRole.HEADING, - new Page.GetByRoleOptions().setName("Product 2")))) + new Page.GetByRoleOptions().setName("Product 2"))))) .hasCount(1); ``` @@ -1079,7 +1079,7 @@ await expect(page ```java assertThat(page .getByRole(AriaRole.LISTITEM) - .filter(new Locator.FilterOptions().setHasNot(page.getByText("Product 2"))) + .filter(new Locator.FilterOptions().setHasNot(page.getByText("Product 2")))) .hasCount(1); ``` @@ -1356,7 +1356,7 @@ expect(page.get_by_role("listitem")).to_have_count(3) ``` ```java -assertThat(page.getByRole(AriaRole.LISTITEM).hasCount(3); +assertThat(page.getByRole(AriaRole.LISTITEM)).hasCount(3); ``` ```csharp diff --git a/docs/src/mock.md b/docs/src/mock.md index 87ddf2ec96..5c87e91d5b 100644 --- a/docs/src/mock.md +++ b/docs/src/mock.md @@ -195,15 +195,15 @@ await Expect(page.GetByTextAsync("Loquat", new () { Exact = true })).ToBeVisible page.route("*/**/api/v1/fruits", route -> { Response response = route.fetch(); byte[] json = response.body(); - parsed = new Gson().fromJson(json, JsonObject.class) + JsonObject parsed = new Gson().fromJson(new String(json), JsonObject.class); parsed.add(new JsonObject().add("name", "Loquat").add("id", 100)); // Fulfill using the original response, while patching the response body // with the given JSON object. - route.fulfill(new Route.FulfillOptions().setResponse(response).setBody(json.toString())); + route.fulfill(new Route.FulfillOptions().setResponse(response).setBody(parsed.toString())); }); // Go to the page -page.goto("https://demo.playwright.dev/api-mocking"); +page.navigate("https://demo.playwright.dev/api-mocking"); // Assert that the Loquat fruit is visible assertThat(page.getByText("Loquat", new Page.GetByTextOptions().setExact(true))).isVisible(); @@ -294,7 +294,7 @@ page.routeFromHAR(Path.of("./hars/fruit.har"), new RouteFromHAROptions() ); // Go to the page -page.goto("https://demo.playwright.dev/api-mocking"); +page.navigate("https://demo.playwright.dev/api-mocking"); // Assert that the fruit is visible assertThat(page.getByText("Strawberry")).isVisible(); @@ -392,10 +392,11 @@ page.routeFromHAR(Path.of("./hars/fruit.har"), new RouteFromHAROptions() ); // Go to the page -page.goto("https://demo.playwright.dev/api-mocking"); +page.navigate("https://demo.playwright.dev/api-mocking"); // Assert that the Playwright fruit is visible -assertThat(page.getByText("Playwright", new Page.GetByTextOptions().setExact(true))).isVisible(); +assertThat(page.getByText("Playwright", new Page.GetByTextOptions() + .setExact(true))).isVisible(); ``` In the trace of our test we can see that the route was fulfilled from the HAR file and the API was not called. ![trace showing the HAR file being used](https://github.com/microsoft/playwright/assets/13063165/1bd7ab66-ea4f-43c2-a4e5-ca17d4837ff1) diff --git a/docs/src/network.md b/docs/src/network.md index 152231556e..33c012d9b5 100644 --- a/docs/src/network.md +++ b/docs/src/network.md @@ -146,8 +146,8 @@ const browser = await chromium.launch({ ```java Browser browser = chromium.launch(new BrowserType.LaunchOptions() .setProxy(new Proxy("http://myproxy.com:3128") - .setUsername('usr') - .setPassword('pwd'))); + .setUsername("usr") + .setPassword("pwd"))); ``` ```python async @@ -627,7 +627,7 @@ page.route("**/title.html", route -> { String body = response.text(); body = body.replace("", "<title>My prefix:"); Map<String, String> headers = response.headers(); - headers.put("content-type": "text/html"); + headers.put("content-type", "text/html"); route.fulfill(new Route.FulfillOptions() // Pass all fields from the response. .setResponse(response) @@ -700,6 +700,27 @@ await Page.RouteAsync("**/title.html", async route => }); ``` +## Glob URL patterns + +Playwright uses simplified glob patterns for URL matching in network interception methods like [`method: Page.route`] or [`method: Page.waitForResponse`]. These patterns support basic wildcards: + +1. Asterisks: + - A single `*` matches any characters except `/` + - A double `**` matches any characters including `/` +1. Question mark `?` matches any single character except `/` +1. Curly braces `{}` can be used to match a list of options separated by commas `,` + +Examples: +- `https://example.com/*.js` matches `https://example.com/file.js` but not `https://example.com/path/file.js` +- `**/*.js` matches both `https://example.com/file.js` and `https://example.com/path/file.js` +- `**/*.{png,jpg,jpeg}` matches all image requests + +Important notes: + +- The glob pattern must match the entire URL, not just a part of it. +- When using globs for URL matching, consider the full URL structure, including the protocol and path separators. +- For more complex matching requirements, consider using [RegExp] instead of glob patterns. + ## WebSockets Playwright supports [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) inspection out of the box. Every time a WebSocket is created, the [`event: Page.webSocket`] event is fired. This event contains the [WebSocket] instance for further web socket frames inspection: diff --git a/docs/src/pom.md b/docs/src/pom.md index 983598a2c2..5108d5718b 100644 --- a/docs/src/pom.md +++ b/docs/src/pom.md @@ -289,7 +289,7 @@ Page objects can then be used inside a test. ```java import models.SearchPage; import com.microsoft.playwright.*; -... +// ... // In the test Page page = browser.newPage(); diff --git a/docs/src/release-notes-csharp.md b/docs/src/release-notes-csharp.md index b6a249b0eb..fee7d732fe 100644 --- a/docs/src/release-notes-csharp.md +++ b/docs/src/release-notes-csharp.md @@ -4,6 +4,47 @@ title: "Release notes" toc_max_heading_level: 2 --- + +## Version 1.48 + +### WebSocket routing + +New methods [`method: Page.routeWebSocket`] and [`method: BrowserContext.routeWebSocket`] allow to intercept, modify and mock WebSocket connections initiated in the page. Below is a simple example that mocks WebSocket communication by responding to a `"request"` with a `"response"`. + +```csharp +await page.RouteWebSocketAsync("/ws", ws => { + ws.OnMessage(message => { + if (message == "request") + ws.Send("response"); + }); +}); +``` + +See [WebSocketRoute] for more details. + +### UI updates + +- New "copy" buttons for annotations and test location in the HTML report. +- Route method calls like [`method: Route.fulfill`] are not shown in the report and trace viewer anymore. You can see which network requests were routed in the network tab instead. +- New "Copy as cURL" and "Copy as fetch" buttons for requests in the network tab. + +### Miscellaneous + +- New method [`method: Page.requestGC`] may help detect memory leaks. +- Requests made by [APIRequestContext] now record detailed timing and security information in the HAR. + +### Browser Versions + +- Chromium 130.0.6723.19 +- Mozilla Firefox 130.0 +- WebKit 18.0 + +This version was also tested against the following stable channels: + +- Google Chrome 129 +- Microsoft Edge 129 + + ## Version 1.47 ### Network Tab improvements @@ -21,8 +62,8 @@ The Network tab in the trace viewer has several nice improvements: - The `mcr.microsoft.com/playwright/dotnet:v1.47.0` now serves a Playwright image based on Ubuntu 24.04 Noble. To use the 22.04 jammy-based image, please use `mcr.microsoft.com/playwright/dotnet:v1.47.0-jammy` instead. - The `:latest`/`:focal`/`:jammy` tag for Playwright Docker images is no longer being published. Pin to a specific version for better stability and reproducibility. -- TLS client certificates can now be passed from memory by passing [`option: cert`] and [`option: key`] as byte arrays instead of file paths. -- [`option: noWaitAfter`] in [`method: Locator.selectOption`] was deprecated. +- TLS client certificates can now be passed from memory by passing [`option: Browser.newContext.clientCertificates.cert`] and [`option: Browser.newContext.clientCertificates.key`] as byte arrays instead of file paths. +- [`option: Locator.selectOption.noWaitAfter`] in [`method: Locator.selectOption`] was deprecated. - We've seen reports of WebGL in Webkit misbehaving on GitHub Actions `macos-13`. We recommend upgrading GitHub Actions to `macos-14`. ### Browser Versions @@ -284,7 +325,7 @@ await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Light and easy" }) ### New APIs -- [`method: Page.pdf`] accepts two new options [`option: tagged`] and [`option: outline`]. +- [`method: Page.pdf`] accepts two new options [`option: Page.pdf.tagged`] and [`option: Page.pdf.outline`]. ### Announcements @@ -307,7 +348,7 @@ This version was also tested against the following stable channels: - New method [`method: Page.unrouteAll`] removes all routes registered by [`method: Page.route`] and [`method: Page.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them. - New method [`method: BrowserContext.unrouteAll`] removes all routes registered by [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them. -- New option [`option: style`] in [`method: Page.screenshot`] and [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot. +- New options [`option: Page.screenshot.style`] in [`method: Page.screenshot`] and [`option: Locator.screenshot.style`] in [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot. ### Browser Versions @@ -345,8 +386,8 @@ await Expect(Page.GetByPlaceholder("Search docs")).ToHaveValueAsync("locator"); ### New APIs -- Option [`option: reason`] in [`method: Page.close`], [`method: BrowserContext.close`] and [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure. -- Option [`option: firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`]. +- Options [`option: Page.close.reason`] in [`method: Page.close`], [`option: BrowserContext.close.reason`] in [`method: BrowserContext.close`] and [`option: Browser.close.reason`] in [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure. +- Option [`option: BrowserType.launchPersistentContext.firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`]. ### Other Changes @@ -517,7 +558,7 @@ This version was also tested against the following stable channels: await Page.GetByRole(AriaRole.Button, new() { Name = "Dismiss" }).ClickAsync(); await newEmail.ClickAsync(); ``` -* Use new options [`option: hasNot`] and [`option: hasNotText`] in [`method: Locator.filter`] +* Use new options [`option: Locator.filter.hasNot`] and [`option: Locator.filter.hasNotText`] in [`method: Locator.filter`] to find elements that **do not match** certain conditions. ```csharp @@ -534,10 +575,10 @@ This version was also tested against the following stable channels: ### New APIs - [`method: Locator.or`] -- New option [`option: hasNot`] in [`method: Locator.filter`] -- New option [`option: hasNotText`] in [`method: Locator.filter`] +- New option [`option: Locator.filter.hasNot`] in [`method: Locator.filter`] +- New option [`option: Locator.filter.hasNotText`] in [`method: Locator.filter`] - [`method: LocatorAssertions.toBeAttached`] -- New option [`option: timeout`] in [`method: Route.fetch`] +- New option [`option: Route.fetch.timeout`] in [`method: Route.fetch`] ### ⚠️ Breaking change @@ -560,9 +601,9 @@ This version was also tested against the following stable channels: ### New APIs -- New options [`option: updateMode`] and [`option: updateContent`] in [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`]. +- New options [`option: Page.routeFromHAR.updateMode`] and [`option: Page.routeFromHAR.updateContent`] in [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`]. - Chaining existing locator objects, see [locator docs](./locators.md#matching-inside-a-locator) for details. -- New option [`option: name`] in method [`method: Tracing.startChunk`]. +- New option [`option: Tracing.startChunk.name`] in method [`method: Tracing.startChunk`]. ### Browser Versions diff --git a/docs/src/release-notes-java.md b/docs/src/release-notes-java.md index 89a49f7451..908a768357 100644 --- a/docs/src/release-notes-java.md +++ b/docs/src/release-notes-java.md @@ -4,6 +4,46 @@ title: "Release notes" toc_max_heading_level: 2 --- +## Version 1.48 + +### WebSocket routing + +New methods [`method: Page.routeWebSocket`] and [`method: BrowserContext.routeWebSocket`] allow to intercept, modify and mock WebSocket connections initiated in the page. Below is a simple example that mocks WebSocket communication by responding to a `"request"` with a `"response"`. + +```java +page.routeWebSocket("/ws", ws -> { + ws.onMessage(message -> { + if ("request".equals(message)) + ws.send("response"); + }); +}); +``` + +See [WebSocketRoute] for more details. + +### UI updates + +- New "copy" buttons for annotations and test location in the HTML report. +- Route method calls like [`method: Route.fulfill`] are not shown in the report and trace viewer anymore. You can see which network requests were routed in the network tab instead. +- New "Copy as cURL" and "Copy as fetch" buttons for requests in the network tab. + +### Miscellaneous + +- New method [`method: Page.requestGC`] may help detect memory leaks. +- Requests made by [APIRequestContext] now record detailed timing and security information in the HAR. + +### Browser Versions + +- Chromium 130.0.6723.19 +- Mozilla Firefox 130.0 +- WebKit 18.0 + +This version was also tested against the following stable channels: + +- Google Chrome 129 +- Microsoft Edge 129 + + ## Version 1.47 ### Network Tab improvements @@ -21,8 +61,8 @@ The Network tab in the trace viewer has several nice improvements: - The `mcr.microsoft.com/playwright/java:v1.47.0` now serves a Playwright image based on Ubuntu 24.04 Noble. To use the 22.02 jammy-based image, please use `mcr.microsoft.com/playwright/java:v1.47.0-jammy` instead. - The `:latest`/`:focal`/`:jammy` tag for Playwright Docker images is no longer being published. Pin to a specific version for better stability and reproducibility. -- TLS client certificates can now be passed from memory by passing [`option: cert`] and [`option: key`] as byte arrays instead of file paths. -- [`option: noWaitAfter`] in [`method: Locator.selectOption`] was deprecated. +- TLS client certificates can now be passed from memory by passing [`option: Browser.newContext.clientCertificates.cert`] and [`option: Browser.newContext.clientCertificates.key`] as byte arrays instead of file paths. +- [`option: Locator.selectOption.noWaitAfter`] in [`method: Locator.selectOption`] was deprecated. - We've seen reports of WebGL in Webkit misbehaving on GitHub Actions `macos-13`. We recommend upgrading GitHub Actions to `macos-14`. ### Browser Versions @@ -338,7 +378,7 @@ New method [`method: Page.addLocatorHandler`] registers a callback that will be // Setup the handler. page.addLocatorHandler( page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Hej! You are in control of your cookies.")), - () - > { + () -> { page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Accept all")).click(); }); // Write the test as usual. @@ -349,7 +389,7 @@ assertThat(page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setName( ### New APIs -- [`method: Page.pdf`] accepts two new options [`option: tagged`] and [`option: outline`]. +- [`method: Page.pdf`] accepts two new options [`option: Page.pdf.tagged`] and [`option: Page.pdf.outline`]. ### Announcements @@ -372,7 +412,7 @@ This version was also tested against the following stable channels: - New method [`method: Page.unrouteAll`] removes all routes registered by [`method: Page.route`] and [`method: Page.routeFromHAR`]. - New method [`method: BrowserContext.unrouteAll`] removes all routes registered by [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`]. -- New option [`option: style`] in [`method: Page.screenshot`] and [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot. +- New options [`option: Page.screenshot.style`] in [`method: Page.screenshot`] and [`option: Locator.screenshot.style`] in [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot. ### Browser Versions @@ -410,8 +450,8 @@ assertThat(page.getByPlaceholder("Search docs")).hasValue("locator"); ### New APIs -- Option [`option: reason`] in [`method: Page.close`], [`method: BrowserContext.close`] and [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure. -- Option [`option: firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`]. +- Options [`option: Page.close.reason`] in [`method: Page.close`], [`option: BrowserContext.close.reason`] in [`method: BrowserContext.close`] and [`option: Browser.close.reason`] in [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure. +- Option [`option: BrowserType.launchPersistentContext.firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`]. ### Other Changes @@ -597,7 +637,7 @@ This version was also tested against the following stable channels: page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Dismiss")).click(); newEmail.click(); ``` -* Use new options [`option: hasNot`] and [`option: hasNotText`] in [`method: Locator.filter`] +* Use new options [`option: Locator.filter.hasNot`] and [`option: Locator.filter.hasNotText`] in [`method: Locator.filter`] to find elements that **do not match** certain conditions. ```java @@ -616,10 +656,10 @@ This version was also tested against the following stable channels: ### New APIs - [`method: Locator.or`] -- New option [`option: hasNot`] in [`method: Locator.filter`] -- New option [`option: hasNotText`] in [`method: Locator.filter`] +- New option [`option: Locator.filter.hasNot`] in [`method: Locator.filter`] +- New option [`option: Locator.filter.hasNotText`] in [`method: Locator.filter`] - [`method: LocatorAssertions.toBeAttached`] -- New option [`option: timeout`] in [`method: Route.fetch`] +- New option [`option: Route.fetch.timeout`] in [`method: Route.fetch`] ### Other highlights @@ -646,9 +686,9 @@ This version was also tested against the following stable channels: ### New APIs -- New options [`option: updateMode`] and [`option: updateContent`] in [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`]. +- New options [`option: Page.routeFromHAR.updateMode`] and [`option: Page.routeFromHAR.updateContent`] in [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`]. - Chaining existing locator objects, see [locator docs](./locators.md#matching-inside-a-locator) for details. -- New option [`option: name`] in method [`method: Tracing.startChunk`]. +- New option [`option: Tracing.startChunk.name`] in method [`method: Tracing.startChunk`]. ### Browser Versions @@ -1147,14 +1187,12 @@ Playwright for Java 1.18 introduces [Web-First Assertions](./test-assertions). Consider the following example: ```java -... import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; public class TestExample { - ... @Test void statusBecomesSubmitted() { - ... + // ... page.locator("#submit-button").click(); assertThat(page.locator(".status")).hasText("Submitted"); } @@ -1431,19 +1469,19 @@ button.click("button >> visible=true"); Traces are recorded using the new [`property: BrowserContext.tracing`] API: ```java -Browser browser = chromium.launch(); +Browser browser = playwright.chromium().launch(); BrowserContext context = browser.newContext(); // Start tracing before creating / navigating a page. -context.tracing.start(new Tracing.StartOptions() +context.tracing().start(new Tracing.StartOptions() .setScreenshots(true) - .setSnapshots(true); + .setSnapshots(true)); Page page = context.newPage(); -page.goto("https://playwright.dev"); +page.navigate("https://playwright.dev"); // Stop tracing and export it into a zip archive. -context.tracing.stop(new Tracing.StopOptions() +context.tracing().stop(new Tracing.StopOptions() .setPath(Paths.get("trace.zip"))); ``` diff --git a/docs/src/release-notes-js.md b/docs/src/release-notes-js.md index a89b79c5ce..b366c43dbd 100644 --- a/docs/src/release-notes-js.md +++ b/docs/src/release-notes-js.md @@ -6,6 +6,48 @@ toc_max_heading_level: 2 import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.48 + +### WebSocket routing + +New methods [`method: Page.routeWebSocket`] and [`method: BrowserContext.routeWebSocket`] allow to intercept, modify and mock WebSocket connections initiated in the page. Below is a simple example that mocks WebSocket communication by responding to a `"request"` with a `"response"`. + +```js +await page.routeWebSocket('/ws', ws => { + ws.onMessage(message => { + if (message === 'request') + ws.send('response'); + }); +}); +``` + +See [WebSocketRoute] for more details. + +### UI updates + +- New "copy" buttons for annotations and test location in the HTML report. +- Route method calls like [`method: Route.fulfill`] are not shown in the report and trace viewer anymore. You can see which network requests were routed in the network tab instead. +- New "Copy as cURL" and "Copy as fetch" buttons for requests in the network tab. + +### Miscellaneous + +- Option [`option: APIRequestContext.fetch.form`] and similar ones now accept [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData). +- New method [`method: Page.requestGC`] may help detect memory leaks. +- New option [`option: Test.step.location`] to pass custom step location. +- Requests made by [APIRequestContext] now record detailed timing and security information in the HAR. + +### Browser Versions + +- Chromium 130.0.6723.19 +- Mozilla Firefox 130.0 +- WebKit 18.0 + +This version was also tested against the following stable channels: + +- Google Chrome 129 +- Microsoft Edge 129 + + ## Version 1.47 ### Network Tab improvements @@ -44,16 +86,16 @@ test('query params', async ({ request }) => { ); // ... }); -``` +``` ### Miscellaneous - The `mcr.microsoft.com/playwright:v1.47.0` now serves a Playwright image based on Ubuntu 24.04 Noble. To use the 22.04 jammy-based image, please use `mcr.microsoft.com/playwright:v1.47.0-jammy` instead. -- New option [`option: behavior`] in [`method: Page.removeAllListeners`], [`method: Browser.removeAllListeners`] and [`method: BrowserContext.removeAllListeners`] to wait for ongoing listeners to complete. -- TLS client certificates can now be passed from memory by passing [`option: cert`] and [`option: key`] as buffers instead of file paths. +- New options [`option: Page.removeAllListeners.behavior`], [`option: Browser.removeAllListeners.behavior`] and [`option: BrowserContext.removeAllListeners.behavior`] to wait for ongoing listeners to complete. +- TLS client certificates can now be passed from memory by passing [`option: Browser.newContext.clientCertificates.cert`] and [`option: Browser.newContext.clientCertificates.key`] as buffers instead of file paths. - Attachments with a `text/html` content type can now be opened in a new tab in the HTML report. This is useful for including third-party reports or other HTML content in the Playwright test report and distributing it to your team. -- [`option: noWaitAfter`] in [`method: Locator.selectOption`] was deprecated. +- [`option: Locator.selectOption.noWaitAfter`] option in [`method: Locator.selectOption`] was deprecated. - We've seen reports of WebGL in Webkit misbehaving on GitHub Actions `macos-13`. We recommend upgrading GitHub Actions to `macos-14`. ### Browser Versions @@ -528,7 +570,7 @@ This version was also tested against the following stable channels: - New method [`method: Page.unrouteAll`] removes all routes registered by [`method: Page.route`] and [`method: Page.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them. - New method [`method: BrowserContext.unrouteAll`] removes all routes registered by [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them. -- New option [`option: style`] in [`method: Page.screenshot`] and [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot. +- New options [`option: Page.screenshot.style`] in [`method: Page.screenshot`] and [`option: Locator.screenshot.style`] in [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot. - New option `stylePath` for methods [`method: PageAssertions.toHaveScreenshot#1`] and [`method: LocatorAssertions.toHaveScreenshot#1`] to apply a custom stylesheet while making the screenshot. - New `fileName` option for [Blob reporter](./test-reporters#blob-reporter), to specify the name of the report to be created. @@ -577,8 +619,8 @@ test('test', async ({ page }) => { ### New APIs -- Option [`option: reason`] in [`method: Page.close`], [`method: BrowserContext.close`] and [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure. -- Option [`option: firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`]. +- Options [`option: Page.close.reason`] in [`method: Page.close`], [`option: BrowserContext.close.reason`] in [`method: BrowserContext.close`] and [`option: Browser.close.reason`] in [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure. +- Option [`option: BrowserType.launchPersistentContext.firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`]. ### Other Changes @@ -1047,7 +1089,7 @@ This version was also tested against the following stable channels: await page.getByRole('button', { name: 'Dismiss' }).click(); await newEmail.click(); ``` -* Use new options [`option: hasNot`] and [`option: hasNotText`] in [`method: Locator.filter`] +* Use new options [`option: Locator.filter.hasNot`] and [`option: Locator.filter.hasNotText`] in [`method: Locator.filter`] to find elements that **do not match** certain conditions. ```js @@ -1064,10 +1106,10 @@ This version was also tested against the following stable channels: ### New APIs - [`method: Locator.or`] -- New option [`option: hasNot`] in [`method: Locator.filter`] -- New option [`option: hasNotText`] in [`method: Locator.filter`] +- New option [`option: Locator.filter.hasNot`] in [`method: Locator.filter`] +- New option [`option: Locator.filter.hasNotText`] in [`method: Locator.filter`] - [`method: LocatorAssertions.toBeAttached`] -- New option [`option: timeout`] in [`method: Route.fetch`] +- New option [`option: Route.fetch.timeout`] in [`method: Route.fetch`] - [`method: Reporter.onExit`] ### ⚠️ Breaking change @@ -1107,10 +1149,10 @@ npx playwright test --ui ### New APIs -- New options [`option: updateMode`] and [`option: updateContent`] in [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`]. +- New options [`option: Page.routeFromHAR.updateMode`] and [`option: Page.routeFromHAR.updateContent`] in [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`]. - Chaining existing locator objects, see [locator docs](./locators.md#matching-inside-a-locator) for details. - New property [`property: TestInfo.testId`]. -- New option [`option: name`] in method [`method: Tracing.startChunk`]. +- New option [`option: Tracing.startChunk.name`] in method [`method: Tracing.startChunk`]. ### ⚠️ Breaking change in component tests diff --git a/docs/src/release-notes-python.md b/docs/src/release-notes-python.md index 7defa1c0d1..211e0ad5c2 100644 --- a/docs/src/release-notes-python.md +++ b/docs/src/release-notes-python.md @@ -4,6 +4,47 @@ title: "Release notes" toc_max_heading_level: 2 --- +## Version 1.48 + +### WebSocket routing + +New methods [`method: Page.routeWebSocket`] and [`method: BrowserContext.routeWebSocket`] allow to intercept, modify and mock WebSocket connections initiated in the page. Below is a simple example that mocks WebSocket communication by responding to a `"request"` with a `"response"`. + +```python +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + ws.send("response") + +page.route_web_socket("/ws", lambda ws: ws.on_message( + lambda message: message_handler(ws, message) +)) +``` + +See [WebSocketRoute] for more details. + +### UI updates + +- New "copy" buttons for annotations and test location in the HTML report. +- Route method calls like [`method: Route.fulfill`] are not shown in the report and trace viewer anymore. You can see which network requests were routed in the network tab instead. +- New "Copy as cURL" and "Copy as fetch" buttons for requests in the network tab. + +### Miscellaneous + +- New method [`method: Page.requestGC`] may help detect memory leaks. +- Requests made by [APIRequestContext] now record detailed timing and security information in the HAR. + +### Browser Versions + +- Chromium 130.0.6723.19 +- Mozilla Firefox 130.0 +- WebKit 18.0 + +This version was also tested against the following stable channels: + +- Google Chrome 129 +- Microsoft Edge 129 + + ## Version 1.47 ### Network Tab improvements @@ -21,8 +62,8 @@ The Network tab in the trace viewer has several nice improvements: - The `mcr.microsoft.com/playwright/python:v1.47.0` now serves a Playwright image based on Ubuntu 24.04 Noble. To use the 22.04 jammy-based image, please use `mcr.microsoft.com/playwright/python:v1.47.0-jammy` instead. - The `:latest`/`:focal`/`:jammy` tag for Playwright Docker images is no longer being published. Pin to a specific version for better stability and reproducibility. -- TLS client certificates can now be passed from memory by passing [`option: cert`] and [`option: key`] as bytes instead of file paths. -- [`option: noWaitAfter`] in [`method: Locator.selectOption`] was deprecated. +- TLS client certificates can now be passed from memory by passing [`option: Browser.newContext.clientCertificates.cert`] and [`option: Browser.newContext.clientCertificates.key`] as bytes instead of file paths. +- [`option: Locator.selectOption.noWaitAfter`] in [`method: Locator.selectOption`] was deprecated. - We've seen reports of WebGL in Webkit misbehaving on GitHub Actions `macos-13`. We recommend upgrading GitHub Actions to `macos-14`. ### Browser Versions @@ -260,7 +301,7 @@ expect(page.get_by_role("heading", name="Light and easy")).to_be_visible() ### New APIs -- [`method: Page.pdf`] accepts two new options [`option: tagged`] and [`option: outline`]. +- [`method: Page.pdf`] accepts two new options [`option: Page.pdf.tagged`] and [`option: Page.pdf.outline`]. ### Announcements @@ -283,7 +324,7 @@ This version was also tested against the following stable channels: - New method [`method: Page.unrouteAll`] removes all routes registered by [`method: Page.route`] and [`method: Page.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them. - New method [`method: BrowserContext.unrouteAll`] removes all routes registered by [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them. -- New option [`option: style`] in [`method: Page.screenshot`] and [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot. +- New options [`option: Page.screenshot.style`] in [`method: Page.screenshot`] and [`option: Locator.screenshot.style`] in [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot. ### Browser Versions @@ -324,8 +365,8 @@ def test_example(page: Page) -> None: ### New APIs -- Option [`option: reason`] in [`method: Page.close`], [`method: BrowserContext.close`] and [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure. -- Option [`option: firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`]. +- Options [`option: Page.close.reason`] in [`method: Page.close`], [`option: BrowserContext.close.reason`] in [`method: BrowserContext.close`] and [`option: Browser.close.reason`] in [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure. +- Option [`option: BrowserType.launchPersistentContext.firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`]. ### Other Changes @@ -504,7 +545,7 @@ This version was also tested against the following stable channels: page.get_by_role("button", name="Dismiss").click() new_email.click() ``` -* Use new options [`option: hasNot`] and [`option: hasNotText`] in [`method: Locator.filter`] +* Use new options [`option: Locator.filter.hasNot`] and [`option: Locator.filter.hasNotText`] in [`method: Locator.filter`] to find elements that **do not match** certain conditions. ```python @@ -520,10 +561,10 @@ This version was also tested against the following stable channels: ### New APIs - [`method: Locator.or`] -- New option [`option: hasNot`] in [`method: Locator.filter`] -- New option [`option: hasNotText`] in [`method: Locator.filter`] +- New option [`option: Locator.filter.hasNot`] in [`method: Locator.filter`] +- New option [`option: Locator.filter.hasNotText`] in [`method: Locator.filter`] - [`method: LocatorAssertions.toBeAttached`] -- New option [`option: timeout`] in [`method: Route.fetch`] +- New option [`option: Route.fetch.timeout`] in [`method: Route.fetch`] ### ⚠️ Breaking change @@ -547,9 +588,9 @@ This version was also tested against the following stable channels: ### New APIs - Custom expect message, see [test assertions documentation](./test-assertions.md#custom-expect-message). -- New options [`option: updateMode`] and [`option: updateContent`] in [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`]. +- New options [`option: Page.routeFromHAR.updateMode`] and [`option: Page.routeFromHAR.updateContent`] in [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`]. - Chaining existing locator objects, see [locator docs](./locators.md#matching-inside-a-locator) for details. -- New option [`option: name`] in method [`method: Tracing.startChunk`]. +- New option [`option: Tracing.startChunk.name`] in method [`method: Tracing.startChunk`]. ### Browser Versions diff --git a/docs/src/screenshots.md b/docs/src/screenshots.md index d1c7e0e04f..ba50a852a4 100644 --- a/docs/src/screenshots.md +++ b/docs/src/screenshots.md @@ -21,7 +21,7 @@ page.screenshot(path="screenshot.png") ```java page.screenshot(new Page.ScreenshotOptions() - .setPath(Paths.get("screenshot.png"))) + .setPath(Paths.get("screenshot.png"))); ``` ```csharp diff --git a/docs/src/selenium-grid.md b/docs/src/selenium-grid.md index 636bfb6d43..672ea54ac8 100644 --- a/docs/src/selenium-grid.md +++ b/docs/src/selenium-grid.md @@ -10,8 +10,6 @@ Playwright can connect to [Selenium Grid Hub](https://www.selenium.dev/documenta :::warning There is a risk of Playwright integration with Selenium Grid Hub breaking in the future. Make sure you weight risks against benefits before using it. -<br /> -<br /> <details> <summary> <span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>More details</span> @@ -79,19 +77,19 @@ SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_CAPABILITIES=" If your grid requires additional headers to be set (for example, you should provide authorization token to use browsers in your cloud), you can set `SELENIUM_REMOTE_HEADERS` environment variable to provide JSON-serialized headers. ```bash js -SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_HEADERS="{'Authorization':'OAuth 12345'}" npx playwright test +SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_HEADERS="{'Authorization':'Basic b64enc'}" npx playwright test ``` ```bash python -SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_HEADERS="{'Authorization':'OAuth 12345'}" pytest --browser chromium +SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_HEADERS="{'Authorization':'Basic b64enc'}" pytest --browser chromium ``` ```bash java -SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_HEADERS="{'Authorization':'OAuth 12345'}" mvn test +SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_HEADERS="{'Authorization':'Basic b64enc'}" mvn test ``` ```bash csharp -SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_HEADERS="{'Authorization':'OAuth 12345'}" dotnet test +SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 SELENIUM_REMOTE_HEADERS="{'Authorization':'Basic b64enc'}" dotnet test ``` ### Detailed logs @@ -120,7 +118,7 @@ If you file an issue, please include this log. ## Using Selenium Docker -One easy way to use Selenium Grid is to run official docker containers. Read more in [selenium docker images](https://github.com/SeleniumHQ/docker-selenium) documentation. For experimental arm images, see [docker-seleniarm](https://github.com/seleniumhq-community/docker-seleniarm). +One easy way to use Selenium Grid is to run official docker containers. Read more in [selenium docker images](https://github.com/SeleniumHQ/docker-selenium) documentation. For image tagging convention, [read more](https://github.com/SeleniumHQ/docker-selenium/wiki/Tagging-Convention#selenium-grid-4x-and-above). ### Standalone mode @@ -129,10 +127,7 @@ Here is an example of running selenium standalone and connecting Playwright to i First start Selenium. ```bash -docker run -d -p 4444:4444 --shm-size="2g" -e SE_NODE_GRID_URL="http://localhost:4444" selenium/standalone-chrome:4.3.0-20220726 - -# Alternatively for arm architecture -docker run -d -p 4444:4444 --shm-size="2g" -e SE_NODE_GRID_URL="http://localhost:4444" seleniarm/standalone-chromium:103.0 +docker run -d -p 4444:4444 --shm-size="2g" -e SE_NODE_GRID_URL="http://localhost:4444" selenium/standalone-chromium:latest ``` Then run Playwright. @@ -160,24 +155,14 @@ Here is an example of running selenium hub and a single selenium node, and conne First start the hub container and one or more node containers. ```bash -docker run -d -p 4442-4444:4442-4444 --name selenium-hub selenium/hub:4.3.0-20220726 +docker run -d -p 4442-4444:4442-4444 --name selenium-hub selenium/hub:4.25.0 docker run -d -p 5555:5555 \ --shm-size="2g" \ -e SE_EVENT_BUS_HOST=<selenium-hub-ip> \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -e SE_NODE_GRID_URL="http://<selenium-hub-ip>:4444" - selenium/node-chrome:4.3.0-20220726 - -# Alternatively for arm architecture -docker run -d -p 4442-4444:4442-4444 --name selenium-hub seleniarm/hub:4.3.0-20220728 -docker run -d -p 5555:5555 \ - --shm-size="2g" \ - -e SE_EVENT_BUS_HOST=<selenium-hub-ip> \ - -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ - -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ - -e SE_NODE_GRID_URL="http://<selenium-hub-ip>:4444" - seleniarm/node-chromium:103.0 + selenium/node-chromium:4.25.0 ``` Then run Playwright. diff --git a/docs/src/test-reporter-api/class-location.md b/docs/src/test-api/class-location.md similarity index 100% rename from docs/src/test-reporter-api/class-location.md rename to docs/src/test-api/class-location.md diff --git a/docs/src/test-api/class-test.md b/docs/src/test-api/class-test.md index 9b56fbf2e6..4706d462f0 100644 --- a/docs/src/test-api/class-test.md +++ b/docs/src/test-api/class-test.md @@ -1710,6 +1710,12 @@ Step body. Whether to box the step in the report. Defaults to `false`. When the step is boxed, errors thrown from the step internals point to the step call site. See below for more details. +### option: Test.step.location +* since: v1.48 +- `location` <[Location]> + +Specifies a custom location for the step to be shown in test reports and trace viewer. By default, location of the [`method: Test.step`] call is shown. + ## method: Test.use * since: v1.10 diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index 4130bdd66d..d013f5e4ea 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -41,12 +41,12 @@ export default defineConfig({ - type: ?<[Object]> - `timeout` ?<[int]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms. - `toHaveScreenshot` ?<[Object]> Configuration for the [`method: PageAssertions.toHaveScreenshot#1`] method. - - `animations` ?<[ScreenshotAnimations]<"allow"|"disabled">> See [`option: animations`] in [`method: Page.screenshot`]. Defaults to `"disabled"`. - - `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`. + - `animations` ?<[ScreenshotAnimations]<"allow"|"disabled">> See [`option: Page.screenshot.animations`] in [`method: Page.screenshot`]. Defaults to `"disabled"`. + - `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: Page.screenshot.caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`. - `maxDiffPixels` ?<[int]> An acceptable amount of pixels that could be different, unset by default. - `maxDiffPixelRatio` ?<[float]> An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. - - `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: scale`] in [`method: Page.screenshot`]. Defaults to `"css"`. - - `stylePath` ?<[string]|[Array]<[string]>> See [`option: style`] in [`method: Page.screenshot`]. + - `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: Page.screenshot.scale`] in [`method: Page.screenshot`]. Defaults to `"css"`. + - `stylePath` ?<[string]|[Array]<[string]>> See [`option: Page.screenshot.style`] in [`method: Page.screenshot`]. - `threshold` ?<[float]> An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - `toMatchSnapshot` ?<[Object]> Configuration for the [`method: SnapshotAssertions.toMatchSnapshot#1`] method. - `maxDiffPixels` ?<[int]> An acceptable amount of pixels that could be different, unset by default. diff --git a/docs/src/test-api/class-testinfo.md b/docs/src/test-api/class-testinfo.md index 38c706adf7..93cbc93b75 100644 --- a/docs/src/test-api/class-testinfo.md +++ b/docs/src/test-api/class-testinfo.md @@ -216,7 +216,9 @@ Test function as passed to `test(title, testFunction)`. Tags that apply to the test. Learn more about [tags](../test-annotations.md#tag-tests). -Note that any changes made to this list while the test is running will not be visible to test reporters. +:::note +Any changes made to this list while the test is running will not be visible to test reporters. +::: ## property: TestInfo.testId * since: v1.32 diff --git a/docs/src/test-api/class-testproject.md b/docs/src/test-api/class-testproject.md index 5e7c911a65..d93286fa26 100644 --- a/docs/src/test-api/class-testproject.md +++ b/docs/src/test-api/class-testproject.md @@ -94,10 +94,10 @@ export default defineConfig({ - `threshold` ?<[float]> an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default. - `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default. - - `animations` ?<[ScreenshotAnimations]<"allow"|"disabled">> See [`option: animations`] in [`method: Page.screenshot`]. Defaults to `"disabled"`. - - `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`. - - `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: scale`] in [`method: Page.screenshot`]. Defaults to `"css"`. - - `stylePath` ?<[string]|[Array]<[string]>> See [`option: style`] in [`method: Page.screenshot`]. + - `animations` ?<[ScreenshotAnimations]<"allow"|"disabled">> See [`option: Page.screenshot.animations`] in [`method: Page.screenshot`]. Defaults to `"disabled"`. + - `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: Page.screenshot.caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`. + - `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: Page.screenshot.scale`] in [`method: Page.screenshot`]. Defaults to `"css"`. + - `stylePath` ?<[string]|[Array]<[string]>> See [`option: Page.screenshot.style`] in [`method: Page.screenshot`]. - `toMatchSnapshot` ?<[Object]> Configuration for the [`method: SnapshotAssertions.toMatchSnapshot#1`] method. - `threshold` ?<[float]> an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`. - `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default. diff --git a/docs/src/test-runners-java.md b/docs/src/test-runners-java.md index 1aaa7135c4..ed1d3d81e5 100644 --- a/docs/src/test-runners-java.md +++ b/docs/src/test-runners-java.md @@ -202,7 +202,7 @@ You can use a Gradle build configuration script, written in Groovy or Kotlin. }> <TabItem value="gradle"> -```java +```groovy plugins { application id 'java' @@ -234,7 +234,7 @@ test { </TabItem> <TabItem value="gradle-kotlin"> -```java +```groovy plugins { application id("java") diff --git a/docs/src/test-sharding-js.md b/docs/src/test-sharding-js.md index d0312db811..263733e78d 100644 --- a/docs/src/test-sharding-js.md +++ b/docs/src/test-sharding-js.md @@ -85,7 +85,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: lts/* - name: Install dependencies run: npm ci - name: Install Playwright browsers @@ -118,7 +118,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: lts/* - name: Install dependencies run: npm ci diff --git a/docs/src/trace-viewer.md b/docs/src/trace-viewer.md index 207646c1d7..55338c41b9 100644 --- a/docs/src/trace-viewer.md +++ b/docs/src/trace-viewer.md @@ -31,7 +31,7 @@ In the Actions tab you can see what locator was used for every action and how lo ### Screenshots -When tracing with the [`option: screenshots`] option turned on (default), each trace records a screencast and renders it as a film strip. You can hover over the film strip to see a magnified image of for each action and state which helps you easily find the action you want to inspect. +When tracing with the [`option: Tracing.start.screenshots`] option turned on (default), each trace records a screencast and renders it as a film strip. You can hover over the film strip to see a magnified image of for each action and state which helps you easily find the action you want to inspect. Double click on an action to see the time range for that action. You can use the slider in the timeline to increase the actions selected and these will be shown in the Actions tab and all console logs and network logs will be filtered to only show the logs for the actions selected. @@ -40,7 +40,7 @@ Double click on an action to see the time range for that action. You can use the ### Snapshots -When tracing with the [`option: snapshots`] option turned on (default), Playwright captures a set of complete DOM snapshots for each action. Depending on the type of the action, it will capture: +When tracing with the [`option: Tracing.start.snapshots`] option turned on (default), Playwright captures a set of complete DOM snapshots for each action. Depending on the type of the action, it will capture: | Type | Description | |------|-------------| @@ -48,8 +48,6 @@ When tracing with the [`option: snapshots`] option turned on (default), Playwrig |Action|A snapshot at the moment of the performed input. This type of snapshot is especially useful when exploring where exactly Playwright clicked.| |After|A snapshot after the action.| -<br/> - Here is what the typical Action snapshot looks like: ![action tab in trace viewer](https://github.com/microsoft/playwright/assets/13063165/7168d549-eb0a-4964-9c93-483f03711fa9) diff --git a/package-lock.json b/package-lock.json index b7cdad125e..71c0dad10c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "playwright-internal", - "version": "1.48.0-next", + "version": "1.49.0-next", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "playwright-internal", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "workspaces": [ "packages/*" @@ -40,7 +40,7 @@ "@zip.js/zip.js": "^2.7.29", "ansi-styles": "^4.3.0", "chokidar": "^3.5.3", - "chromium-bidi": "^0.6.4", + "chromium-bidi": "^0.7.1", "colors": "^1.4.0", "concurrently": "^6.2.1", "cross-env": "^7.0.3", @@ -61,7 +61,7 @@ "react-dom": "^18.1.0", "ssim.js": "^3.5.0", "typescript": "^5.5.3", - "vite": "^5.0.13", + "vite": "^5.4.6", "ws": "^8.17.1", "xml2js": "^0.5.0", "yaml": "^2.2.2" @@ -852,9 +852,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -1517,9 +1517,9 @@ "link": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", - "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", "cpu": [ "arm" ], @@ -1529,9 +1529,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", - "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", "cpu": [ "arm64" ], @@ -1541,9 +1541,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", - "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", "cpu": [ "arm64" ], @@ -1553,9 +1553,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", - "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", "cpu": [ "x64" ], @@ -1565,9 +1565,21 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", - "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", "cpu": [ "arm" ], @@ -1577,9 +1589,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", - "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", "cpu": [ "arm64" ], @@ -1589,9 +1601,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", - "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", "cpu": [ "arm64" ], @@ -1601,11 +1613,11 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", - "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", "cpu": [ - "ppc64le" + "ppc64" ], "optional": true, "os": [ @@ -1613,9 +1625,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", - "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", "cpu": [ "riscv64" ], @@ -1625,9 +1637,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", - "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", "cpu": [ "s390x" ], @@ -1637,9 +1649,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", - "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", "cpu": [ "x64" ], @@ -1649,9 +1661,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", - "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", "cpu": [ "x64" ], @@ -1661,9 +1673,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", - "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", "cpu": [ "arm64" ], @@ -1673,9 +1685,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", - "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", "cpu": [ "ia32" ], @@ -1685,9 +1697,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", - "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", "cpu": [ "x64" ], @@ -2876,9 +2888,9 @@ } }, "node_modules/chromium-bidi": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.5.tgz", - "integrity": "sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.7.1.tgz", + "integrity": "sha512-am9lR+HidiBtPtEYV7aFBpFJaQZhwJbYKr37cOHw0GGR+uiG0O79f20JNLjR0qEwPMuxOHvdBu4HHfimClBOCg==", "dev": true, "dependencies": { "mitt": "3.0.1", @@ -5405,9 +5417,9 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { "braces": "^3.0.3", @@ -5862,9 +5874,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -5917,9 +5929,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -5936,8 +5948,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6301,9 +6313,9 @@ } }, "node_modules/rollup": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", - "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "dependencies": { "@types/estree": "1.0.5" }, @@ -6315,21 +6327,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.0", - "@rollup/rollup-android-arm64": "4.14.0", - "@rollup/rollup-darwin-arm64": "4.14.0", - "@rollup/rollup-darwin-x64": "4.14.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", - "@rollup/rollup-linux-arm64-gnu": "4.14.0", - "@rollup/rollup-linux-arm64-musl": "4.14.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", - "@rollup/rollup-linux-riscv64-gnu": "4.14.0", - "@rollup/rollup-linux-s390x-gnu": "4.14.0", - "@rollup/rollup-linux-x64-gnu": "4.14.0", - "@rollup/rollup-linux-x64-musl": "4.14.0", - "@rollup/rollup-win32-arm64-msvc": "4.14.0", - "@rollup/rollup-win32-ia32-msvc": "4.14.0", - "@rollup/rollup-win32-x64-msvc": "4.14.0", + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", "fsevents": "~2.3.2" } }, @@ -6589,9 +6602,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -7171,13 +7184,13 @@ } }, "node_modules/vite": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", - "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -7196,6 +7209,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -7213,6 +7227,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -7243,9 +7260,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -7258,9 +7275,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -7273,9 +7290,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -7288,9 +7305,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -7303,9 +7320,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -7318,9 +7335,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -7333,9 +7350,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -7348,9 +7365,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -7363,9 +7380,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -7378,9 +7395,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -7393,9 +7410,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -7408,9 +7425,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -7423,9 +7440,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -7438,9 +7455,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -7453,9 +7470,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -7468,9 +7485,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -7483,9 +7500,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -7498,9 +7515,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -7513,9 +7530,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -7528,9 +7545,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -7543,9 +7560,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -7558,9 +7575,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -7573,9 +7590,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -7584,29 +7601,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/vitefu": { @@ -7908,10 +7925,10 @@ } }, "packages/playwright": { - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" }, "bin": { "playwright": "cli.js" @@ -7925,11 +7942,11 @@ }, "packages/playwright-browser-chromium": { "name": "@playwright/browser-chromium", - "version": "1.48.0-next", + "version": "1.49.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" }, "engines": { "node": ">=18" @@ -7937,11 +7954,11 @@ }, "packages/playwright-browser-firefox": { "name": "@playwright/browser-firefox", - "version": "1.48.0-next", + "version": "1.49.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" }, "engines": { "node": ">=18" @@ -7949,22 +7966,22 @@ }, "packages/playwright-browser-webkit": { "name": "@playwright/browser-webkit", - "version": "1.48.0-next", + "version": "1.49.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" }, "engines": { "node": ">=18" } }, "packages/playwright-chromium": { - "version": "1.48.0-next", + "version": "1.49.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" }, "bin": { "playwright": "cli.js" @@ -7974,7 +7991,7 @@ } }, "packages/playwright-core": { - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -7985,11 +8002,11 @@ }, "packages/playwright-ct-core": { "name": "@playwright/experimental-ct-core", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "playwright": "1.48.0-next", - "playwright-core": "1.48.0-next", + "playwright": "1.49.0-next", + "playwright-core": "1.49.0-next", "vite": "^5.2.8" }, "engines": { @@ -7998,10 +8015,10 @@ }, "packages/playwright-ct-react": { "name": "@playwright/experimental-ct-react", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8013,10 +8030,10 @@ }, "packages/playwright-ct-react17": { "name": "@playwright/experimental-ct-react17", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8028,10 +8045,10 @@ }, "packages/playwright-ct-solid": { "name": "@playwright/experimental-ct-solid", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "vite-plugin-solid": "^2.7.0" }, "bin": { @@ -8046,10 +8063,10 @@ }, "packages/playwright-ct-svelte": { "name": "@playwright/experimental-ct-svelte", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@sveltejs/vite-plugin-svelte": "^3.0.1" }, "bin": { @@ -8064,10 +8081,10 @@ }, "packages/playwright-ct-vue": { "name": "@playwright/experimental-ct-vue", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@vitejs/plugin-vue": "^4.2.1" }, "bin": { @@ -8079,10 +8096,10 @@ }, "packages/playwright-ct-vue2": { "name": "@playwright/experimental-ct-vue2", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@vitejs/plugin-vue2": "^2.2.0" }, "bin": { @@ -8131,11 +8148,11 @@ } }, "packages/playwright-firefox": { - "version": "1.48.0-next", + "version": "1.49.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" }, "bin": { "playwright": "cli.js" @@ -8146,10 +8163,10 @@ }, "packages/playwright-test": { "name": "@playwright/test", - "version": "1.48.0-next", + "version": "1.49.0-next", "license": "Apache-2.0", "dependencies": { - "playwright": "1.48.0-next" + "playwright": "1.49.0-next" }, "bin": { "playwright": "cli.js" @@ -8159,11 +8176,11 @@ } }, "packages/playwright-webkit": { - "version": "1.48.0-next", + "version": "1.49.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" }, "bin": { "playwright": "cli.js" diff --git a/package.json b/package.json index c095546c7d..1f2ce943c9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "playwright-internal", "private": true, - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -79,7 +79,7 @@ "@zip.js/zip.js": "^2.7.29", "ansi-styles": "^4.3.0", "chokidar": "^3.5.3", - "chromium-bidi": "^0.6.4", + "chromium-bidi": "^0.7.1", "colors": "^1.4.0", "concurrently": "^6.2.1", "cross-env": "^7.0.3", @@ -100,7 +100,7 @@ "react-dom": "^18.1.0", "ssim.js": "^3.5.0", "typescript": "^5.5.3", - "vite": "^5.0.13", + "vite": "^5.4.6", "ws": "^8.17.1", "xml2js": "^0.5.0", "yaml": "^2.2.2" diff --git a/packages/html-reporter/src/testCaseView.css b/packages/html-reporter/src/testCaseView.css index d87cf3aacb..56be1d9620 100644 --- a/packages/html-reporter/src/testCaseView.css +++ b/packages/html-reporter/src/testCaseView.css @@ -47,6 +47,7 @@ flex: none; align-items: center; padding: 0 8px 8px; + line-height: 24px; } .test-case-path { @@ -60,6 +61,7 @@ align-items: center; padding: 0 8px; line-height: 24px; + white-space: pre-wrap; } @media only screen and (max-width: 600px) { diff --git a/packages/html-reporter/src/testCaseView.spec.tsx b/packages/html-reporter/src/testCaseView.spec.tsx index 72552f5184..7c9c99eeb3 100644 --- a/packages/html-reporter/src/testCaseView.spec.tsx +++ b/packages/html-reporter/src/testCaseView.spec.tsx @@ -81,9 +81,9 @@ test('should render copy buttons for annotations', async ({ mount, page, context const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={testCase} run={0} anchor=''></TestCaseView>); await expect(component.getByText('Annotation text', { exact: false }).first()).toBeVisible(); - component.getByText('Annotation text', { exact: false }).first().hover(); - await expect(component.getByLabel('Copy to clipboard').first()).toBeVisible(); - await component.getByLabel('Copy to clipboard').first().click(); + await component.getByText('Annotation text', { exact: false }).first().hover(); + await expect(component.locator('.test-case-annotation').getByLabel('Copy to clipboard').first()).toBeVisible(); + await component.locator('.test-case-annotation').getByLabel('Copy to clipboard').first().click(); const handle = await page.evaluateHandle(() => navigator.clipboard.readText()); const clipboardContent = await handle.jsonValue(); expect(clipboardContent).toBe('Annotation text'); diff --git a/packages/html-reporter/src/testCaseView.tsx b/packages/html-reporter/src/testCaseView.tsx index 1fe4f42ed3..e3656bf414 100644 --- a/packages/html-reporter/src/testCaseView.tsx +++ b/packages/html-reporter/src/testCaseView.tsx @@ -50,7 +50,11 @@ export const TestCaseView: React.FC<{ {test && <div className='test-case-path'>{test.path.join(' › ')}</div>} {test && <div className='test-case-title'>{test?.title}</div>} {test && <div className='hbox'> - <div className='test-case-location'>{test.location.file}:{test.location.line}</div> + <div className='test-case-location'> + <CopyToClipboardContainer value={`${test?.location.file}:${test?.location.line}`}> + {test.location.file}:{test.location.line} + </CopyToClipboardContainer> + </div> <div style={{ flex: 'auto' }}></div> <div className='test-case-duration'>{msToString(test.duration)}</div> </div>} diff --git a/packages/playwright-browser-chromium/package.json b/packages/playwright-browser-chromium/package.json index ac4869cb7a..917150ade7 100644 --- a/packages/playwright-browser-chromium/package.json +++ b/packages/playwright-browser-chromium/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-chromium", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright package that automatically installs Chromium", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" } } diff --git a/packages/playwright-browser-firefox/package.json b/packages/playwright-browser-firefox/package.json index 974c76ac0c..98f0332b71 100644 --- a/packages/playwright-browser-firefox/package.json +++ b/packages/playwright-browser-firefox/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-firefox", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright package that automatically installs Firefox", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" } } diff --git a/packages/playwright-browser-webkit/package.json b/packages/playwright-browser-webkit/package.json index 58e819b0d3..09574a266c 100644 --- a/packages/playwright-browser-webkit/package.json +++ b/packages/playwright-browser-webkit/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-webkit", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright package that automatically installs WebKit", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" } } diff --git a/packages/playwright-chromium/package.json b/packages/playwright-chromium/package.json index 00f5299d3e..3390def41c 100644 --- a/packages/playwright-chromium/package.json +++ b/packages/playwright-chromium/package.json @@ -1,6 +1,6 @@ { "name": "playwright-chromium", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "A high-level API to automate Chromium", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" } } diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index dc91235659..00c7a751f4 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -3,21 +3,21 @@ "browsers": [ { "name": "chromium", - "revision": "1135", + "revision": "1140", "installByDefault": true, - "browserVersion": "129.0.6668.42" + "browserVersion": "130.0.6723.31" }, { "name": "chromium-tip-of-tree", - "revision": "1259", + "revision": "1266", "installByDefault": false, - "browserVersion": "130.0.6713.0" + "browserVersion": "131.0.6754.0" }, { "name": "firefox", - "revision": "1464", + "revision": "1465", "installByDefault": true, - "browserVersion": "130.0" + "browserVersion": "131.0" }, { "name": "firefox-beta", @@ -27,7 +27,7 @@ }, { "name": "webkit", - "revision": "2077", + "revision": "2084", "installByDefault": true, "revisionOverrides": { "mac10.14": "1446", diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index 3a931d4b9a..d55a652b57 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -1,6 +1,6 @@ { "name": "playwright-core", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index d8fa8230c6..1895f2dfcf 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -397,7 +397,7 @@ async function launchContext(options: Options, extraOptions: LaunchOptions): Pro process.stdout.write('\n-------------8<-------------\n'); const autoExitCondition = process.env.PWTEST_CLI_AUTO_EXIT_WHEN; if (autoExitCondition && text.includes(autoExitCondition)) - Promise.all(context.pages().map(async p => p.close())); + closeBrowser(); }; // Make sure we exit abnormally when browser crashes. const logs: string[] = []; @@ -504,7 +504,7 @@ async function launchContext(options: Options, extraOptions: LaunchOptions): Pro if (hasPage) return; // Avoid the error when the last page is closed because the browser has been closed. - closeBrowser().catch(e => null); + closeBrowser().catch(() => {}); }); }); process.on('SIGINT', async () => { @@ -560,17 +560,13 @@ async function open(options: Options, url: string | undefined, language: string) async function codegen(options: Options & { target: string, output?: string, testIdAttribute?: string }, url: string | undefined) { const { target: language, output: outputFile, testIdAttribute: testIdAttributeName } = options; - const tracesDir = path.join(os.tmpdir(), `recorder-trace-${Date.now()}`); + const tracesDir = path.join(os.tmpdir(), `playwright-recorder-trace-${Date.now()}`); const { context, launchOptions, contextOptions } = await launchContext(options, { headless: !!process.env.PWTEST_CLI_HEADLESS, executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH, tracesDir, }); dotenv.config({ path: 'playwright.env' }); - if (process.env.PW_RECORDER_IS_TRACE_VIEWER) { - await fs.promises.mkdir(tracesDir, { recursive: true }); - await context.tracing.start({ name: 'trace', _live: true }); - } await context._enableRecorder({ language, launchOptions, @@ -578,6 +574,7 @@ async function codegen(options: Options & { target: string, output?: string, tes device: options.device, saveStorage: options.saveStorage, mode: 'recording', + codegenMode: process.env.PW_RECORDER_IS_TRACE_VIEWER ? 'trace-events' : 'actions', testIdAttributeName, outputFile: outputFile ? path.resolve(outputFile) : undefined, }); diff --git a/packages/playwright-core/src/client/api.ts b/packages/playwright-core/src/client/api.ts index 6eab70e159..0d3d2448fe 100644 --- a/packages/playwright-core/src/client/api.ts +++ b/packages/playwright-core/src/client/api.ts @@ -34,7 +34,7 @@ export { TimeoutError } from './errors'; export { Frame } from './frame'; export { Keyboard, Mouse, Touchscreen } from './input'; export { JSHandle } from './jsHandle'; -export { Request, Response, Route, WebSocket } from './network'; +export { Request, Response, Route, WebSocket, WebSocketRoute } from './network'; export { APIRequest, APIRequestContext, APIResponse } from './fetch'; export { Page } from './page'; export { Selectors } from './selectors'; diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index ef222136dd..5ff432ec60 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -48,6 +48,7 @@ import { Clock } from './clock'; export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext { _pages = new Set<Page>(); _routes: network.RouteHandler[] = []; + _webSocketRoutes: network.WebSocketRouteHandler[] = []; readonly _browser: Browser | null = null; _browserType: BrowserType | undefined; readonly _bindings = new Map<string, (source: structs.BindingSource, ...args: any[]) => any>(); @@ -90,6 +91,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> this._channel.on('close', () => this._onClose()); this._channel.on('page', ({ page }) => this._onPage(Page.from(page))); this._channel.on('route', ({ route }) => this._onRoute(network.Route.from(route))); + this._channel.on('webSocketRoute', ({ webSocketRoute }) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute))); this._channel.on('backgroundPage', ({ page }) => { const backgroundPage = Page.from(page); this._backgroundPages.add(backgroundPage); @@ -218,7 +220,15 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> } // If the page is closed or unrouteAll() was called without waiting and interception disabled, // the method will throw an error - silence it. - await route._innerContinue(true).catch(() => {}); + await route._innerContinue(true /* isFallback */).catch(() => {}); + } + + async _onWebSocketRoute(webSocketRoute: network.WebSocketRoute) { + const routeHandler = this._webSocketRoutes.find(route => route.matches(webSocketRoute.url())); + if (routeHandler) + await routeHandler.handle(webSocketRoute); + else + webSocketRoute.connectToServer(); } async _onBinding(bindingCall: BindingCall) { @@ -328,6 +338,11 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> await this._updateInterceptionPatterns(); } + async routeWebSocket(url: URLMatch, handler: network.WebSocketRouteHandlerCallback): Promise<void> { + this._webSocketRoutes.unshift(new network.WebSocketRouteHandler(this._options.baseURL, url, handler)); + await this._updateWebSocketInterceptionPatterns(); + } + async _recordIntoHAR(har: string, page: Page | null, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full'} = {}): Promise<void> { const { harId } = await this._channel.harStart({ page: page?._channel, @@ -387,6 +402,11 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> await this._channel.setNetworkInterceptionPatterns({ patterns }); } + private async _updateWebSocketInterceptionPatterns() { + const patterns = network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes); + await this._channel.setWebSocketInterceptionPatterns({ patterns }); + } + _effectiveCloseReason(): string | undefined { return this._closeReason || this._browser?._closeReason; } @@ -472,17 +492,8 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> await this._closedPromise; } - async _enableRecorder(params: { - language: string, - launchOptions?: LaunchOptions, - contextOptions?: BrowserContextOptions, - device?: string, - saveStorage?: string, - mode?: 'recording' | 'inspecting', - testIdAttributeName?: string, - outputFile?: string, - }) { - await this._channel.recorderSupplementEnable(params); + async _enableRecorder(params: channels.BrowserContextEnableRecorderParams) { + await this._channel.enableRecorder(params); } } diff --git a/packages/playwright-core/src/client/channelOwner.ts b/packages/playwright-core/src/client/channelOwner.ts index abe1cbf254..89f3edced3 100644 --- a/packages/playwright-core/src/client/channelOwner.ts +++ b/packages/playwright-core/src/client/channelOwner.ts @@ -40,6 +40,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel _logger: Logger | undefined; readonly _instrumentation: ClientInstrumentation; private _eventToSubscriptionMapping: Map<string, string> = new Map(); + private _isInternalType = false; _wasCollected: boolean = false; constructor(parent: ChannelOwner | Connection, type: string, guid: string, initializer: channels.InitializerTraits<T>) { @@ -61,6 +62,10 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel this._initializer = initializer; } + protected markAsInternalType() { + this._isInternalType = true; + } + _setEventToSubscriptionMapping(mapping: Map<string, string>) { this._eventToSubscriptionMapping = mapping; } @@ -173,7 +178,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel let apiName: string | undefined = stackTrace.apiName; const frames: channels.StackFrame[] = stackTrace.frames; - isInternal = isInternal || this._type === 'LocalUtils'; + isInternal = isInternal || this._isInternalType; if (isInternal) apiName = undefined; diff --git a/packages/playwright-core/src/client/connection.ts b/packages/playwright-core/src/client/connection.ts index 953fc279e9..75d1878da3 100644 --- a/packages/playwright-core/src/client/connection.ts +++ b/packages/playwright-core/src/client/connection.ts @@ -21,7 +21,7 @@ import { ChannelOwner } from './channelOwner'; import { ElementHandle } from './elementHandle'; import { Frame } from './frame'; import { JSHandle } from './jsHandle'; -import { Request, Response, Route, WebSocket } from './network'; +import { Request, Response, Route, WebSocket, WebSocketRoute } from './network'; import { Page, BindingCall } from './page'; import { Worker } from './worker'; import { Dialog } from './dialog'; @@ -309,6 +309,9 @@ export class Connection extends EventEmitter { case 'WebSocket': result = new WebSocket(parent, type, guid, initializer); break; + case 'WebSocketRoute': + result = new WebSocketRoute(parent, type, guid, initializer); + break; case 'Worker': result = new Worker(parent, type, guid, initializer); break; diff --git a/packages/playwright-core/src/client/jsHandle.ts b/packages/playwright-core/src/client/jsHandle.ts index bb85894870..8577400f3d 100644 --- a/packages/playwright-core/src/client/jsHandle.ts +++ b/packages/playwright-core/src/client/jsHandle.ts @@ -78,13 +78,6 @@ export class JSHandle<T = any> extends ChannelOwner<channels.JSHandleChannel> im } } - async _objectCount() { - return await this._wrapApiCall(async () => { - const { count } = await this._channel.objectCount(); - return count; - }); - } - override toString(): string { return this._preview; } diff --git a/packages/playwright-core/src/client/localUtils.ts b/packages/playwright-core/src/client/localUtils.ts index 6cadb00326..530547b227 100644 --- a/packages/playwright-core/src/client/localUtils.ts +++ b/packages/playwright-core/src/client/localUtils.ts @@ -33,6 +33,7 @@ export class LocalUtils extends ChannelOwner<channels.LocalUtilsChannel> { constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.LocalUtilsInitializer) { super(parent, type, guid, initializer); + this.markAsInternalType(); this.devices = {}; for (const { name, descriptor } of initializer.deviceDescriptors) this.devices[name] = descriptor; diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 164af0f7e2..c24f0de431 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -299,6 +299,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.RouteInitializer) { super(parent, type, guid, initializer); + this.markAsInternalType(); } request(): Request { @@ -325,7 +326,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro async abort(errorCode?: string) { await this._handleRoute(async () => { - await this._raceWithTargetClose(this._channel.abort({ requestUrl: this.request()._initializer.url, errorCode })); + await this._raceWithTargetClose(this._channel.abort({ errorCode })); }); } @@ -409,7 +410,6 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro headers['content-length'] = String(length); await this._raceWithTargetClose(this._channel.fulfill({ - requestUrl: this.request()._initializer.url, status: statusOption || 200, headers: headersObjectToArray(headers), body, @@ -421,7 +421,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro async continue(options: FallbackOverrides = {}) { await this._handleRoute(async () => { this.request()._applyFallbackOverrides(options); - await this._innerContinue(); + await this._innerContinue(false /* isFallback */); }); } @@ -436,22 +436,178 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro chain.resolve(done); } - async _innerContinue(internal = false) { + async _innerContinue(isFallback: boolean) { const options = this.request()._fallbackOverridesForContinue(); - return await this._wrapApiCall(async () => { - await this._raceWithTargetClose(this._channel.continue({ - requestUrl: this.request()._initializer.url, - url: options.url, - method: options.method, - headers: options.headers ? headersObjectToArray(options.headers) : undefined, - postData: options.postDataBuffer, - isFallback: internal, - })); - }, !!internal); + return await this._raceWithTargetClose(this._channel.continue({ + url: options.url, + method: options.method, + headers: options.headers ? headersObjectToArray(options.headers) : undefined, + postData: options.postDataBuffer, + isFallback, + })); + } +} + +export class WebSocketRoute extends ChannelOwner<channels.WebSocketRouteChannel> implements api.WebSocketRoute { + static from(route: channels.WebSocketRouteChannel): WebSocketRoute { + return (route as any)._object; + } + + private _onPageMessage?: (message: string | Buffer) => any; + private _onPageClose?: (code: number | undefined, reason: string | undefined) => any; + private _onServerMessage?: (message: string | Buffer) => any; + private _onServerClose?: (code: number | undefined, reason: string | undefined) => any; + private _server: api.WebSocketRoute; + private _connected = false; + + constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.WebSocketRouteInitializer) { + super(parent, type, guid, initializer); + + this._server = { + onMessage: (handler: (message: string | Buffer) => any) => { + this._onServerMessage = handler; + }, + + onClose: (handler: (code: number | undefined, reason: string | undefined) => any) => { + this._onServerClose = handler; + }, + + connectToServer: () => { + throw new Error(`connectToServer must be called on the page-side WebSocketRoute`); + }, + + url: () => { + return this._initializer.url; + }, + + close: async (options: { code?: number, reason?: string } = {}) => { + await this._channel.closeServer({ ...options, wasClean: true }).catch(() => {}); + }, + + send: (message: string | Buffer) => { + if (isString(message)) + this._channel.sendToServer({ message, isBase64: false }).catch(() => {}); + else + this._channel.sendToServer({ message: message.toString('base64'), isBase64: true }).catch(() => {}); + }, + + async [Symbol.asyncDispose]() { + await this.close(); + }, + }; + + this._channel.on('messageFromPage', ({ message, isBase64 }) => { + if (this._onPageMessage) + this._onPageMessage(isBase64 ? Buffer.from(message, 'base64') : message); + else if (this._connected) + this._channel.sendToServer({ message, isBase64 }).catch(() => {}); + }); + + this._channel.on('messageFromServer', ({ message, isBase64 }) => { + if (this._onServerMessage) + this._onServerMessage(isBase64 ? Buffer.from(message, 'base64') : message); + else + this._channel.sendToPage({ message, isBase64 }).catch(() => {}); + }); + + this._channel.on('closePage', ({ code, reason, wasClean }) => { + if (this._onPageClose) + this._onPageClose(code, reason); + else + this._channel.closeServer({ code, reason, wasClean }).catch(() => {}); + }); + + this._channel.on('closeServer', ({ code, reason, wasClean }) => { + if (this._onServerClose) + this._onServerClose(code, reason); + else + this._channel.closePage({ code, reason, wasClean }).catch(() => {}); + }); + } + + url() { + return this._initializer.url; + } + + async close(options: { code?: number, reason?: string } = {}) { + await this._channel.closePage({ ...options, wasClean: true }).catch(() => {}); + } + + connectToServer() { + if (this._connected) + throw new Error('Already connected to the server'); + this._connected = true; + this._channel.connect().catch(() => {}); + return this._server; + } + + send(message: string | Buffer) { + if (isString(message)) + this._channel.sendToPage({ message, isBase64: false }).catch(() => {}); + else + this._channel.sendToPage({ message: message.toString('base64'), isBase64: true }).catch(() => {}); + } + + onMessage(handler: (message: string | Buffer) => any) { + this._onPageMessage = handler; + } + + onClose(handler: (code: number | undefined, reason: string | undefined) => any) { + this._onPageClose = handler; + } + + async [Symbol.asyncDispose]() { + await this.close(); + } + + async _afterHandle() { + if (this._connected) + return; + // Ensure that websocket is "open" and can send messages without an actual server connection. + await this._channel.ensureOpened(); + } +} + +export class WebSocketRouteHandler { + private readonly _baseURL: string | undefined; + readonly url: URLMatch; + readonly handler: WebSocketRouteHandlerCallback; + + constructor(baseURL: string | undefined, url: URLMatch, handler: WebSocketRouteHandlerCallback) { + this._baseURL = baseURL; + this.url = url; + this.handler = handler; + } + + static prepareInterceptionPatterns(handlers: WebSocketRouteHandler[]) { + const patterns: channels.BrowserContextSetWebSocketInterceptionPatternsParams['patterns'] = []; + let all = false; + for (const handler of handlers) { + if (isString(handler.url)) + patterns.push({ glob: handler.url }); + else if (isRegExp(handler.url)) + patterns.push({ regexSource: handler.url.source, regexFlags: handler.url.flags }); + else + all = true; + } + if (all) + return [{ glob: '**/*' }]; + return patterns; + } + + public matches(wsURL: string): boolean { + return urlMatches(this._baseURL, wsURL, this.url); + } + + public async handle(webSocketRoute: WebSocketRoute) { + const handler = this.handler; + await handler(webSocketRoute); + await webSocketRoute._afterHandle(); } } export type RouteHandlerCallback = (route: Route, request: Request) => Promise<any> | void; +export type WebSocketRouteHandlerCallback = (ws: WebSocketRoute) => Promise<any> | void; export type ResourceTiming = { startTime: number; diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 0bbe78f4c8..6654294edd 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -40,7 +40,7 @@ import { Keyboard, Mouse, Touchscreen } from './input'; import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle'; import type { FrameLocator, Locator, LocatorOptions } from './locator'; import type { ByRoleOptions } from '../utils/isomorphic/locatorUtils'; -import { type RouteHandlerCallback, type Request, Response, Route, RouteHandler, validateHeaders, WebSocket } from './network'; +import { type RouteHandlerCallback, type Request, Response, Route, RouteHandler, validateHeaders, WebSocket, type WebSocketRouteHandlerCallback, WebSocketRoute, WebSocketRouteHandler } from './network'; import type { FilePayload, Headers, LifecycleEvent, SelectOption, SelectOptionOptions, Size, WaitForEventOptions, WaitForFunctionOptions } from './types'; import { Video } from './video'; import { Waiter } from './waiter'; @@ -78,6 +78,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page readonly _closedOrCrashedScope = new LongStandingScope(); private _viewportSize: Size | null; _routes: RouteHandler[] = []; + _webSocketRoutes: WebSocketRouteHandler[] = []; readonly accessibility: Accessibility; readonly coverage: Coverage; @@ -137,6 +138,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page this._channel.on('frameDetached', ({ frame }) => this._onFrameDetached(Frame.from(frame))); this._channel.on('locatorHandlerTriggered', ({ uid }) => this._onLocatorHandlerTriggered(uid)); this._channel.on('route', ({ route }) => this._onRoute(Route.from(route))); + this._channel.on('webSocketRoute', ({ webSocketRoute }) => this._onWebSocketRoute(WebSocketRoute.from(webSocketRoute))); this._channel.on('video', ({ artifact }) => { const artifactObject = Artifact.from(artifact); this._forceVideo()._artifactReady(artifactObject); @@ -200,6 +202,14 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page await this._browserContext._onRoute(route); } + private async _onWebSocketRoute(webSocketRoute: WebSocketRoute) { + const routeHandler = this._webSocketRoutes.find(route => route.matches(webSocketRoute.url())); + if (routeHandler) + await routeHandler.handle(webSocketRoute); + else + await this._browserContext._onWebSocketRoute(webSocketRoute); + } + async _onBinding(bindingCall: BindingCall) { const func = this._bindings.get(bindingCall._initializer.name); if (func) { @@ -468,8 +478,8 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page return Response.fromNullable((await this._channel.goForward({ ...options, waitUntil })).response); } - async forceGarbageCollection() { - await this._channel.forceGarbageCollection(); + async requestGC() { + await this._channel.requestGC(); } async emulateMedia(options: { media?: 'screen' | 'print' | null, colorScheme?: 'dark' | 'light' | 'no-preference' | null, reducedMotion?: 'reduce' | 'no-preference' | null, forcedColors?: 'active' | 'none' | null } = {}) { @@ -515,6 +525,11 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page await harRouter.addPageRoute(this); } + async routeWebSocket(url: URLMatch, handler: WebSocketRouteHandlerCallback): Promise<void> { + this._webSocketRoutes.unshift(new WebSocketRouteHandler(this._browserContext._options.baseURL, url, handler)); + await this._updateWebSocketInterceptionPatterns(); + } + private _disposeHarRouters() { this._harRouters.forEach(router => router.dispose()); this._harRouters = []; @@ -551,6 +566,11 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page await this._channel.setNetworkInterceptionPatterns({ patterns }); } + private async _updateWebSocketInterceptionPatterns() { + const patterns = WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes); + await this._channel.setWebSocketInterceptionPatterns({ patterns }); + } + async screenshot(options: Omit<channels.PageScreenshotOptions, 'mask'> & { path?: string, mask?: Locator[] } = {}): Promise<Buffer> { const copy: channels.PageScreenshotOptions = { ...options, mask: undefined }; if (!copy.type) diff --git a/packages/playwright-core/src/client/tracing.ts b/packages/playwright-core/src/client/tracing.ts index 7330cd9f26..b5c411cc65 100644 --- a/packages/playwright-core/src/client/tracing.ts +++ b/packages/playwright-core/src/client/tracing.ts @@ -31,20 +31,18 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.TracingInitializer) { super(parent, type, guid, initializer); + this.markAsInternalType(); } async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean, _live?: boolean } = {}) { this._includeSources = !!options.sources; - const traceName = await this._wrapApiCall(async () => { - await this._channel.tracingStart({ - name: options.name, - snapshots: options.snapshots, - screenshots: options.screenshots, - live: options._live, - }); - const response = await this._channel.tracingStartChunk({ name: options.name, title: options.title }); - return response.traceName; - }, true); + await this._channel.tracingStart({ + name: options.name, + snapshots: options.snapshots, + screenshots: options.screenshots, + live: options._live, + }); + const { traceName } = await this._channel.tracingStartChunk({ name: options.name, title: options.title }); await this._startCollectingStacks(traceName); } @@ -63,16 +61,12 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap } async stopChunk(options: { path?: string } = {}) { - await this._wrapApiCall(async () => { - await this._doStopChunk(options.path); - }, true); + await this._doStopChunk(options.path); } async stop(options: { path?: string } = {}) { - await this._wrapApiCall(async () => { - await this._doStopChunk(options.path); - await this._channel.tracingStop(); - }, true); + await this._doStopChunk(options.path); + await this._channel.tracingStop(); } private async _doStopChunk(filePath: string | undefined) { diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index abea7f8fce..d5d91b165d 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -832,6 +832,9 @@ scheme.BrowserContextPageErrorEvent = tObject({ scheme.BrowserContextRouteEvent = tObject({ route: tChannel(['Route']), }); +scheme.BrowserContextWebSocketRouteEvent = tObject({ + webSocketRoute: tChannel(['WebSocketRoute']), +}); scheme.BrowserContextVideoEvent = tObject({ artifact: tChannel(['Artifact']), }); @@ -943,6 +946,14 @@ scheme.BrowserContextSetNetworkInterceptionPatternsParams = tObject({ })), }); scheme.BrowserContextSetNetworkInterceptionPatternsResult = tOptional(tObject({})); +scheme.BrowserContextSetWebSocketInterceptionPatternsParams = tObject({ + patterns: tArray(tObject({ + glob: tOptional(tString), + regexSource: tOptional(tString), + regexFlags: tOptional(tString), + })), +}); +scheme.BrowserContextSetWebSocketInterceptionPatternsResult = tOptional(tObject({})); scheme.BrowserContextSetOfflineParams = tObject({ offline: tBoolean, }); @@ -954,9 +965,10 @@ scheme.BrowserContextStorageStateResult = tObject({ }); scheme.BrowserContextPauseParams = tOptional(tObject({})); scheme.BrowserContextPauseResult = tOptional(tObject({})); -scheme.BrowserContextRecorderSupplementEnableParams = tObject({ +scheme.BrowserContextEnableRecorderParams = tObject({ language: tOptional(tString), mode: tOptional(tEnum(['inspecting', 'recording'])), + codegenMode: tOptional(tEnum(['actions', 'trace-events'])), pauseOnNextStatement: tOptional(tBoolean), testIdAttributeName: tOptional(tString), launchOptions: tOptional(tAny), @@ -966,7 +978,7 @@ scheme.BrowserContextRecorderSupplementEnableParams = tObject({ outputFile: tOptional(tString), omitCallTracking: tOptional(tBoolean), }); -scheme.BrowserContextRecorderSupplementEnableResult = tOptional(tObject({})); +scheme.BrowserContextEnableRecorderResult = tOptional(tObject({})); scheme.BrowserContextNewCDPSessionParams = tObject({ page: tOptional(tChannel(['Page'])), frame: tOptional(tChannel(['Frame'])), @@ -1070,6 +1082,9 @@ scheme.PageLocatorHandlerTriggeredEvent = tObject({ scheme.PageRouteEvent = tObject({ route: tChannel(['Route']), }); +scheme.PageWebSocketRouteEvent = tObject({ + webSocketRoute: tChannel(['WebSocketRoute']), +}); scheme.PageVideoEvent = tObject({ artifact: tChannel(['Artifact']), }); @@ -1122,8 +1137,8 @@ scheme.PageGoForwardParams = tObject({ scheme.PageGoForwardResult = tObject({ response: tOptional(tChannel(['Response'])), }); -scheme.PageForceGarbageCollectionParams = tOptional(tObject({})); -scheme.PageForceGarbageCollectionResult = tOptional(tObject({})); +scheme.PageRequestGCParams = tOptional(tObject({})); +scheme.PageRequestGCResult = tOptional(tObject({})); scheme.PageRegisterLocatorHandlerParams = tObject({ selector: tString, noWaitAfter: tOptional(tBoolean), @@ -1211,6 +1226,14 @@ scheme.PageSetNetworkInterceptionPatternsParams = tObject({ })), }); scheme.PageSetNetworkInterceptionPatternsResult = tOptional(tObject({})); +scheme.PageSetWebSocketInterceptionPatternsParams = tObject({ + patterns: tArray(tObject({ + glob: tOptional(tString), + regexSource: tOptional(tString), + regexFlags: tOptional(tString), + })), +}); +scheme.PageSetWebSocketInterceptionPatternsResult = tOptional(tObject({})); scheme.PageSetViewportSizeParams = tObject({ viewportSize: tObject({ width: tNumber, @@ -1820,12 +1843,6 @@ scheme.JSHandleJsonValueResult = tObject({ value: tType('SerializedValue'), }); scheme.ElementHandleJsonValueResult = tType('JSHandleJsonValueResult'); -scheme.JSHandleObjectCountParams = tOptional(tObject({})); -scheme.ElementHandleObjectCountParams = tType('JSHandleObjectCountParams'); -scheme.JSHandleObjectCountResult = tObject({ - count: tNumber, -}); -scheme.ElementHandleObjectCountResult = tType('JSHandleObjectCountResult'); scheme.ElementHandleInitializer = tObject({ preview: tString, }); @@ -2093,7 +2110,6 @@ scheme.RouteRedirectNavigationRequestParams = tObject({ scheme.RouteRedirectNavigationRequestResult = tOptional(tObject({})); scheme.RouteAbortParams = tObject({ errorCode: tOptional(tString), - requestUrl: tString, }); scheme.RouteAbortResult = tOptional(tObject({})); scheme.RouteContinueParams = tObject({ @@ -2101,7 +2117,6 @@ scheme.RouteContinueParams = tObject({ method: tOptional(tString), headers: tOptional(tArray(tType('NameValue'))), postData: tOptional(tBinary), - requestUrl: tString, isFallback: tBoolean, }); scheme.RouteContinueResult = tOptional(tObject({})); @@ -2111,9 +2126,55 @@ scheme.RouteFulfillParams = tObject({ body: tOptional(tString), isBase64: tOptional(tBoolean), fetchResponseUid: tOptional(tString), - requestUrl: tString, }); scheme.RouteFulfillResult = tOptional(tObject({})); +scheme.WebSocketRouteInitializer = tObject({ + url: tString, +}); +scheme.WebSocketRouteMessageFromPageEvent = tObject({ + message: tString, + isBase64: tBoolean, +}); +scheme.WebSocketRouteMessageFromServerEvent = tObject({ + message: tString, + isBase64: tBoolean, +}); +scheme.WebSocketRouteClosePageEvent = tObject({ + code: tOptional(tNumber), + reason: tOptional(tString), + wasClean: tBoolean, +}); +scheme.WebSocketRouteCloseServerEvent = tObject({ + code: tOptional(tNumber), + reason: tOptional(tString), + wasClean: tBoolean, +}); +scheme.WebSocketRouteConnectParams = tOptional(tObject({})); +scheme.WebSocketRouteConnectResult = tOptional(tObject({})); +scheme.WebSocketRouteEnsureOpenedParams = tOptional(tObject({})); +scheme.WebSocketRouteEnsureOpenedResult = tOptional(tObject({})); +scheme.WebSocketRouteSendToPageParams = tObject({ + message: tString, + isBase64: tBoolean, +}); +scheme.WebSocketRouteSendToPageResult = tOptional(tObject({})); +scheme.WebSocketRouteSendToServerParams = tObject({ + message: tString, + isBase64: tBoolean, +}); +scheme.WebSocketRouteSendToServerResult = tOptional(tObject({})); +scheme.WebSocketRouteClosePageParams = tObject({ + code: tOptional(tNumber), + reason: tOptional(tString), + wasClean: tBoolean, +}); +scheme.WebSocketRouteClosePageResult = tOptional(tObject({})); +scheme.WebSocketRouteCloseServerParams = tObject({ + code: tOptional(tNumber), + reason: tOptional(tString), + wasClean: tBoolean, +}); +scheme.WebSocketRouteCloseServerResult = tOptional(tObject({})); scheme.ResourceTiming = tObject({ startTime: tNumber, domainLookupStart: tNumber, diff --git a/packages/playwright-core/src/server/bidi/bidiConnection.ts b/packages/playwright-core/src/server/bidi/bidiConnection.ts index 7138f2e06a..f348815940 100644 --- a/packages/playwright-core/src/server/bidi/bidiConnection.ts +++ b/packages/playwright-core/src/server/bidi/bidiConnection.ts @@ -72,7 +72,7 @@ export class BidiConnection { let context; if ('context' in object.params) context = object.params.context; - else if (object.method === 'log.entryAdded') + else if (object.method === 'log.entryAdded' || object.method === 'script.message') context = object.params.source?.context; if (context) { const session = this._browsingContextToSession.get(context); diff --git a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts index eaacb629e6..f53b160ccf 100644 --- a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts +++ b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts @@ -23,7 +23,7 @@ import { BidiSerializer } from './third_party/bidiSerializer'; export class BidiExecutionContext implements js.ExecutionContextDelegate { private readonly _session: BidiSession; - private readonly _target: bidi.Script.Target; + readonly _target: bidi.Script.Target; constructor(session: BidiSession, realmInfo: bidi.Script.RealmInfo) { this._session = session; @@ -77,10 +77,6 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate { throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response)); } - rawCallFunctionNoReply(func: Function, ...args: any[]) { - throw new Error('Method not implemented.'); - } - async evaluateWithArguments(functionDeclaration: string, returnByValue: boolean, utilityScript: js.JSHandle<any>, values: any[], objectIds: string[]): Promise<any> { const response = await this._session.send('script.callFunction', { functionDeclaration, @@ -122,10 +118,6 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate { }); } - objectCount(objectId: js.ObjectId): Promise<number> { - throw new Error('Method not implemented.'); - } - async rawCallFunction(functionDeclaration: string, arg: bidi.Script.LocalValue): Promise<bidi.Script.RemoteValue> { const response = await this._session.send('script.callFunction', { functionDeclaration, diff --git a/packages/playwright-core/src/server/bidi/bidiFirefox.ts b/packages/playwright-core/src/server/bidi/bidiFirefox.ts index 3fb7c15b90..204cabdef7 100644 --- a/packages/playwright-core/src/server/bidi/bidiFirefox.ts +++ b/packages/playwright-core/src/server/bidi/bidiFirefox.ts @@ -26,6 +26,7 @@ import type { ConnectionTransport } from '../transport'; import type * as types from '../types'; import { BidiBrowser } from './bidiBrowser'; import { kBrowserCloseMessageId } from './bidiConnection'; +import { createProfile } from './third_party/firefoxPrefs'; export class BidiFirefox extends BrowserType { constructor(parent: SdkObject) { @@ -51,6 +52,14 @@ export class BidiFirefox extends BrowserType { override amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env { if (!path.isAbsolute(os.homedir())) throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${os.platform() === 'win32' ? 'USERPROFILE' : 'HOME'} to a relative path?`); + + env = { + ...env, + 'MOZ_CRASHREPORTER': '1', + 'MOZ_CRASHREPORTER_NO_REPORT': '1', + 'MOZ_CRASHREPORTER_SHUTDOWN': '1', + }; + if (os.platform() === 'linux') { // Always remove SNAP_NAME and SNAP_INSTANCE_NAME env variables since they // confuse Firefox: in our case, builds never come from SNAP. @@ -64,6 +73,13 @@ export class BidiFirefox extends BrowserType { transport.send({ method: 'browser.close', params: {}, id: kBrowserCloseMessageId }); } + override async prepareUserDataDir(options: types.LaunchOptions, userDataDir: string): Promise<void> { + await createProfile({ + path: userDataDir, + preferences: options.firefoxUserPrefs || {}, + }); + } + override defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] { const { args = [], headless } = options; const userDataDirArg = args.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile')); diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index c2d499bd67..56bb43cb1a 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -21,7 +21,9 @@ import type * as accessibility from '../accessibility'; import * as dom from '../dom'; import * as dialog from '../dialog'; import type * as frames from '../frames'; -import { type InitScript, Page, type PageDelegate } from '../page'; +import { Page } from '../page'; +import type * as channels from '@protocol/channels'; +import type { InitScript, PageDelegate } from '../page'; import type { Progress } from '../progress'; import type * as types from '../types'; import type { BidiBrowserContext } from './bidiBrowser'; @@ -31,8 +33,10 @@ import * as bidi from './third_party/bidiProtocol'; import { BidiExecutionContext } from './bidiExecutionContext'; import { BidiNetworkManager } from './bidiNetworkManager'; import { BrowserContext } from '../browserContext'; +import { BidiPDF } from './bidiPdf'; const UTILITY_WORLD_NAME = '__playwright_utility_world__'; +const kPlaywrightBindingChannel = 'playwrightChannel'; export class BidiPage implements PageDelegate { readonly rawMouse: RawMouseImpl; @@ -46,6 +50,7 @@ export class BidiPage implements PageDelegate { private _sessionListeners: RegisteredListener[] = []; readonly _browserContext: BidiBrowserContext; readonly _networkManager: BidiNetworkManager; + private readonly _pdf: BidiPDF; _initializedPage: Page | null = null; private _initScriptIds: string[] = []; @@ -59,9 +64,11 @@ export class BidiPage implements PageDelegate { this._page = new Page(this, browserContext); this._browserContext = browserContext; this._networkManager = new BidiNetworkManager(this._session, this._page, this._onNavigationResponseStarted.bind(this)); + this._pdf = new BidiPDF(this._session); this._page.on(Page.Events.FrameDetached, (frame: frames.Frame) => this._removeContextsForFrame(frame, false)); this._sessionListeners = [ eventsHelper.addEventListener(bidiSession, 'script.realmCreated', this._onRealmCreated.bind(this)), + 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.navigationAborted', this._onNavigationAborted.bind(this)), @@ -93,6 +100,7 @@ export class BidiPage implements PageDelegate { this.updateHttpCredentials(), this.updateRequestInterception(), this._updateViewport(), + this._installMainBinding(), this._addAllInitScripts(), ]); } @@ -275,6 +283,9 @@ export class BidiPage implements PageDelegate { } async bringToFront(): Promise<void> { + await this._session.send('browsingContext.activate', { + context: this._session.sessionId, + }); } private async _updateViewport(): Promise<void> { @@ -315,16 +326,61 @@ export class BidiPage implements PageDelegate { }); } - goBack(): Promise<boolean> { + async goBack(): Promise<boolean> { + return await this._session.send('browsingContext.traverseHistory', { + context: this._session.sessionId, + delta: -1, + }).then(() => true).catch(() => false); + } + + async goForward(): Promise<boolean> { + return await this._session.send('browsingContext.traverseHistory', { + context: this._session.sessionId, + delta: +1, + }).then(() => true).catch(() => false); + } + + async requestGC(): Promise<void> { throw new Error('Method not implemented.'); } - goForward(): Promise<boolean> { - throw new Error('Method not implemented.'); + // TODO: consider calling this only when bindings are added. + private async _installMainBinding() { + const functionDeclaration = addMainBinding.toString(); + const args: bidi.Script.ChannelValue[] = [{ + type: 'channel', + value: { + channel: kPlaywrightBindingChannel, + ownership: bidi.Script.ResultOwnership.Root, + } + }]; + const promises = []; + promises.push(this._session.send('script.addPreloadScript', { + functionDeclaration, + arguments: args, + })); + promises.push(this._session.send('script.callFunction', { + functionDeclaration, + arguments: args, + target: toBidiExecutionContext(await this._page.mainFrame()._mainContext())._target, + awaitPromise: false, + userActivation: false, + })); + await Promise.all(promises); } - async forceGarbageCollection(): Promise<void> { - throw new Error('Method not implemented.'); + private async _onScriptMessage(event: bidi.Script.MessageParameters) { + if (event.channel !== kPlaywrightBindingChannel) + return; + const pageOrError = await this.pageOrError(); + if (pageOrError instanceof Error) + return; + const context = this._realmToContext.get(event.source.realm); + if (!context) + return; + if (event.data.type !== 'string') + return; + await this._page._onBindingCalled(event.data.value, context); } async addInitScript(initScript: InitScript): Promise<void> { @@ -355,7 +411,20 @@ export class BidiPage implements PageDelegate { } async takeScreenshot(progress: Progress, format: string, documentRect: types.Rect | undefined, viewportRect: types.Rect | undefined, quality: number | undefined, fitsViewport: boolean, scale: 'css' | 'device'): Promise<Buffer> { - throw new Error('Method not implemented.'); + const rect = (documentRect || viewportRect)!; + const { data } = await this._session.send('browsingContext.captureScreenshot', { + context: this._session.sessionId, + format: { + type: `image/${format === 'png' ? 'png' : 'jpeg'}`, + quality: quality || 80, + }, + origin: documentRect ? 'document' : 'viewport', + clip: { + type: 'box', + ...rect, + } + }); + return Buffer.from(data, 'base64'); } async getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> { @@ -493,6 +562,10 @@ export class BidiPage implements PageDelegate { async resetForReuse(): Promise<void> { } + async pdf(options: channels.PagePdfParams): Promise<Buffer> { + return this._pdf.generate(options); + } + async getFrameElement(frame: frames.Frame): Promise<dom.ElementHandle> { const parent = frame.parentFrame(); if (!parent) @@ -522,6 +595,10 @@ export class BidiPage implements PageDelegate { } } +function addMainBinding(callback: (arg: any) => void) { + (globalThis as any)['__playwright__binding__'] = callback; +} + function toBidiExecutionContext(executionContext: dom.FrameExecutionContext): BidiExecutionContext { return (executionContext as any)[contextDelegateSymbol] as BidiExecutionContext; } diff --git a/packages/playwright-core/src/server/bidi/bidiPdf.ts b/packages/playwright-core/src/server/bidi/bidiPdf.ts new file mode 100644 index 0000000000..89fefb5260 --- /dev/null +++ b/packages/playwright-core/src/server/bidi/bidiPdf.ts @@ -0,0 +1,109 @@ +/** + * Copyright 2017 Google Inc. All rights reserved. + * Modifications 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. + */ + +import { assert } from '../../utils'; +import type * as channels from '@protocol/channels'; +import type { BidiSession } from './bidiConnection'; + +const PagePaperFormats: { [key: string]: { width: number, height: number }} = { + letter: { width: 8.5, height: 11 }, + legal: { width: 8.5, height: 14 }, + tabloid: { width: 11, height: 17 }, + ledger: { width: 17, height: 11 }, + a0: { width: 33.1, height: 46.8 }, + a1: { width: 23.4, height: 33.1 }, + a2: { width: 16.54, height: 23.4 }, + a3: { width: 11.7, height: 16.54 }, + a4: { width: 8.27, height: 11.7 }, + a5: { width: 5.83, height: 8.27 }, + a6: { width: 4.13, height: 5.83 }, +}; + +const unitToPixels: { [key: string]: number } = { + 'px': 1, + 'in': 96, + 'cm': 37.8, + 'mm': 3.78 +}; + +function convertPrintParameterToInches(text: string | undefined): number | undefined { + if (text === undefined) + return undefined; + let unit = text.substring(text.length - 2).toLowerCase(); + let valueText = ''; + if (unitToPixels.hasOwnProperty(unit)) { + valueText = text.substring(0, text.length - 2); + } else { + // In case of unknown unit try to parse the whole parameter as number of pixels. + // This is consistent with phantom's paperSize behavior. + unit = 'px'; + valueText = text; + } + const value = Number(valueText); + assert(!isNaN(value), 'Failed to parse parameter value: ' + text); + const pixels = value * unitToPixels[unit]; + return pixels / 96; +} + +export class BidiPDF { + private _session: BidiSession; + + constructor(session: BidiSession) { + this._session = session; + } + + async generate(options: channels.PagePdfParams): Promise<Buffer> { + const { + scale = 1, + printBackground = false, + landscape = false, + pageRanges = '', + margin = {}, + } = options; + + let paperWidth = 8.5; + let paperHeight = 11; + if (options.format) { + const format = PagePaperFormats[options.format.toLowerCase()]; + assert(format, 'Unknown paper format: ' + options.format); + paperWidth = format.width; + paperHeight = format.height; + } else { + paperWidth = convertPrintParameterToInches(options.width) || paperWidth; + paperHeight = convertPrintParameterToInches(options.height) || paperHeight; + } + + const { data } = await this._session.send('browsingContext.print', { + context: this._session.sessionId, + background: printBackground, + margin: { + bottom: convertPrintParameterToInches(margin.bottom) || 0, + left: convertPrintParameterToInches(margin.left) || 0, + right: convertPrintParameterToInches(margin.right) || 0, + top: convertPrintParameterToInches(margin.top) || 0 + }, + orientation: landscape ? 'landscape' : 'portrait', + page: { + width: paperWidth, + height: paperHeight + }, + pageRanges: pageRanges ? pageRanges.split(',').map(r => r.trim()) : undefined, + scale, + }); + return Buffer.from(data, 'base64'); + } +} diff --git a/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts b/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts new file mode 100644 index 0000000000..7c08bebe6d --- /dev/null +++ b/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts @@ -0,0 +1,282 @@ +/** + * @license + * Copyright 2023 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import fs from 'fs'; +import path from 'path'; + +/* eslint-disable curly, indent */ + +interface ProfileOptions { + preferences: Record<string, unknown>; + path: string; +} + +export async function createProfile(options: ProfileOptions): Promise<void> { + if (!fs.existsSync(options.path)) { + await fs.promises.mkdir(options.path, { + recursive: true, + }); + } + await writePreferences({ + preferences: { + ...defaultProfilePreferences(options.preferences), + ...options.preferences, + }, + path: options.path, + }); +} + +function defaultProfilePreferences( + extraPrefs: Record<string, unknown> +): Record<string, unknown> { + const server = 'dummy.test'; + + const defaultPrefs = { + // Make sure Shield doesn't hit the network. + 'app.normandy.api_url': '', + // Disable Firefox old build background check + 'app.update.checkInstallTime': false, + // Disable automatically upgrading Firefox + 'app.update.disabledForTesting': true, + + // Increase the APZ content response timeout to 1 minute + 'apz.content_response_timeout': 60000, + + // Prevent various error message on the console + // jest-puppeteer asserts that no error message is emitted by the console + 'browser.contentblocking.features.standard': + '-tp,tpPrivate,cookieBehavior0,-cm,-fp', + + // Enable the dump function: which sends messages to the system + // console + // https://bugzilla.mozilla.org/show_bug.cgi?id=1543115 + 'browser.dom.window.dump.enabled': true, + // Disable topstories + 'browser.newtabpage.activity-stream.feeds.system.topstories': false, + // Always display a blank page + 'browser.newtabpage.enabled': false, + // Background thumbnails in particular cause grief: and disabling + // thumbnails in general cannot hurt + 'browser.pagethumbnails.capturing_disabled': true, + + // Disable safebrowsing components. + 'browser.safebrowsing.blockedURIs.enabled': false, + 'browser.safebrowsing.downloads.enabled': false, + 'browser.safebrowsing.malware.enabled': false, + 'browser.safebrowsing.phishing.enabled': false, + + // Disable updates to search engines. + 'browser.search.update': false, + // Do not restore the last open set of tabs if the browser has crashed + 'browser.sessionstore.resume_from_crash': false, + // Skip check for default browser on startup + 'browser.shell.checkDefaultBrowser': false, + + // Disable newtabpage + 'browser.startup.homepage': 'about:blank', + // Do not redirect user when a milstone upgrade of Firefox is detected + 'browser.startup.homepage_override.mstone': 'ignore', + // Start with a blank page about:blank + 'browser.startup.page': 0, + + // Do not allow background tabs to be zombified on Android: otherwise for + // tests that open additional tabs: the test harness tab itself might get + // unloaded + 'browser.tabs.disableBackgroundZombification': false, + // Do not warn when closing all other open tabs + 'browser.tabs.warnOnCloseOtherTabs': false, + // Do not warn when multiple tabs will be opened + 'browser.tabs.warnOnOpen': false, + + // Do not automatically offer translations, as tests do not expect this. + 'browser.translations.automaticallyPopup': false, + + // Disable the UI tour. + 'browser.uitour.enabled': false, + // Turn off search suggestions in the location bar so as not to trigger + // network connections. + 'browser.urlbar.suggest.searches': false, + // Disable first run splash page on Windows 10 + 'browser.usedOnWindows10.introURL': '', + // Do not warn on quitting Firefox + 'browser.warnOnQuit': false, + + // Defensively disable data reporting systems + 'datareporting.healthreport.documentServerURI': `http://${server}/dummy/healthreport/`, + 'datareporting.healthreport.logging.consoleEnabled': false, + 'datareporting.healthreport.service.enabled': false, + 'datareporting.healthreport.service.firstRun': false, + 'datareporting.healthreport.uploadEnabled': false, + + // Do not show datareporting policy notifications which can interfere with tests + 'datareporting.policy.dataSubmissionEnabled': false, + 'datareporting.policy.dataSubmissionPolicyBypassNotification': true, + + // DevTools JSONViewer sometimes fails to load dependencies with its require.js. + // This doesn't affect Puppeteer but spams console (Bug 1424372) + 'devtools.jsonview.enabled': false, + + // Disable popup-blocker + 'dom.disable_open_during_load': false, + + // Enable the support for File object creation in the content process + // Required for |Page.setFileInputFiles| protocol method. + 'dom.file.createInChild': true, + + // Disable the ProcessHangMonitor + 'dom.ipc.reportProcessHangs': false, + + // Disable slow script dialogues + 'dom.max_chrome_script_run_time': 0, + 'dom.max_script_run_time': 0, + + // Only load extensions from the application and user profile + // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION + 'extensions.autoDisableScopes': 0, + 'extensions.enabledScopes': 5, + + // Disable metadata caching for installed add-ons by default + 'extensions.getAddons.cache.enabled': false, + + // Disable installing any distribution extensions or add-ons. + 'extensions.installDistroAddons': false, + + // Disabled screenshots extension + 'extensions.screenshots.disabled': true, + + // Turn off extension updates so they do not bother tests + 'extensions.update.enabled': false, + + // Turn off extension updates so they do not bother tests + 'extensions.update.notifyUser': false, + + // Make sure opening about:addons will not hit the network + 'extensions.webservice.discoverURL': `http://${server}/dummy/discoveryURL`, + + // Allow the application to have focus even it runs in the background + 'focusmanager.testmode': true, + + // Disable useragent updates + 'general.useragent.updates.enabled': false, + + // Always use network provider for geolocation tests so we bypass the + // macOS dialog raised by the corelocation provider + 'geo.provider.testing': true, + + // Do not scan Wifi + 'geo.wifi.scan': false, + + // No hang monitor + 'hangmonitor.timeout': 0, + + // Show chrome errors and warnings in the error console + 'javascript.options.showInConsole': true, + + // Disable download and usage of OpenH264: and Widevine plugins + 'media.gmp-manager.updateEnabled': false, + + // Disable the GFX sanity window + 'media.sanity-test.disabled': true, + + // Disable experimental feature that is only available in Nightly + 'network.cookie.sameSite.laxByDefault': false, + + // Do not prompt for temporary redirects + 'network.http.prompt-temp-redirect': false, + + // Disable speculative connections so they are not reported as leaking + // when they are hanging around + 'network.http.speculative-parallel-limit': 0, + + // Do not automatically switch between offline and online + 'network.manage-offline-status': false, + + // Make sure SNTP requests do not hit the network + 'network.sntp.pools': server, + + // Disable Flash. + 'plugin.state.flash': 0, + + 'privacy.trackingprotection.enabled': false, + + // Can be removed once Firefox 89 is no longer supported + // https://bugzilla.mozilla.org/show_bug.cgi?id=1710839 + 'remote.enabled': true, + + // Don't do network connections for mitm priming + 'security.certerrors.mitm.priming.enabled': false, + + // Local documents have access to all other local documents, + // including directory listings + 'security.fileuri.strict_origin_policy': false, + + // Do not wait for the notification button security delay + 'security.notification_enable_delay': 0, + + // Ensure blocklist updates do not hit the network + 'services.settings.server': `http://${server}/dummy/blocklist/`, + + // Do not automatically fill sign-in forms with known usernames and + // passwords + 'signon.autofillForms': false, + + // Disable password capture, so that tests that include forms are not + // influenced by the presence of the persistent doorhanger notification + 'signon.rememberSignons': false, + + // Disable first-run welcome page + 'startup.homepage_welcome_url': 'about:blank', + + // Disable first-run welcome page + 'startup.homepage_welcome_url.additional': '', + + // Disable browser animations (tabs, fullscreen, sliding alerts) + 'toolkit.cosmeticAnimations.enabled': false, + + // Prevent starting into safe mode after application crashes + 'toolkit.startup.max_resumed_crashes': -1, + }; + + return Object.assign(defaultPrefs, extraPrefs); +} + +/** + * Populates the user.js file with custom preferences as needed to allow + * Firefox's CDP support to properly function. These preferences will be + * automatically copied over to prefs.js during startup of Firefox. To be + * able to restore the original values of preferences a backup of prefs.js + * will be created. + * + * @param prefs - List of preferences to add. + * @param profilePath - Firefox profile to write the preferences to. + */ +async function writePreferences(options: ProfileOptions): Promise<void> { + const prefsPath = path.join(options.path, 'prefs.js'); + const lines = Object.entries(options.preferences).map(([key, value]) => { + return `user_pref(${JSON.stringify(key)}, ${JSON.stringify(value)});`; + }); + + // Use allSettled to prevent corruption + const result = await Promise.allSettled([ + fs.promises.writeFile(path.join(options.path, 'user.js'), lines.join('\n')), + // Create a backup of the preferences file if it already exitsts. + fs.promises.access(prefsPath, fs.constants.F_OK).then( + async () => { + await fs.promises.copyFile( + prefsPath, + path.join(options.path, 'prefs.js.playwright') + ); + }, + // Swallow only if file does not exist + () => {} + ), + ]); + for (const command of result) { + if (command.status === 'rejected') { + throw command.reason; + } + } +} diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index 499356ca49..025bd0f388 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -131,15 +131,15 @@ export abstract class BrowserContext extends SdkObject { // When PWDEBUG=1, show inspector for each context. if (debugMode() === 'inspector') - await Recorder.show(this, RecorderApp.factory(this), { pauseOnNextStatement: true }); + await Recorder.show('actions', this, RecorderApp.factory(this), { pauseOnNextStatement: true }); // When paused, show inspector. if (this._debugger.isPaused()) - Recorder.showInspector(this, RecorderApp.factory(this)); + Recorder.showInspectorNoReply(this, RecorderApp.factory(this)); this._debugger.on(Debugger.Events.PausedStateChanged, () => { if (this._debugger.isPaused()) - Recorder.showInspector(this, RecorderApp.factory(this)); + Recorder.showInspectorNoReply(this, RecorderApp.factory(this)); }); if (debugMode() === 'console') @@ -525,7 +525,7 @@ export abstract class BrowserContext extends SdkObject { const internalMetadata = serverSideCallMetadata(); const page = await this.newPage(internalMetadata); await page._setServerRequestInterceptor(handler => { - handler.fulfill({ body: '<html></html>', requestUrl: handler.request().url() }).catch(() => {}); + handler.fulfill({ body: '<html></html>' }).catch(() => {}); return true; }); for (const origin of originsToSave) { @@ -559,7 +559,7 @@ export abstract class BrowserContext extends SdkObject { isServerSide: false, }); await page._setServerRequestInterceptor(handler => { - handler.fulfill({ body: '<html></html>', requestUrl: handler.request().url() }).catch(() => {}); + handler.fulfill({ body: '<html></html>' }).catch(() => {}); return true; }); @@ -594,7 +594,7 @@ export abstract class BrowserContext extends SdkObject { const internalMetadata = serverSideCallMetadata(); const page = await this.newPage(internalMetadata); await page._setServerRequestInterceptor(handler => { - handler.fulfill({ body: '<html></html>', requestUrl: handler.request().url() }).catch(() => {}); + handler.fulfill({ body: '<html></html>' }).catch(() => {}); return true; }); for (const originState of state.origins) { diff --git a/packages/playwright-core/src/server/browserType.ts b/packages/playwright-core/src/server/browserType.ts index abc15a20f4..ec8524c247 100644 --- a/packages/playwright-core/src/server/browserType.ts +++ b/packages/playwright-core/src/server/browserType.ts @@ -192,6 +192,7 @@ export abstract class BrowserType extends SdkObject { userDataDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), `playwright_${this._name}dev_profile-`)); tempDirectories.push(userDataDir); } + await this.prepareUserDataDir(options, userDataDir); const browserArguments = []; if (ignoreAllDefaultArgs) @@ -328,6 +329,9 @@ export abstract class BrowserType extends SdkObject { return undefined; } + async prepareUserDataDir(options: types.LaunchOptions, userDataDir: string): Promise<void> { + } + abstract defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[]; abstract connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<Browser>; abstract amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env; diff --git a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts index 54b80a5d82..01df7a66ac 100644 --- a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts +++ b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts @@ -35,8 +35,9 @@ export const chromiumSwitches = [ // Translate - https://github.com/microsoft/playwright/issues/16126 // HttpsUpgrades - https://github.com/microsoft/playwright/pull/27605 // PaintHolding - https://github.com/microsoft/playwright/issues/28023 - // PlzDedicatedWorker - https://github.com/microsoft/playwright/issues/31747 - '--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,PlzDedicatedWorker', + // ThirdPartyStoragePartitioning - https://github.com/microsoft/playwright/issues/32230 + // LensOverlay - Hides the Lens feature in the URL address bar. Its not working in unofficial builds. + '--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay', '--allow-pre-commit-input', '--disable-hang-monitor', '--disable-ipc-flooding-protection', diff --git a/packages/playwright-core/src/server/chromium/crExecutionContext.ts b/packages/playwright-core/src/server/chromium/crExecutionContext.ts index c6952f5c82..1cd58de7af 100644 --- a/packages/playwright-core/src/server/chromium/crExecutionContext.ts +++ b/packages/playwright-core/src/server/chromium/crExecutionContext.ts @@ -53,16 +53,6 @@ export class CRExecutionContext implements js.ExecutionContextDelegate { return remoteObject.objectId!; } - rawCallFunctionNoReply(func: Function, ...args: any[]) { - this._client.send('Runtime.callFunctionOn', { - functionDeclaration: func.toString(), - arguments: args.map(a => a instanceof js.JSHandle ? { objectId: a._objectId } : { value: a }), - returnByValue: true, - executionContextId: this._contextId, - userGesture: true - }).catch(() => {}); - } - async evaluateWithArguments(expression: string, returnByValue: boolean, utilityScript: js.JSHandle<any>, values: any[], objectIds: string[]): Promise<any> { const { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.callFunctionOn', { functionDeclaration: expression, @@ -102,14 +92,6 @@ export class CRExecutionContext implements js.ExecutionContextDelegate { async releaseHandle(objectId: js.ObjectId): Promise<void> { await releaseObject(this._client, objectId); } - - async objectCount(objectId: js.ObjectId): Promise<number> { - const result = await this._client.send('Runtime.queryObjects', { - prototypeObjectId: objectId - }); - const match = result.objects.description!.match(/Array\((\d+)\)/)!; - return +match[1]; - } } function rewriteError(error: Error): Protocol.Runtime.evaluateReturnValue { diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index fbdc9db91a..57ebc5fa69 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -247,7 +247,7 @@ export class CRPage implements PageDelegate { return this._go(+1); } - async forceGarbageCollection(): Promise<void> { + async requestGC(): Promise<void> { await this._mainFrameSession._client.send('HeapProfiler.collectGarbage'); } @@ -364,6 +364,8 @@ export class CRPage implements PageDelegate { } async resetForReuse(): Promise<void> { + // See https://github.com/microsoft/playwright/issues/22432. + await this.rawMouse.move(-1, -1, 'none', new Set(), new Set(), true); } async pdf(options: channels.PagePdfParams): Promise<Buffer> { @@ -694,16 +696,15 @@ class FrameSession { if (!frame || this._eventBelongsToStaleFrame(frame._id)) return; const delegate = new CRExecutionContext(this._client, contextPayload); - let worldName: types.World; + let worldName: types.World|null = null; if (contextPayload.auxData && !!contextPayload.auxData.isDefault) worldName = 'main'; else if (contextPayload.name === UTILITY_WORLD_NAME) worldName = 'utility'; - else - return; const context = new dom.FrameExecutionContext(delegate, frame, worldName); (context as any)[contextDelegateSymbol] = delegate; - frame._contextCreated(worldName, context); + if (worldName) + frame._contextCreated(worldName, context); this._contextIdToContext.set(contextPayload.id, context); } diff --git a/packages/playwright-core/src/server/chromium/protocol.d.ts b/packages/playwright-core/src/server/chromium/protocol.d.ts index caadb2a577..14416bde1e 100644 --- a/packages/playwright-core/src/server/chromium/protocol.d.ts +++ b/packages/playwright-core/src/server/chromium/protocol.d.ts @@ -840,7 +840,7 @@ CORS RFC1918 enforcement. resourceIPAddressSpace?: Network.IPAddressSpace; clientSecurityState?: Network.ClientSecurityState; } - export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"UntrustworthyReportingOrigin"|"InsecureContext"|"InvalidHeader"|"InvalidRegisterTriggerHeader"|"SourceAndTriggerHeaders"|"SourceIgnored"|"TriggerIgnored"|"OsSourceIgnored"|"OsTriggerIgnored"|"InvalidRegisterOsSourceHeader"|"InvalidRegisterOsTriggerHeader"|"WebAndOsHeaders"|"NoWebOrOsSupport"|"NavigationRegistrationWithoutTransientUserActivation"|"InvalidInfoHeader"|"NoRegisterSourceHeader"|"NoRegisterTriggerHeader"|"NoRegisterOsSourceHeader"|"NoRegisterOsTriggerHeader"; + export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"UntrustworthyReportingOrigin"|"InsecureContext"|"InvalidHeader"|"InvalidRegisterTriggerHeader"|"SourceAndTriggerHeaders"|"SourceIgnored"|"TriggerIgnored"|"OsSourceIgnored"|"OsTriggerIgnored"|"InvalidRegisterOsSourceHeader"|"InvalidRegisterOsTriggerHeader"|"WebAndOsHeaders"|"NoWebOrOsSupport"|"NavigationRegistrationWithoutTransientUserActivation"|"InvalidInfoHeader"|"NoRegisterSourceHeader"|"NoRegisterTriggerHeader"|"NoRegisterOsSourceHeader"|"NoRegisterOsTriggerHeader"|"NavigationRegistrationUniqueScopeAlreadySet"; export type SharedDictionaryError = "UseErrorCrossOriginNoCorsRequest"|"UseErrorDictionaryLoadFailure"|"UseErrorMatchingDictionaryNotUsed"|"UseErrorUnexpectedContentDictionaryHeader"|"WriteErrorCossOriginNoCorsRequest"|"WriteErrorDisallowedBySettings"|"WriteErrorExpiredResponse"|"WriteErrorFeatureDisabled"|"WriteErrorInsufficientResources"|"WriteErrorInvalidMatchField"|"WriteErrorInvalidStructuredHeader"|"WriteErrorNavigationRequest"|"WriteErrorNoMatchField"|"WriteErrorNonListMatchDestField"|"WriteErrorNonSecureContext"|"WriteErrorNonStringIdField"|"WriteErrorNonStringInMatchDestList"|"WriteErrorNonStringMatchField"|"WriteErrorNonTokenTypeField"|"WriteErrorRequestAborted"|"WriteErrorShuttingDown"|"WriteErrorTooLongIdField"|"WriteErrorUnsupportedType"; /** * Details for issues around "Attribution Reporting API" usage. @@ -1534,7 +1534,7 @@ events afterwards if enabled and recording. */ windowState?: WindowState; } - export type PermissionType = "accessibilityEvents"|"audioCapture"|"backgroundSync"|"backgroundFetch"|"capturedSurfaceControl"|"clipboardReadWrite"|"clipboardSanitizedWrite"|"displayCapture"|"durableStorage"|"flash"|"geolocation"|"idleDetection"|"localFonts"|"midi"|"midiSysex"|"nfc"|"notifications"|"paymentHandler"|"periodicBackgroundSync"|"protectedMediaIdentifier"|"sensors"|"storageAccess"|"speakerSelection"|"topLevelStorageAccess"|"videoCapture"|"videoCapturePanTiltZoom"|"wakeLockScreen"|"wakeLockSystem"|"windowManagement"; + export type PermissionType = "accessibilityEvents"|"audioCapture"|"backgroundSync"|"backgroundFetch"|"capturedSurfaceControl"|"clipboardReadWrite"|"clipboardSanitizedWrite"|"displayCapture"|"durableStorage"|"flash"|"geolocation"|"idleDetection"|"localFonts"|"midi"|"midiSysex"|"nfc"|"notifications"|"paymentHandler"|"periodicBackgroundSync"|"protectedMediaIdentifier"|"sensors"|"storageAccess"|"speakerSelection"|"topLevelStorageAccess"|"videoCapture"|"videoCapturePanTiltZoom"|"wakeLockScreen"|"wakeLockSystem"|"webAppInstallation"|"windowManagement"; export type PermissionSetting = "granted"|"denied"|"prompt"; /** * Definition of PermissionDescriptor defined in the Permissions API: @@ -3561,7 +3561,7 @@ front-end. /** * Pseudo element type. */ - export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scroll-next-button"|"scroll-prev-button"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new"; + export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"column"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scroll-next-button"|"scroll-prev-button"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new"|"placeholder"|"file-selector-button"|"details-content"|"select-fallback-button"|"select-fallback-button-text"|"picker"; /** * Shadow root type. */ @@ -3710,6 +3710,7 @@ The property is always undefined now. isSVG?: boolean; compatibilityMode?: CompatibilityMode; assignedSlot?: BackendNode; + isScrollable?: boolean; } /** * A structure to hold the top-level node of a detached tree and an array of its retained descendants. @@ -3954,6 +3955,19 @@ The property is always undefined now. * Called when top layer elements are changed. */ export type topLayerElementsUpdatedPayload = void; + /** + * Fired when a node's scrollability state changes. + */ + export type scrollableFlagUpdatedPayload = { + /** + * The id of the node. + */ + nodeId: DOM.NodeId; + /** + * If the node is scrollable. + */ + isScrollable: boolean; + } /** * Called when a pseudo element is removed from an element. */ @@ -8102,8 +8116,25 @@ or hexadecimal (0x prefixed) string. */ size: number; } + /** + * DOM object counter data. + */ + export interface DOMCounter { + /** + * Object name. Note: object names should be presumed volatile and clients should not expect +the returned names to be consistent across runs. + */ + name: string; + /** + * Object count. + */ + count: number; + } + /** + * Retruns current DOM object counters. + */ export type getDOMCountersParameters = { } export type getDOMCountersReturnValue = { @@ -8111,6 +8142,21 @@ or hexadecimal (0x prefixed) string. nodes: number; jsEventListeners: number; } + /** + * Retruns DOM object counters after preparing renderer for leak detection. + */ + export type getDOMCountersForLeakDetectionParameters = { + } + export type getDOMCountersForLeakDetectionReturnValue = { + /** + * DOM object counters. + */ + counters: DOMCounter[]; + } + /** + * Prepares for leak detection by terminating workers, stopping spellcheckers, +dropping non-essential internal caches, running garbage collections, etc. + */ export type prepareForLeakDetectionParameters = { } export type prepareForLeakDetectionReturnValue = { @@ -8902,7 +8948,7 @@ This is a temporary ability and it will be removed in the future. /** * Types of reasons why a cookie should have been blocked by 3PCD but is exempted for the request. */ - export type CookieExemptionReason = "None"|"UserSetting"|"TPCDMetadata"|"TPCDDeprecationTrial"|"TPCDHeuristics"|"EnterprisePolicy"|"StorageAccess"|"TopLevelStorageAccess"|"CorsOptIn"|"Scheme"; + export type CookieExemptionReason = "None"|"UserSetting"|"TPCDMetadata"|"TPCDDeprecationTrial"|"TopLevelTPCDDeprecationTrial"|"TPCDHeuristics"|"EnterprisePolicy"|"StorageAccess"|"TopLevelStorageAccess"|"Scheme"; /** * A cookie which was not stored from a response with the corresponding reason. */ @@ -11452,7 +11498,7 @@ as an ad. * All Permissions Policy features. This enum should match the one defined in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5. */ - export type PermissionsPolicyFeature = "accelerometer"|"all-screens-capture"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"deferred-fetch"|"digital-credentials-get"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"media-playback-while-not-visible"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking"; + export type PermissionsPolicyFeature = "accelerometer"|"all-screens-capture"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"controlled-frame"|"cross-origin-isolated"|"deferred-fetch"|"digital-credentials-get"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"media-playback-while-not-visible"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"popins"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-app-installation"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking"; /** * Reason for a permissions policy feature to be disabled. */ @@ -12040,7 +12086,7 @@ https://github.com/WICG/manifest-incubations/blob/gh-pages/scope_extensions-expl /** * List of not restored reasons for back-forward cache. */ - export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebViewSettingsChanged"|"WebViewJavaScriptObjectChanged"|"WebViewMessageListenerInjected"|"WebViewSafeBrowsingAllowlistChanged"|"WebViewDocumentStartJavascriptChanged"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame"|"RequestedByWebViewClient"; + export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebViewSettingsChanged"|"WebViewJavaScriptObjectChanged"|"WebViewMessageListenerInjected"|"WebViewSafeBrowsingAllowlistChanged"|"WebViewDocumentStartJavascriptChanged"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"ContentDiscarded"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame"|"RequestedByWebViewClient"; /** * Types of not restored reasons for back-forward cache. */ @@ -12151,6 +12197,16 @@ dependent on the reason: frameId: FrameId; reason: "remove"|"swap"; } + /** + * Fired before frame subtree is detached. Emitted before any frame of the +subtree is actually detached. + */ + export type frameSubtreeWillBeDetachedPayload = { + /** + * Id of the frame that is the root of the subtree that will be detached. + */ + frameId: FrameId; + } /** * Fired once navigation of the frame has completed. Frame is now associated with the new loader. */ @@ -14250,6 +14306,15 @@ int, only present for source registrations debugData: AttributionReportingAggregatableDebugReportingData[]; aggregationCoordinatorOrigin?: string; } + export interface AttributionScopesData { + values: string[]; + /** + * number instead of integer because not all uint32 can be represented by +int + */ + limit: number; + maxEventStates: number; + } export interface AttributionReportingSourceRegistration { time: Network.TimeSinceEpoch; /** @@ -14273,8 +14338,9 @@ int, only present for source registrations triggerDataMatching: AttributionReportingTriggerDataMatching; destinationLimitPriority: SignedInt64AsBase10; aggregatableDebugReportingConfig: AttributionReportingAggregatableDebugReportingConfig; + scopesData?: AttributionScopesData; } - export type AttributionReportingSourceRegistrationResult = "success"|"internalError"|"insufficientSourceCapacity"|"insufficientUniqueDestinationCapacity"|"excessiveReportingOrigins"|"prohibitedByBrowserPolicy"|"successNoised"|"destinationReportingLimitReached"|"destinationGlobalLimitReached"|"destinationBothLimitsReached"|"reportingOriginsPerSiteLimitReached"|"exceedsMaxChannelCapacity"|"exceedsMaxTriggerStateCardinality"|"destinationPerDayReportingLimitReached"; + export type AttributionReportingSourceRegistrationResult = "success"|"internalError"|"insufficientSourceCapacity"|"insufficientUniqueDestinationCapacity"|"excessiveReportingOrigins"|"prohibitedByBrowserPolicy"|"successNoised"|"destinationReportingLimitReached"|"destinationGlobalLimitReached"|"destinationBothLimitsReached"|"reportingOriginsPerSiteLimitReached"|"exceedsMaxChannelCapacity"|"exceedsMaxScopesChannelCapacity"|"exceedsMaxTriggerStateCardinality"|"exceedsMaxEventStatesLimit"|"destinationPerDayReportingLimitReached"; export type AttributionReportingSourceRegistrationTimeConfig = "include"|"exclude"; export interface AttributionReportingAggregatableValueDictEntry { key: string; @@ -14317,6 +14383,7 @@ int sourceRegistrationTimeConfig: AttributionReportingSourceRegistrationTimeConfig; triggerContextId?: string; aggregatableDebugReportingConfig: AttributionReportingAggregatableDebugReportingConfig; + scopes: string[]; } export type AttributionReportingEventLevelResult = "success"|"successDroppedLowerPriority"|"internalError"|"noCapacityForAttributionDestination"|"noMatchingSources"|"deduplicated"|"excessiveAttributions"|"priorityTooLow"|"neverAttributedSource"|"excessiveReportingOrigins"|"noMatchingSourceFilterData"|"prohibitedByBrowserPolicy"|"noMatchingConfigurations"|"excessiveReports"|"falselyAttributedSource"|"reportWindowPassed"|"notRegistered"|"reportWindowNotStarted"|"noMatchingTriggerData"; export type AttributionReportingAggregatableResult = "success"|"internalError"|"noCapacityForAttributionDestination"|"noMatchingSources"|"excessiveAttributions"|"excessiveReportingOrigins"|"noHistograms"|"insufficientBudget"|"noMatchingSourceFilterData"|"notRegistered"|"prohibitedByBrowserPolicy"|"deduplicated"|"reportWindowPassed"|"excessiveReports"; @@ -17019,7 +17086,7 @@ status is shared by prefetchStatusUpdated and prerenderStatusUpdated. * TODO(https://crbug.com/1384419): revisit the list of PrefetchStatus and filter out the ones that aren't necessary to the developers. */ - export type PrefetchStatus = "PrefetchAllowed"|"PrefetchFailedIneligibleRedirect"|"PrefetchFailedInvalidRedirect"|"PrefetchFailedMIMENotSupported"|"PrefetchFailedNetError"|"PrefetchFailedNon2XX"|"PrefetchFailedPerPageLimitExceeded"|"PrefetchEvictedAfterCandidateRemoved"|"PrefetchEvictedForNewerPrefetch"|"PrefetchHeldback"|"PrefetchIneligibleRetryAfter"|"PrefetchIsPrivacyDecoy"|"PrefetchIsStale"|"PrefetchNotEligibleBrowserContextOffTheRecord"|"PrefetchNotEligibleDataSaverEnabled"|"PrefetchNotEligibleExistingProxy"|"PrefetchNotEligibleHostIsNonUnique"|"PrefetchNotEligibleNonDefaultStoragePartition"|"PrefetchNotEligibleSameSiteCrossOriginPrefetchRequiredProxy"|"PrefetchNotEligibleSchemeIsNotHttps"|"PrefetchNotEligibleUserHasCookies"|"PrefetchNotEligibleUserHasServiceWorker"|"PrefetchNotEligibleBatterySaverEnabled"|"PrefetchNotEligiblePreloadingDisabled"|"PrefetchNotFinishedInTime"|"PrefetchNotStarted"|"PrefetchNotUsedCookiesChanged"|"PrefetchProxyNotAvailable"|"PrefetchResponseUsed"|"PrefetchSuccessfulButNotUsed"|"PrefetchNotUsedProbeFailed"; + export type PrefetchStatus = "PrefetchAllowed"|"PrefetchFailedIneligibleRedirect"|"PrefetchFailedInvalidRedirect"|"PrefetchFailedMIMENotSupported"|"PrefetchFailedNetError"|"PrefetchFailedNon2XX"|"PrefetchEvictedAfterCandidateRemoved"|"PrefetchEvictedForNewerPrefetch"|"PrefetchHeldback"|"PrefetchIneligibleRetryAfter"|"PrefetchIsPrivacyDecoy"|"PrefetchIsStale"|"PrefetchNotEligibleBrowserContextOffTheRecord"|"PrefetchNotEligibleDataSaverEnabled"|"PrefetchNotEligibleExistingProxy"|"PrefetchNotEligibleHostIsNonUnique"|"PrefetchNotEligibleNonDefaultStoragePartition"|"PrefetchNotEligibleSameSiteCrossOriginPrefetchRequiredProxy"|"PrefetchNotEligibleSchemeIsNotHttps"|"PrefetchNotEligibleUserHasCookies"|"PrefetchNotEligibleUserHasServiceWorker"|"PrefetchNotEligibleBatterySaverEnabled"|"PrefetchNotEligiblePreloadingDisabled"|"PrefetchNotFinishedInTime"|"PrefetchNotStarted"|"PrefetchNotUsedCookiesChanged"|"PrefetchProxyNotAvailable"|"PrefetchResponseUsed"|"PrefetchSuccessfulButNotUsed"|"PrefetchNotUsedProbeFailed"; /** * Information of headers to be displayed when the header mismatch occurred. */ @@ -20115,6 +20182,7 @@ Error was thrown. "DOM.inlineStyleInvalidated": DOM.inlineStyleInvalidatedPayload; "DOM.pseudoElementAdded": DOM.pseudoElementAddedPayload; "DOM.topLayerElementsUpdated": DOM.topLayerElementsUpdatedPayload; + "DOM.scrollableFlagUpdated": DOM.scrollableFlagUpdatedPayload; "DOM.pseudoElementRemoved": DOM.pseudoElementRemovedPayload; "DOM.setChildNodes": DOM.setChildNodesPayload; "DOM.shadowRootPopped": DOM.shadowRootPoppedPayload; @@ -20173,6 +20241,7 @@ Error was thrown. "Page.frameAttached": Page.frameAttachedPayload; "Page.frameClearedScheduledNavigation": Page.frameClearedScheduledNavigationPayload; "Page.frameDetached": Page.frameDetachedPayload; + "Page.frameSubtreeWillBeDetached": Page.frameSubtreeWillBeDetachedPayload; "Page.frameNavigated": Page.frameNavigatedPayload; "Page.documentOpened": Page.documentOpenedPayload; "Page.frameResized": Page.frameResizedPayload; @@ -20539,6 +20608,7 @@ Error was thrown. "Log.startViolationsReport": Log.startViolationsReportParameters; "Log.stopViolationsReport": Log.stopViolationsReportParameters; "Memory.getDOMCounters": Memory.getDOMCountersParameters; + "Memory.getDOMCountersForLeakDetection": Memory.getDOMCountersForLeakDetectionParameters; "Memory.prepareForLeakDetection": Memory.prepareForLeakDetectionParameters; "Memory.forciblyPurgeJavaScriptMemory": Memory.forciblyPurgeJavaScriptMemoryParameters; "Memory.setPressureNotificationsSuppressed": Memory.setPressureNotificationsSuppressedParameters; @@ -21148,6 +21218,7 @@ Error was thrown. "Log.startViolationsReport": Log.startViolationsReportReturnValue; "Log.stopViolationsReport": Log.stopViolationsReportReturnValue; "Memory.getDOMCounters": Memory.getDOMCountersReturnValue; + "Memory.getDOMCountersForLeakDetection": Memory.getDOMCountersForLeakDetectionReturnValue; "Memory.prepareForLeakDetection": Memory.prepareForLeakDetectionReturnValue; "Memory.forciblyPurgeJavaScriptMemory": Memory.forciblyPurgeJavaScriptMemoryReturnValue; "Memory.setPressureNotificationsSuppressed": Memory.setPressureNotificationsSuppressedReturnValue; diff --git a/packages/playwright-core/src/server/codegen/csharp.ts b/packages/playwright-core/src/server/codegen/csharp.ts index 2244a372fc..8e6561f04e 100644 --- a/packages/playwright-core/src/server/codegen/csharp.ts +++ b/packages/playwright-core/src/server/codegen/csharp.ts @@ -15,7 +15,8 @@ */ import type { BrowserContextOptions } from '../../../types/types'; -import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type * as actions from '@recorder/actions'; import { sanitizeDeviceOptions, toClickOptionsForSourceCode, toKeyboardModifiers, toSignalMap } from './language'; import { escapeWithQuotes, asLocator } from '../../utils'; import { deviceDescriptors } from '../deviceDescriptors'; @@ -45,14 +46,14 @@ export class CSharpLanguageGenerator implements LanguageGenerator { this._mode = mode; } - generateAction(actionInContext: ActionInContext): string { + generateAction(actionInContext: actions.ActionInContext): string { const action = this._generateActionInner(actionInContext); if (action) return action; return ''; } - _generateActionInner(actionInContext: ActionInContext): string { + _generateActionInner(actionInContext: actions.ActionInContext): string { const action = actionInContext.action; if (this._mode !== 'library' && (action.name === 'openPage' || action.name === 'closePage')) return ''; @@ -68,7 +69,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator { return formatter.format(); } - const locators = actionInContext.frame.framePath.map(selector => `.${this._asLocator(selector)}.ContentFrame()`); + const locators = actionInContext.frame.framePath.map(selector => `.${this._asLocator(selector)}.ContentFrame`); const subject = `${pageAlias}${locators.join('')}`; const signals = toSignalMap(action); @@ -101,7 +102,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator { return formatter.format(); } - private _generateActionCall(subject: string, actionInContext: ActionInContext): string { + private _generateActionCall(subject: string, actionInContext: actions.ActionInContext): string { const action = actionInContext.action; switch (action.name) { case 'openPage': diff --git a/packages/playwright-core/src/server/codegen/java.ts b/packages/playwright-core/src/server/codegen/java.ts index 3a640d36e2..507a040bce 100644 --- a/packages/playwright-core/src/server/codegen/java.ts +++ b/packages/playwright-core/src/server/codegen/java.ts @@ -16,7 +16,8 @@ import type { BrowserContextOptions } from '../../../types/types'; import type * as types from '../types'; -import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type * as actions from '@recorder/actions'; +import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; import { toClickOptionsForSourceCode, toKeyboardModifiers, toSignalMap } from './language'; import { deviceDescriptors } from '../deviceDescriptors'; import { JavaScriptFormatter } from './javascript'; @@ -44,7 +45,7 @@ export class JavaLanguageGenerator implements LanguageGenerator { this._mode = mode; } - generateAction(actionInContext: ActionInContext): string { + generateAction(actionInContext: actions.ActionInContext): string { const action = actionInContext.action; const pageAlias = actionInContext.frame.pageAlias; const offset = this._mode === 'junit' ? 4 : 6; @@ -90,7 +91,7 @@ export class JavaLanguageGenerator implements LanguageGenerator { return formatter.format(); } - private _generateActionCall(subject: string, actionInContext: ActionInContext, inFrameLocator: boolean): string { + private _generateActionCall(subject: string, actionInContext: actions.ActionInContext, inFrameLocator: boolean): string { const action = actionInContext.action; switch (action.name) { case 'openPage': diff --git a/packages/playwright-core/src/server/codegen/javascript.ts b/packages/playwright-core/src/server/codegen/javascript.ts index c3ed05d4d4..17f627b601 100644 --- a/packages/playwright-core/src/server/codegen/javascript.ts +++ b/packages/playwright-core/src/server/codegen/javascript.ts @@ -15,7 +15,8 @@ */ import type { BrowserContextOptions } from '../../../types/types'; -import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type * as actions from '@recorder/actions'; import { sanitizeDeviceOptions, toSignalMap, toKeyboardModifiers, toClickOptionsForSourceCode } from './language'; import { deviceDescriptors } from '../deviceDescriptors'; import { escapeWithQuotes, asLocator } from '../../utils'; @@ -33,7 +34,7 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator { this._isTest = isTest; } - generateAction(actionInContext: ActionInContext): string { + generateAction(actionInContext: actions.ActionInContext): string { const action = actionInContext.action; if (this._isTest && (action.name === 'openPage' || action.name === 'closePage')) return ''; @@ -74,7 +75,7 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator { return formatter.format(); } - private _generateActionCall(subject: string, actionInContext: ActionInContext): string { + private _generateActionCall(subject: string, actionInContext: actions.ActionInContext): string { const action = actionInContext.action; switch (action.name) { case 'openPage': diff --git a/packages/playwright-core/src/server/codegen/jsonl.ts b/packages/playwright-core/src/server/codegen/jsonl.ts index 78485297b6..8f506433f0 100644 --- a/packages/playwright-core/src/server/codegen/jsonl.ts +++ b/packages/playwright-core/src/server/codegen/jsonl.ts @@ -15,7 +15,8 @@ */ import { asLocator } from '../../utils'; -import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type * as actions from '@recorder/actions'; +import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; export class JsonlLanguageGenerator implements LanguageGenerator { id = 'jsonl'; @@ -23,7 +24,7 @@ export class JsonlLanguageGenerator implements LanguageGenerator { name = 'JSONL'; highlighter = 'javascript' as Language; - generateAction(actionInContext: ActionInContext): string { + generateAction(actionInContext: actions.ActionInContext): string { const locator = (actionInContext.action as any).selector ? JSON.parse(asLocator('jsonl', (actionInContext.action as any).selector)) : undefined; const entry = { ...actionInContext.action, diff --git a/packages/playwright-core/src/server/codegen/language.ts b/packages/playwright-core/src/server/codegen/language.ts index 4b1ba99b6f..b38959b89b 100644 --- a/packages/playwright-core/src/server/codegen/language.ts +++ b/packages/playwright-core/src/server/codegen/language.ts @@ -15,12 +15,11 @@ */ import type { BrowserContextOptions } from '../../..'; -import type * as actions from '../recorder/recorderActions'; +import type * as actions from '@recorder/actions'; import type * as types from '../types'; -import type { ActionInContext, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type { LanguageGenerator, LanguageGeneratorOptions } from './types'; -export function generateCode(actions: ActionInContext[], languageGenerator: LanguageGenerator, options: LanguageGeneratorOptions) { - actions = collapseActions(actions); +export function generateCode(actions: actions.ActionInContext[], languageGenerator: LanguageGenerator, options: LanguageGeneratorOptions) { const header = languageGenerator.generateHeader(options); const footer = languageGenerator.generateFooter(options.saveStorage); const actionTexts = actions.map(a => languageGenerator.generateAction(a)).filter(Boolean); @@ -70,6 +69,23 @@ export function toKeyboardModifiers(modifiers: number): types.SmartKeyboardModif return result; } +export function fromKeyboardModifiers(modifiers?: types.SmartKeyboardModifier[]): number { + let result = 0; + if (!modifiers) + return result; + if (modifiers.includes('Alt')) + result |= 1; + if (modifiers.includes('Control')) + result |= 2; + if (modifiers.includes('ControlOrMeta')) + result |= 2; + if (modifiers.includes('Meta')) + result |= 4; + if (modifiers.includes('Shift')) + result |= 8; + return result; +} + export function toClickOptionsForSourceCode(action: actions.ClickAction): types.MouseClickOptions { const modifiers = toKeyboardModifiers(action.modifiers); const options: types.MouseClickOptions = {}; @@ -84,19 +100,3 @@ export function toClickOptionsForSourceCode(action: actions.ClickAction): types. options.position = action.position; return options; } - -function collapseActions(actions: ActionInContext[]): ActionInContext[] { - const result: ActionInContext[] = []; - for (const action of actions) { - const lastAction = result[result.length - 1]; - const isSameAction = lastAction && lastAction.action.name === action.action.name && lastAction.frame.pageAlias === action.frame.pageAlias && lastAction.frame.framePath.join('|') === action.frame.framePath.join('|'); - const isSameSelector = lastAction && 'selector' in lastAction.action && 'selector' in action.action && action.action.selector === lastAction.action.selector; - const shouldMerge = isSameAction && (action.action.name === 'navigate' || (action.action.name === 'fill' && isSameSelector)); - if (!shouldMerge) { - result.push(action); - continue; - } - result[result.length - 1] = action; - } - return result; -} diff --git a/packages/playwright-core/src/server/codegen/python.ts b/packages/playwright-core/src/server/codegen/python.ts index 6c2b60dc70..38894695bc 100644 --- a/packages/playwright-core/src/server/codegen/python.ts +++ b/packages/playwright-core/src/server/codegen/python.ts @@ -15,7 +15,8 @@ */ import type { BrowserContextOptions } from '../../../types/types'; -import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; +import type * as actions from '@recorder/actions'; import { sanitizeDeviceOptions, toSignalMap, toKeyboardModifiers, toClickOptionsForSourceCode } from './language'; import { escapeWithQuotes, toSnakeCase, asLocator } from '../../utils'; import { deviceDescriptors } from '../deviceDescriptors'; @@ -40,7 +41,7 @@ export class PythonLanguageGenerator implements LanguageGenerator { this._asyncPrefix = isAsync ? 'async ' : ''; } - generateAction(actionInContext: ActionInContext): string { + generateAction(actionInContext: actions.ActionInContext): string { const action = actionInContext.action; if (this._isPyTest && (action.name === 'openPage' || action.name === 'closePage')) return ''; @@ -55,7 +56,7 @@ export class PythonLanguageGenerator implements LanguageGenerator { return formatter.format(); } - const locators = actionInContext.frame.framePath.map(selector => `.${this._asLocator(selector)}.content_frame()`); + const locators = actionInContext.frame.framePath.map(selector => `.${this._asLocator(selector)}.content_frame`); const subject = `${pageAlias}${locators.join('')}`; const signals = toSignalMap(action); @@ -83,7 +84,7 @@ export class PythonLanguageGenerator implements LanguageGenerator { return formatter.format(); } - private _generateActionCall(subject: string, actionInContext: ActionInContext): string { + private _generateActionCall(subject: string, actionInContext: actions.ActionInContext): string { const action = actionInContext.action; switch (action.name) { case 'openPage': diff --git a/packages/playwright-core/src/server/codegen/types.ts b/packages/playwright-core/src/server/codegen/types.ts index 48c1141a3e..81cd59948b 100644 --- a/packages/playwright-core/src/server/codegen/types.ts +++ b/packages/playwright-core/src/server/codegen/types.ts @@ -15,7 +15,7 @@ */ import type { BrowserContextOptions, LaunchOptions } from '../../../types/types'; -import type * as actions from '../recorder/recorderActions'; +import type * as actions from '@recorder/actions'; import type { Language } from '../../utils'; export type { Language } from '../../utils'; @@ -27,24 +27,12 @@ export type LanguageGeneratorOptions = { saveStorage?: string; }; -export type FrameDescription = { - pageAlias: string; - framePath: string[]; -}; - -export type ActionInContext = { - frame: FrameDescription; - description?: string; - action: actions.Action; - timestamp: number; -}; - export interface LanguageGenerator { id: string; groupName: string; name: string; highlighter: Language; generateHeader(options: LanguageGeneratorOptions): string; - generateAction(actionInContext: ActionInContext): string; + generateAction(actionInContext: actions.ActionInContext): string; generateFooter(saveStorage: string | undefined): string; } diff --git a/packages/playwright-core/src/server/debugController.ts b/packages/playwright-core/src/server/debugController.ts index 2a950d7c6a..53c6c3d99e 100644 --- a/packages/playwright-core/src/server/debugController.ts +++ b/packages/playwright-core/src/server/debugController.ts @@ -197,7 +197,7 @@ export class DebugController extends SdkObject { const contexts = new Set<BrowserContext>(); for (const page of this._playwright.allPages()) contexts.add(page.context()); - const result = await Promise.all([...contexts].map(c => Recorder.show(c, () => Promise.resolve(new InspectingRecorderApp(this)), { omitCallTracking: true }))); + const result = await Promise.all([...contexts].map(c => Recorder.showInspector(c, { omitCallTracking: true }, () => Promise.resolve(new InspectingRecorderApp(this))))); return result.filter(Boolean) as Recorder[]; } diff --git a/packages/playwright-core/src/server/deviceDescriptorsSource.json b/packages/playwright-core/src/server/deviceDescriptorsSource.json index efb2801f2c..1b0a015346 100644 --- a/packages/playwright-core/src/server/deviceDescriptorsSource.json +++ b/packages/playwright-core/src/server/deviceDescriptorsSource.json @@ -110,7 +110,7 @@ "defaultBrowserType": "webkit" }, "Galaxy S5": { - "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 360, "height": 640 @@ -121,7 +121,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S5 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 640, "height": 360 @@ -132,7 +132,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S8": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 360, "height": 740 @@ -143,7 +143,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S8 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 740, "height": 360 @@ -154,7 +154,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S9+": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 320, "height": 658 @@ -165,7 +165,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S9+ landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 658, "height": 320 @@ -176,7 +176,7 @@ "defaultBrowserType": "chromium" }, "Galaxy Tab S4": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36", "viewport": { "width": 712, "height": 1138 @@ -187,7 +187,7 @@ "defaultBrowserType": "chromium" }, "Galaxy Tab S4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36", "viewport": { "width": 1138, "height": 712 @@ -1098,7 +1098,7 @@ "defaultBrowserType": "webkit" }, "LG Optimus L70": { - "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 384, "height": 640 @@ -1109,7 +1109,7 @@ "defaultBrowserType": "chromium" }, "LG Optimus L70 landscape": { - "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 640, "height": 384 @@ -1120,7 +1120,7 @@ "defaultBrowserType": "chromium" }, "Microsoft Lumia 550": { - "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36 Edge/14.14263", + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 640, "height": 360 @@ -1131,7 +1131,7 @@ "defaultBrowserType": "chromium" }, "Microsoft Lumia 550 landscape": { - "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36 Edge/14.14263", + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 360, "height": 640 @@ -1142,7 +1142,7 @@ "defaultBrowserType": "chromium" }, "Microsoft Lumia 950": { - "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36 Edge/14.14263", + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 360, "height": 640 @@ -1153,7 +1153,7 @@ "defaultBrowserType": "chromium" }, "Microsoft Lumia 950 landscape": { - "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36 Edge/14.14263", + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 640, "height": 360 @@ -1164,7 +1164,7 @@ "defaultBrowserType": "chromium" }, "Nexus 10": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36", "viewport": { "width": 800, "height": 1280 @@ -1175,7 +1175,7 @@ "defaultBrowserType": "chromium" }, "Nexus 10 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36", "viewport": { "width": 1280, "height": 800 @@ -1186,7 +1186,7 @@ "defaultBrowserType": "chromium" }, "Nexus 4": { - "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 384, "height": 640 @@ -1197,7 +1197,7 @@ "defaultBrowserType": "chromium" }, "Nexus 4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 640, "height": 384 @@ -1208,7 +1208,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 360, "height": 640 @@ -1219,7 +1219,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 640, "height": 360 @@ -1230,7 +1230,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5X": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1241,7 +1241,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5X landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1252,7 +1252,7 @@ "defaultBrowserType": "chromium" }, "Nexus 6": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1263,7 +1263,7 @@ "defaultBrowserType": "chromium" }, "Nexus 6 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1274,7 +1274,7 @@ "defaultBrowserType": "chromium" }, "Nexus 6P": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1285,7 +1285,7 @@ "defaultBrowserType": "chromium" }, "Nexus 6P landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1296,7 +1296,7 @@ "defaultBrowserType": "chromium" }, "Nexus 7": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36", "viewport": { "width": 600, "height": 960 @@ -1307,7 +1307,7 @@ "defaultBrowserType": "chromium" }, "Nexus 7 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36", "viewport": { "width": 960, "height": 600 @@ -1362,7 +1362,7 @@ "defaultBrowserType": "webkit" }, "Pixel 2": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 411, "height": 731 @@ -1373,7 +1373,7 @@ "defaultBrowserType": "chromium" }, "Pixel 2 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 731, "height": 411 @@ -1384,7 +1384,7 @@ "defaultBrowserType": "chromium" }, "Pixel 2 XL": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 411, "height": 823 @@ -1395,7 +1395,7 @@ "defaultBrowserType": "chromium" }, "Pixel 2 XL landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 823, "height": 411 @@ -1406,7 +1406,7 @@ "defaultBrowserType": "chromium" }, "Pixel 3": { - "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 393, "height": 786 @@ -1417,7 +1417,7 @@ "defaultBrowserType": "chromium" }, "Pixel 3 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 786, "height": 393 @@ -1428,7 +1428,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4": { - "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 353, "height": 745 @@ -1439,7 +1439,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 745, "height": 353 @@ -1450,7 +1450,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4a (5G)": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "screen": { "width": 412, "height": 892 @@ -1465,7 +1465,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4a (5G) landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "screen": { "height": 892, "width": 412 @@ -1480,7 +1480,7 @@ "defaultBrowserType": "chromium" }, "Pixel 5": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "screen": { "width": 393, "height": 851 @@ -1495,7 +1495,7 @@ "defaultBrowserType": "chromium" }, "Pixel 5 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "screen": { "width": 851, "height": 393 @@ -1510,7 +1510,7 @@ "defaultBrowserType": "chromium" }, "Pixel 7": { - "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "screen": { "width": 412, "height": 915 @@ -1525,7 +1525,7 @@ "defaultBrowserType": "chromium" }, "Pixel 7 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "screen": { "width": 915, "height": 412 @@ -1540,7 +1540,7 @@ "defaultBrowserType": "chromium" }, "Moto G4": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 360, "height": 640 @@ -1551,7 +1551,7 @@ "defaultBrowserType": "chromium" }, "Moto G4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36", "viewport": { "width": 640, "height": 360 @@ -1562,7 +1562,7 @@ "defaultBrowserType": "chromium" }, "Desktop Chrome HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36", "screen": { "width": 1792, "height": 1120 @@ -1577,7 +1577,7 @@ "defaultBrowserType": "chromium" }, "Desktop Edge HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36 Edg/129.0.6668.42", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36 Edg/130.0.6723.31", "screen": { "width": 1792, "height": 1120 @@ -1592,7 +1592,7 @@ "defaultBrowserType": "chromium" }, "Desktop Firefox HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0", "screen": { "width": 1792, "height": 1120 @@ -1622,7 +1622,7 @@ "defaultBrowserType": "webkit" }, "Desktop Chrome": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36", "screen": { "width": 1920, "height": 1080 @@ -1637,7 +1637,7 @@ "defaultBrowserType": "chromium" }, "Desktop Edge": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.42 Safari/537.36 Edg/129.0.6668.42", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36 Edg/130.0.6723.31", "screen": { "width": 1920, "height": 1080 @@ -1652,7 +1652,7 @@ "defaultBrowserType": "chromium" }, "Desktop Firefox": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0", "screen": { "width": 1920, "height": 1080 diff --git a/packages/playwright-core/src/server/dialog.ts b/packages/playwright-core/src/server/dialog.ts index 51dcfc2fc9..f0793d43fb 100644 --- a/packages/playwright-core/src/server/dialog.ts +++ b/packages/playwright-core/src/server/dialog.ts @@ -39,6 +39,7 @@ export class Dialog extends SdkObject { this._onHandle = onHandle; this._defaultValue = defaultValue || ''; this._page._frameManager.dialogDidOpen(this); + this.instrumentation.onDialog(this); } page() { diff --git a/packages/playwright-core/src/server/dispatchers/DEPS.list b/packages/playwright-core/src/server/dispatchers/DEPS.list index 4a9ce35a5a..de1039af05 100644 --- a/packages/playwright-core/src/server/dispatchers/DEPS.list +++ b/packages/playwright-core/src/server/dispatchers/DEPS.list @@ -1,5 +1,6 @@ [*] ../../common/ +../../generated/ ../../protocol/ ../../utils/ ../../zipBundle.ts diff --git a/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts b/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts index 7198229f53..77f38c5558 100644 --- a/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts @@ -103,7 +103,9 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels. } async info(params: channels.AndroidDeviceTapParams): Promise<channels.AndroidDeviceInfoResult> { - return { info: await this._object.send('info', params) }; + const info = await this._object.send('info', params); + fixupAndroidElementInfo(info); + return { info }; } async inputType(params: channels.AndroidDeviceInputTypeParams) { @@ -305,3 +307,14 @@ const keyMap = new Map<string, number>([ ['Copy', 278], ['Paste', 279], ]); + +function fixupAndroidElementInfo(info: channels.AndroidElementInfo) { + // Some of the properties are nullable, see https://developer.android.com/reference/androidx/test/uiautomator/UiObject2. + info.clazz = info.clazz || ''; + info.pkg = info.pkg || ''; + info.res = info.res || ''; + info.desc = info.desc || ''; + info.text = info.text || ''; + for (const child of info.children || []) + fixupAndroidElementInfo(child); +} diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index 5c8fa550a7..c6ffce49f7 100644 --- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -41,12 +41,14 @@ import { serializeError } from '../errors'; import { ElementHandleDispatcher } from './elementHandlerDispatcher'; import { RecorderInTraceViewer } from '../recorder/recorderInTraceViewer'; import { RecorderApp } from '../recorder/recorderApp'; +import { WebSocketRouteDispatcher } from './webSocketRouteDispatcher'; export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextChannel, DispatcherScope> implements channels.BrowserContextChannel { _type_EventTarget = true; _type_BrowserContext = true; private _context: BrowserContext; private _subscriptions = new Set<channels.BrowserContextUpdateSubscriptionParams['event']>(); + _webSocketInterceptionPatterns: channels.BrowserContextSetWebSocketInterceptionPatternsParams['patterns'] = []; constructor(parentScope: DispatcherScope, context: BrowserContext) { // We will reparent these to the context below. @@ -283,6 +285,12 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel }); } + async setWebSocketInterceptionPatterns(params: channels.PageSetWebSocketInterceptionPatternsParams, metadata: CallMetadata): Promise<void> { + this._webSocketInterceptionPatterns = params.patterns; + if (params.patterns.length) + await WebSocketRouteDispatcher.installIfNeeded(this, this._context); + } + async storageState(params: channels.BrowserContextStorageStateParams, metadata: CallMetadata): Promise<channels.BrowserContextStorageStateResult> { return await this._context.storageState(); } @@ -292,9 +300,18 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel await this._context.close(params); } - async recorderSupplementEnable(params: channels.BrowserContextRecorderSupplementEnableParams): Promise<void> { - const factory = process.env.PW_RECORDER_IS_TRACE_VIEWER ? RecorderInTraceViewer.factory(this._context) : RecorderApp.factory(this._context); - await Recorder.show(this._context, factory, params); + async enableRecorder(params: channels.BrowserContextEnableRecorderParams): Promise<void> { + if (params.codegenMode === 'trace-events') { + await this._context.tracing.start({ + name: 'trace', + snapshots: true, + screenshots: true, + live: true, + }); + await Recorder.show('trace-events', this._context, RecorderInTraceViewer.factory(this._context), params); + } else { + await Recorder.show('actions', this._context, RecorderApp.factory(this._context), params); + } } async pause(params: channels.BrowserContextPauseParams, metadata: CallMetadata) { diff --git a/packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts b/packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts index 07e9d38ee5..33960e72d5 100644 --- a/packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts @@ -63,10 +63,6 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandl return { value: serializeResult(await this._object.jsonValue()) }; } - async objectCount(params?: channels.JSHandleObjectCountParams | undefined): Promise<channels.JSHandleObjectCountResult> { - return { count: await this._object.objectCount() }; - } - async dispose(_: any, metadata: CallMetadata) { metadata.potentiallyClosesScope = true; this._object.dispose(); diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index a97ddbf1f0..5e3b73a73f 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -35,12 +35,14 @@ import { ArtifactDispatcher } from './artifactDispatcher'; import type { Download } from '../download'; import { createGuid, urlMatches } from '../../utils'; import type { BrowserContextDispatcher } from './browserContextDispatcher'; +import { WebSocketRouteDispatcher } from './webSocketRouteDispatcher'; export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, BrowserContextDispatcher> implements channels.PageChannel { _type_EventTarget = true; _type_Page = true; private _page: Page; _subscriptions = new Set<channels.PageUpdateSubscriptionParams['event']>(); + _webSocketInterceptionPatterns: channels.PageSetWebSocketInterceptionPatternsParams['patterns'] = []; static from(parentScope: BrowserContextDispatcher, page: Page): PageDispatcher { return PageDispatcher.fromNullable(parentScope, page)!; @@ -137,8 +139,8 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(metadata, params)) }; } - async forceGarbageCollection(params: channels.PageForceGarbageCollectionParams, metadata: CallMetadata): Promise<channels.PageForceGarbageCollectionResult> { - await this._page.forceGarbageCollection(); + async requestGC(params: channels.PageRequestGCParams, metadata: CallMetadata): Promise<channels.PageRequestGCResult> { + await this._page.requestGC(); } async registerLocatorHandler(params: channels.PageRegisterLocatorHandlerParams, metadata: CallMetadata): Promise<channels.PageRegisterLocatorHandlerResult> { @@ -186,6 +188,12 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows }); } + async setWebSocketInterceptionPatterns(params: channels.PageSetWebSocketInterceptionPatternsParams, metadata: CallMetadata): Promise<void> { + this._webSocketInterceptionPatterns = params.patterns; + if (params.patterns.length) + await WebSocketRouteDispatcher.installIfNeeded(this.parentScope(), this._page); + } + async expectScreenshot(params: channels.PageExpectScreenshotParams, metadata: CallMetadata): Promise<channels.PageExpectScreenshotResult> { const mask: { frame: Frame, selector: string }[] = (params.mask || []).map(({ frame, selector }) => ({ frame: (frame as FrameDispatcher)._object, diff --git a/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts b/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts new file mode 100644 index 0000000000..87f469b7d0 --- /dev/null +++ b/packages/playwright-core/src/server/dispatchers/webSocketRouteDispatcher.ts @@ -0,0 +1,156 @@ +/** + * 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. + */ + +import type { BrowserContext } from '../browserContext'; +import type { Frame } from '../frames'; +import { Page } from '../page'; +import type * as channels from '@protocol/channels'; +import { Dispatcher } from './dispatcher'; +import { createGuid, urlMatches } from '../../utils'; +import { PageDispatcher } from './pageDispatcher'; +import type { BrowserContextDispatcher } from './browserContextDispatcher'; +import * as webSocketMockSource from '../../generated/webSocketMockSource'; +import type * as ws from '../injected/webSocketMock'; +import { eventsHelper } from '../../utils/eventsHelper'; + +const kBindingInstalledSymbol = Symbol('webSocketRouteBindingInstalled'); +const kInitScriptInstalledSymbol = Symbol('webSocketRouteInitScriptInstalled'); + +export class WebSocketRouteDispatcher extends Dispatcher<{ guid: string }, channels.WebSocketRouteChannel, PageDispatcher | BrowserContextDispatcher> implements channels.WebSocketRouteChannel { + _type_WebSocketRoute = true; + private _id: string; + private _frame: Frame; + private static _idToDispatcher = new Map<string, WebSocketRouteDispatcher>(); + + constructor(scope: PageDispatcher | BrowserContextDispatcher, id: string, url: string, frame: Frame) { + super(scope, { guid: 'webSocketRoute@' + createGuid() }, 'WebSocketRoute', { url }); + this._id = id; + this._frame = frame; + this._eventListeners.push( + // When the frame navigates or detaches, there will be no more communication + // from the mock websocket, so pretend like it was closed. + eventsHelper.addEventListener(frame._page, Page.Events.InternalFrameNavigatedToNewDocument, (frame: Frame) => { + if (frame === this._frame) + this._executionContextGone(); + }), + eventsHelper.addEventListener(frame._page, Page.Events.FrameDetached, (frame: Frame) => { + if (frame === this._frame) + this._executionContextGone(); + }), + eventsHelper.addEventListener(frame._page, Page.Events.Close, () => this._executionContextGone()), + eventsHelper.addEventListener(frame._page, Page.Events.Crash, () => this._executionContextGone()), + ); + WebSocketRouteDispatcher._idToDispatcher.set(this._id, this); + (scope as any)._dispatchEvent('webSocketRoute', { webSocketRoute: this }); + } + + static async installIfNeeded(contextDispatcher: BrowserContextDispatcher, target: Page | BrowserContext) { + const context = target instanceof Page ? target.context() : target; + if (!(context as any)[kBindingInstalledSymbol]) { + (context as any)[kBindingInstalledSymbol] = true; + + await context.exposeBinding('__pwWebSocketBinding', false, (source, payload: ws.BindingPayload) => { + if (payload.type === 'onCreate') { + const pageDispatcher = PageDispatcher.fromNullable(contextDispatcher, source.page); + let scope: PageDispatcher | BrowserContextDispatcher | undefined; + if (pageDispatcher && matchesPattern(pageDispatcher, context._options.baseURL, payload.url)) + scope = pageDispatcher; + else if (matchesPattern(contextDispatcher, context._options.baseURL, payload.url)) + scope = contextDispatcher; + if (scope) { + new WebSocketRouteDispatcher(scope, payload.id, payload.url, source.frame); + } else { + const request: ws.PassthroughRequest = { id: payload.id, type: 'passthrough' }; + source.frame.evaluateExpression(`globalThis.__pwWebSocketDispatch(${JSON.stringify(request)})`).catch(() => {}); + } + return; + } + + const dispatcher = WebSocketRouteDispatcher._idToDispatcher.get(payload.id); + if (payload.type === 'onMessageFromPage') + dispatcher?._dispatchEvent('messageFromPage', { message: payload.data.data, isBase64: payload.data.isBase64 }); + if (payload.type === 'onMessageFromServer') + dispatcher?._dispatchEvent('messageFromServer', { message: payload.data.data, isBase64: payload.data.isBase64 }); + if (payload.type === 'onClosePage') + dispatcher?._dispatchEvent('closePage', { code: payload.code, reason: payload.reason, wasClean: payload.wasClean }); + if (payload.type === 'onCloseServer') + dispatcher?._dispatchEvent('closeServer', { code: payload.code, reason: payload.reason, wasClean: payload.wasClean }); + }); + } + + if (!(target as any)[kInitScriptInstalledSymbol]) { + (target as any)[kInitScriptInstalledSymbol] = true; + await target.addInitScript(` + (() => { + const module = {}; + ${webSocketMockSource.source} + (module.exports.inject())(globalThis); + })(); + `); + } + } + + async connect(params: channels.WebSocketRouteConnectParams) { + await this._evaluateAPIRequest({ id: this._id, type: 'connect' }); + } + + async ensureOpened(params: channels.WebSocketRouteEnsureOpenedParams) { + await this._evaluateAPIRequest({ id: this._id, type: 'ensureOpened' }); + } + + async sendToPage(params: channels.WebSocketRouteSendToPageParams) { + await this._evaluateAPIRequest({ id: this._id, type: 'sendToPage', data: { data: params.message, isBase64: params.isBase64 } }); + } + + async sendToServer(params: channels.WebSocketRouteSendToServerParams) { + await this._evaluateAPIRequest({ id: this._id, type: 'sendToServer', data: { data: params.message, isBase64: params.isBase64 } }); + } + + async closePage(params: channels.WebSocketRouteClosePageParams) { + await this._evaluateAPIRequest({ id: this._id, type: 'closePage', code: params.code, reason: params.reason, wasClean: params.wasClean }); + } + + async closeServer(params: channels.WebSocketRouteCloseServerParams) { + await this._evaluateAPIRequest({ id: this._id, type: 'closeServer', code: params.code, reason: params.reason, wasClean: params.wasClean }); + } + + private async _evaluateAPIRequest(request: ws.APIRequest) { + await this._frame.evaluateExpression(`globalThis.__pwWebSocketDispatch(${JSON.stringify(request)})`).catch(() => {}); + } + + override _onDispose() { + WebSocketRouteDispatcher._idToDispatcher.delete(this._id); + } + + private _executionContextGone() { + // We could enter here after being disposed upon page closure: + // - first from the recursive dispose inintiated by PageDispatcher; + // - then from our own page.on('close') listener. + if (!this._disposed) { + this._dispatchEvent('closePage', { wasClean: true }); + this._dispatchEvent('closeServer', { wasClean: true }); + } + } +} + +function matchesPattern(dispatcher: PageDispatcher | BrowserContextDispatcher, baseURL: string | undefined, url: string) { + for (const pattern of dispatcher._webSocketInterceptionPatterns || []) { + const urlMatch = pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags) : pattern.glob; + if (urlMatches(baseURL, url, urlMatch)) + return true; + } + return false; +} diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index 55105bd50c..c2ed979fbe 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -50,9 +50,9 @@ export function isNonRecoverableDOMError(error: Error) { export class FrameExecutionContext extends js.ExecutionContext { readonly frame: frames.Frame; private _injectedScriptPromise?: Promise<js.JSHandle>; - readonly world: types.World; + readonly world: types.World | null; - constructor(delegate: js.ExecutionContextDelegate, frame: frames.Frame, world: types.World) { + constructor(delegate: js.ExecutionContextDelegate, frame: frames.Frame, world: types.World|null) { super(frame, delegate, world || 'content-script'); this.frame = frame; this.world = world; @@ -421,7 +421,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { return maybePoint; const point = roundPoint(maybePoint); progress.metadata.point = point; - await progress.beforeInputAction(this); + await this.instrumentation.onBeforeInputAction(this, progress.metadata); let hitTargetInterceptionHandle: js.JSHandle<HitTargetInterceptionResult> | undefined; if (force) { @@ -490,9 +490,19 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { return 'done'; } + private async _markAsTargetElement(metadata: CallMetadata) { + if (!metadata.id) + return; + await this.evaluateInUtility(([injected, node, callId]) => { + if (node.nodeType === 1 /* Node.ELEMENT_NODE */) + injected.markTargetElements(new Set([node as Node as Element]), callId); + }, metadata.id); + } + async hover(metadata: CallMetadata, options: types.PointerActionOptions & types.PointerActionWaitOptions): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._hover(progress, options); return assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); @@ -505,6 +515,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async click(metadata: CallMetadata, options: { noWaitAfter?: boolean } & types.MouseClickOptions & types.PointerActionWaitOptions = {}): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._click(progress, { ...options, waitAfter: !options.noWaitAfter }); return assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); @@ -517,6 +528,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async dblclick(metadata: CallMetadata, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._dblclick(progress, options); return assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); @@ -529,6 +541,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async tap(metadata: CallMetadata, options: types.PointerActionWaitOptions = {}): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._tap(progress, options); return assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); @@ -541,6 +554,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async selectOption(metadata: CallMetadata, elements: ElementHandle[], values: types.SelectOption[], options: types.CommonActionOptions): Promise<string[]> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._selectOption(progress, elements, values, options); return throwRetargetableDOMError(result); }, this._page._timeoutSettings.timeout(options)); @@ -549,7 +563,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async _selectOption(progress: Progress, elements: ElementHandle[], values: types.SelectOption[], options: types.CommonActionOptions): Promise<string[] | 'error:notconnected'> { let resultingOptions: string[] = []; await this._retryAction(progress, 'select option', async () => { - await progress.beforeInputAction(this); + await this.instrumentation.onBeforeInputAction(this, progress.metadata); if (!options.force) progress.log(` waiting for element to be visible and enabled`); const optionsToSelect = [...elements, ...values]; @@ -574,6 +588,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async fill(metadata: CallMetadata, value: string, options: types.CommonActionOptions = {}): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._fill(progress, value, options); assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); @@ -582,7 +597,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async _fill(progress: Progress, value: string, options: types.CommonActionOptions): Promise<'error:notconnected' | 'done'> { progress.log(` fill("${value}")`); return await this._retryAction(progress, 'fill', async () => { - await progress.beforeInputAction(this); + await this.instrumentation.onBeforeInputAction(this, progress.metadata); if (!options.force) progress.log(' waiting for element to be visible, enabled and editable'); const result = await this.evaluateInUtility(async ([injected, node, { value, force }]) => { @@ -629,6 +644,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { const inputFileItems = await prepareFilesForUpload(this._frame, params); const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._setInputFiles(progress, inputFileItems); return assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(params)); @@ -655,7 +671,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { if (result === 'error:notconnected' || !result.asElement()) return 'error:notconnected'; const retargeted = result.asElement() as ElementHandle<HTMLInputElement>; - await progress.beforeInputAction(this); + await this.instrumentation.onBeforeInputAction(this, progress.metadata); progress.throwIfAborted(); // Avoid action that has side-effects. if (localPaths || localDirectory) { const localPathsOrDirectory = localDirectory ? [localDirectory] : localPaths!; @@ -677,6 +693,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async focus(metadata: CallMetadata): Promise<void> { const controller = new ProgressController(metadata, this); await controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._focus(progress); return assertDone(throwRetargetableDOMError(result)); }, 0); @@ -695,6 +712,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async type(metadata: CallMetadata, text: string, options: { delay?: number } & types.TimeoutOptions & types.StrictOptions): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._type(progress, text, options); return assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); @@ -702,7 +720,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async _type(progress: Progress, text: string, options: { delay?: number } & types.TimeoutOptions & types.StrictOptions): Promise<'error:notconnected' | 'done'> { progress.log(`elementHandle.type("${text}")`); - await progress.beforeInputAction(this); + await this.instrumentation.onBeforeInputAction(this, progress.metadata); const result = await this._focus(progress, true /* resetSelectionIfNotFocused */); if (result !== 'done') return result; @@ -714,6 +732,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async press(metadata: CallMetadata, key: string, options: { delay?: number, noWaitAfter?: boolean } & types.TimeoutOptions & types.StrictOptions): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { + await this._markAsTargetElement(metadata); const result = await this._press(progress, key, options); return assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); @@ -721,7 +740,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async _press(progress: Progress, key: string, options: { delay?: number, noWaitAfter?: boolean } & types.TimeoutOptions & types.StrictOptions): Promise<'error:notconnected' | 'done'> { progress.log(`elementHandle.press("${key}")`); - await progress.beforeInputAction(this); + await this.instrumentation.onBeforeInputAction(this, progress.metadata); return this._page._frameManager.waitForSignalsCreatedBy(progress, !options.noWaitAfter, async () => { const result = await this._focus(progress, true /* resetSelectionIfNotFocused */); if (result !== 'done') @@ -753,6 +772,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { const result = await this.evaluateInUtility(([injected, node]) => injected.elementState(node, 'checked'), {}); return throwRetargetableDOMError(result); }; + await this._markAsTargetElement(progress.metadata); if (await isChecked() === state) return 'done'; const result = await this._click(progress, { ...options, waitAfter: 'disabled' }); diff --git a/packages/playwright-core/src/server/download.ts b/packages/playwright-core/src/server/download.ts index f7a92c8c7d..78a9c015dc 100644 --- a/packages/playwright-core/src/server/download.ts +++ b/packages/playwright-core/src/server/download.ts @@ -35,16 +35,25 @@ export class Download { this._suggestedFilename = suggestedFilename; page._browserContext._downloads.add(this); if (suggestedFilename !== undefined) - this._page.emit(Page.Events.Download, this); + this._fireDownloadEvent(); + } + + page(): Page { + return this._page; } _filenameSuggested(suggestedFilename: string) { assert(this._suggestedFilename === undefined); this._suggestedFilename = suggestedFilename; - this._page.emit(Page.Events.Download, this); + this._fireDownloadEvent(); } suggestedFilename(): string { return this._suggestedFilename!; } + + private _fireDownloadEvent() { + this._page.instrumentation.onDownload(this._page, this); + this._page.emit(Page.Events.Download, this); + } } diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index fc4e2c027d..4bf05e22af 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -25,12 +25,12 @@ import zlib from 'zlib'; import type { HTTPCredentials } from '../../types/types'; import { TimeoutSettings } from '../common/timeoutSettings'; import { getUserAgent } from '../utils/userAgent'; -import { assert, createGuid, monotonicTime } from '../utils'; +import { assert, constructURLBasedOnBaseURL, createGuid, monotonicTime } from '../utils'; import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle'; import { BrowserContext, verifyClientCertificates } from './browserContext'; import { CookieStore, domainMatches, parseRawCookie } from './cookieStore'; import { MultipartFormData } from './formData'; -import { httpHappyEyeballsAgent, httpsHappyEyeballsAgent } from '../utils/happy-eyeballs'; +import { httpHappyEyeballsAgent, httpsHappyEyeballsAgent, timingForSocket } from '../utils/happy-eyeballs'; import type { CallMetadata } from './instrumentation'; import { SdkObject } from './instrumentation'; import type { Playwright } from './playwright'; @@ -40,6 +40,8 @@ import { Tracing } from './trace/recorder/tracing'; import type * as types from './types'; import type { HeadersArray, ProxySettings } from './types'; import { getMatchingTLSOptionsForOrigin, rewriteOpenSSLErrorIfNeeded } from './socksClientCertificatesInterceptor'; +import type * as har from '@trace/har'; +import { TLSSocket } from 'tls'; type FetchRequestOptions = { userAgent: string; @@ -71,6 +73,10 @@ export type APIRequestFinishedEvent = { statusCode: number; statusMessage: string; body?: Buffer; + timings: har.Timings; + serverIPAddress?: string; + serverPort?: number; + securityDetails?: har.SecurityDetails; }; type SendRequestOptions = https.RequestOptions & { @@ -153,7 +159,7 @@ export abstract class APIRequestContext extends SdkObject { setHeader(headers, name, value); } - const requestUrl = new URL(params.url, defaults.baseURL); + const requestUrl = new URL(constructURLBasedOnBaseURL(defaults.baseURL, params.url)); if (params.encodedParams) { requestUrl.search = params.encodedParams; } else if (params.params) { @@ -257,6 +263,7 @@ export abstract class APIRequestContext extends SdkObject { try { return await this._sendRequest(progress, url, options, postData); } catch (e) { + e = rewriteOpenSSLErrorIfNeeded(e); if (maxRetries === 0) throw e; if (i === maxRetries || (options.deadline && monotonicTime() + backoff > options.deadline)) @@ -294,8 +301,32 @@ export abstract class APIRequestContext extends SdkObject { // If we have a proxy agent already, do not override it. const agent = options.agent || (url.protocol === 'https:' ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent); const requestOptions = { ...options, agent }; + + const startAt = monotonicTime(); + let dnsLookupAt: number | undefined; + let tcpConnectionAt: number | undefined; + let tlsHandshakeAt: number | undefined; + let requestFinishAt: number | undefined; + let serverIPAddress: string | undefined; + let serverPort: number | undefined; + + let securityDetails: har.SecurityDetails | undefined; + const request = requestConstructor(url, requestOptions as any, async response => { + const responseAt = monotonicTime(); const notifyRequestFinished = (body?: Buffer) => { + const endAt = monotonicTime(); + // spec: http://www.softwareishard.com/blog/har-12-spec/#timings + const timings: har.Timings = { + send: requestFinishAt! - startAt, + wait: responseAt - requestFinishAt!, + receive: endAt - responseAt, + dns: dnsLookupAt ? dnsLookupAt - startAt : -1, + connect: (tlsHandshakeAt ?? tcpConnectionAt!) - startAt, // "If [ssl] is defined then the time is also included in the connect field " + ssl: tlsHandshakeAt ? tlsHandshakeAt - tcpConnectionAt! : -1, + blocked: -1, + }; + const requestFinishedEvent: APIRequestFinishedEvent = { requestEvent, httpVersion: response.httpVersion, @@ -304,7 +335,11 @@ export abstract class APIRequestContext extends SdkObject { headers: response.headers, rawHeaders: response.rawHeaders, cookies, - body + body, + timings, + serverIPAddress, + serverPort, + securityDetails, }; this.emit(APIRequestContext.Events.RequestFinished, requestFinishedEvent); }; @@ -441,7 +476,7 @@ export abstract class APIRequestContext extends SdkObject { body.on('data', chunk => chunks.push(chunk)); body.on('end', notifyBodyFinished); }); - request.on('error', error => reject(rewriteOpenSSLErrorIfNeeded(error))); + request.on('error', reject); const disposeListener = () => { reject(new Error('Request context disposed.')); @@ -450,6 +485,35 @@ export abstract class APIRequestContext extends SdkObject { this.on(APIRequestContext.Events.Dispose, disposeListener); request.on('close', () => this.off(APIRequestContext.Events.Dispose, disposeListener)); + request.on('socket', socket => { + // happy eyeballs don't emit lookup and connect events, so we use our custom ones + const happyEyeBallsTimings = timingForSocket(socket); + dnsLookupAt = happyEyeBallsTimings.dnsLookupAt; + tcpConnectionAt = happyEyeBallsTimings.tcpConnectionAt; + + // non-happy-eyeballs sockets + socket.on('lookup', () => { dnsLookupAt = monotonicTime(); }); + socket.on('connect', () => { tcpConnectionAt = monotonicTime(); }); + socket.on('secureConnect', () => { + tlsHandshakeAt = monotonicTime(); + + if (socket instanceof TLSSocket) { + const peerCertificate = socket.getPeerCertificate(); + securityDetails = { + protocol: socket.getProtocol() ?? undefined, + subjectName: peerCertificate.subject.CN, + validFrom: new Date(peerCertificate.valid_from).getTime() / 1000, + validTo: new Date(peerCertificate.valid_to).getTime() / 1000, + issuer: peerCertificate.issuer.CN + }; + } + }); + + serverIPAddress = socket.remoteAddress; + serverPort = socket.remotePort; + }); + request.on('finish', () => { requestFinishAt = monotonicTime(); }); + progress.log(`→ ${options.method} ${url.toString()}`); if (options.headers) { for (const [name, value] of Object.entries(options.headers)) diff --git a/packages/playwright-core/src/server/firefox/ffExecutionContext.ts b/packages/playwright-core/src/server/firefox/ffExecutionContext.ts index 3a6b931a47..c7a3f106f8 100644 --- a/packages/playwright-core/src/server/firefox/ffExecutionContext.ts +++ b/packages/playwright-core/src/server/firefox/ffExecutionContext.ts @@ -51,15 +51,6 @@ export class FFExecutionContext implements js.ExecutionContextDelegate { return payload.result!.objectId!; } - rawCallFunctionNoReply(func: Function, ...args: any[]) { - this._session.send('Runtime.callFunction', { - functionDeclaration: func.toString(), - args: args.map(a => a instanceof js.JSHandle ? { objectId: a._objectId } : { value: a }) as any, - returnByValue: true, - executionContextId: this._executionContextId - }).catch(() => {}); - } - async evaluateWithArguments(expression: string, returnByValue: boolean, utilityScript: js.JSHandle<any>, values: any[], objectIds: string[]): Promise<any> { const payload = await this._session.send('Runtime.callFunction', { functionDeclaration: expression, @@ -98,10 +89,6 @@ export class FFExecutionContext implements js.ExecutionContextDelegate { objectId }); } - - objectCount(objectId: js.ObjectId): Promise<number> { - throw new Error('Method not implemented in Firefox.'); - } } function checkException(exceptionDetails?: Protocol.Runtime.ExceptionDetails) { diff --git a/packages/playwright-core/src/server/firefox/ffPage.ts b/packages/playwright-core/src/server/firefox/ffPage.ts index 03a27954dd..61790feae4 100644 --- a/packages/playwright-core/src/server/firefox/ffPage.ts +++ b/packages/playwright-core/src/server/firefox/ffPage.ts @@ -163,16 +163,15 @@ export class FFPage implements PageDelegate { if (!frame) return; const delegate = new FFExecutionContext(this._session, executionContextId); - let worldName: types.World; + let worldName: types.World|null = null; if (auxData.name === UTILITY_WORLD_NAME) worldName = 'utility'; else if (!auxData.name) worldName = 'main'; - else - return; const context = new dom.FrameExecutionContext(delegate, frame, worldName); (context as any)[contextDelegateSymbol] = delegate; - frame._contextCreated(worldName, context); + if (worldName) + frame._contextCreated(worldName, context); this._contextIdToContext.set(executionContextId, context); } @@ -400,7 +399,7 @@ export class FFPage implements PageDelegate { return success; } - async forceGarbageCollection(): Promise<void> { + async requestGC(): Promise<void> { await this._session.send('Heap.collectGarbage'); } diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 3b952ea02a..d107a7f981 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -248,6 +248,11 @@ export class FrameManager { const frame = this._frames.get(frameId); if (!frame) return; + const pending = frame.pendingDocument(); + if (pending && pending.documentId === undefined && pending.request === undefined) { + // WebKit has notified about the same-document navigation being requested, so clear it. + frame.setPendingDocument(undefined); + } frame._url = url; const navigationEvent: NavigationEvent = { url, name: frame._name, isPublic: true }; this._fireInternalFrameNavigation(frame, navigationEvent); @@ -782,13 +787,16 @@ export class Frame extends SdkObject { throw new Error(`state: expected one of (attached|detached|visible|hidden)`); return controller.run(async progress => { progress.log(`waiting for ${this._asLocator(selector)}${state === 'attached' ? '' : ' to be ' + state}`); - return await this.waitForSelectorInternal(progress, selector, options, scope); + return await this.waitForSelectorInternal(progress, selector, true, options, scope); }, this._page._timeoutSettings.timeout(options)); } - async waitForSelectorInternal(progress: Progress, selector: string, options: types.WaitForElementOptions, scope?: dom.ElementHandle): Promise<dom.ElementHandle<Element> | null> { + async waitForSelectorInternal(progress: Progress, selector: string, performLocatorHandlersCheckpoint: boolean, options: types.WaitForElementOptions, scope?: dom.ElementHandle): Promise<dom.ElementHandle<Element> | null> { const { state = 'visible' } = options; const promise = this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async continuePolling => { + if (performLocatorHandlersCheckpoint) + await this._page.performLocatorHandlersCheckpoint(progress); + const resolved = await this.selectors.resolveInjectedForSelector(selector, options, scope); progress.throwIfAborted(); if (!resolved) { @@ -1121,8 +1129,10 @@ export class Frame extends SdkObject { progress.throwIfAborted(); if (!resolved) return continuePolling; - const result = await resolved.injected.evaluateHandle((injected, { info }) => { + const result = await resolved.injected.evaluateHandle((injected, { info, callId }) => { const elements = injected.querySelectorAll(info.parsed, document); + if (callId) + injected.markTargetElements(new Set(elements), callId); const element = elements[0] as Element | undefined; let log = ''; if (elements.length > 1) { @@ -1133,7 +1143,7 @@ export class Frame extends SdkObject { log = ` locator resolved to ${injected.previewNode(element)}`; } return { log, success: !!element, element }; - }, { info: resolved.info }); + }, { info: resolved.info, callId: progress.metadata.id }); const { log, success } = await result.evaluate(r => ({ log: r.log, success: r.success })); if (log) progress.log(log); @@ -1475,6 +1485,8 @@ export class Frame extends SdkObject { const { log, matches, received, missingReceived } = await injected.evaluate(async (injected, { info, options, callId }) => { const elements = info ? injected.querySelectorAll(info.parsed, document) : []; + if (callId) + injected.markTargetElements(new Set(elements), callId); const isArray = options.expression === 'to.have.count' || options.expression.endsWith('.array'); let log = ''; if (isArray) @@ -1483,8 +1495,6 @@ export class Frame extends SdkObject { throw injected.strictModeViolationError(info!.parsed, elements); else if (elements.length) log = ` locator resolved to ${injected.previewNode(elements[0])}`; - if (callId) - injected.markTargetElements(new Set(elements), callId); return { log, ...await injected.expect(elements[0], options, elements) }; }, { info, options, callId: progress.metadata.id }); diff --git a/packages/playwright-core/src/server/har/harTracer.ts b/packages/playwright-core/src/server/har/harTracer.ts index e6330f3889..b4b90d7976 100644 --- a/packages/playwright-core/src/server/har/harTracer.ts +++ b/packages/playwright-core/src/server/har/harTracer.ts @@ -212,6 +212,20 @@ export class HarTracer { harEntry.response.statusText = event.statusMessage; harEntry.response.httpVersion = event.httpVersion; harEntry.response.redirectURL = event.headers.location || ''; + + if (!this._options.omitServerIP) { + harEntry.serverIPAddress = event.serverIPAddress; + harEntry._serverPort = event.serverPort; + } + + if (!this._options.omitTiming) { + harEntry.timings = event.timings; + this._computeHarEntryTotalTime(harEntry); + } + + if (!this._options.omitSecurityDetails) + harEntry._securityDetails = event.securityDetails; + for (let i = 0; i < event.rawHeaders.length; i += 2) { harEntry.response.headers.push({ name: event.rawHeaders[i], @@ -230,6 +244,8 @@ export class HarTracer { if (contentType) content.mimeType = contentType; this._storeResponseContent(event.body, content, 'other'); + if (!this._options.omitSizes) + harEntry.response.bodySize = event.body?.length ?? 0; if (this._started) this._delegate.onEntryFinished(harEntry); diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index 69fe959f81..53cf647cfb 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -65,6 +65,7 @@ export class InjectedScript { readonly isUnderTest: boolean; private _sdkLanguage: Language; private _testIdAttributeNameForStrictErrorAndConsoleCodegen: string = 'data-testid'; + private _markedElements?: { callId: string, elements: Set<Element> }; // eslint-disable-next-line no-restricted-globals readonly window: Window & typeof globalThis; readonly document: Document; @@ -1081,14 +1082,33 @@ export class InjectedScript { } markTargetElements(markedElements: Set<Element>, callId: string) { - const customEvent = new CustomEvent('__playwright_target__', { + if (this._markedElements?.callId !== callId) + this._markedElements = undefined; + const previous = this._markedElements?.elements || new Set(); + + const unmarkEvent = new CustomEvent('__playwright_unmark_target__', { bubbles: true, cancelable: true, detail: callId, composed: true, }); - for (const element of markedElements) - element.dispatchEvent(customEvent); + for (const element of previous) { + if (!markedElements.has(element)) + element.dispatchEvent(unmarkEvent); + } + + const markEvent = new CustomEvent('__playwright_mark_target__', { + bubbles: true, + cancelable: true, + detail: callId, + composed: true, + }); + for (const element of markedElements) { + if (!previous.has(element)) + element.dispatchEvent(markEvent); + } + + this._markedElements = { callId, elements: markedElements }; } private _setupGlobalListenersRemovalDetection() { diff --git a/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts b/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts new file mode 100644 index 0000000000..9a96d8f25d --- /dev/null +++ b/packages/playwright-core/src/server/injected/recorder/pollingRecorder.ts @@ -0,0 +1,91 @@ +/** + * 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. + */ + +import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes'; +import type * as actions from '@recorder/actions'; +import type { InjectedScript } from '../injectedScript'; +import { Recorder } from './recorder'; +import type { RecorderDelegate } from './recorder'; + +interface Embedder { + __pw_recorderPerformAction(action: actions.PerformOnRecordAction): Promise<void>; + __pw_recorderRecordAction(action: actions.Action): Promise<void>; + __pw_recorderState(): Promise<UIState>; + __pw_recorderSetSelector(selector: string): Promise<void>; + __pw_recorderSetMode(mode: Mode): Promise<void>; + __pw_recorderSetOverlayState(state: OverlayState): Promise<void>; + __pw_refreshOverlay(): void; +} + +export class PollingRecorder implements RecorderDelegate { + private _recorder: Recorder; + private _embedder: Embedder; + private _pollRecorderModeTimer: number | undefined; + + constructor(injectedScript: InjectedScript) { + this._recorder = new Recorder(injectedScript); + this._embedder = injectedScript.window as any; + + injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners()); + + const refreshOverlay = () => { + this._pollRecorderMode().catch(e => console.log(e)); // eslint-disable-line no-console + }; + this._embedder.__pw_refreshOverlay = refreshOverlay; + refreshOverlay(); + } + + private async _pollRecorderMode() { + const pollPeriod = 1000; + if (this._pollRecorderModeTimer) + clearTimeout(this._pollRecorderModeTimer); + const state = await this._embedder.__pw_recorderState().catch(() => {}); + if (!state) { + this._pollRecorderModeTimer = this._recorder.injectedScript.builtinSetTimeout(() => this._pollRecorderMode(), pollPeriod); + return; + } + const win = this._recorder.document.defaultView!; + if (win.top !== win) { + // Only show action point in the main frame, since it is relative to the page's viewport. + // Otherwise we'll see multiple action points at different locations. + state.actionPoint = undefined; + } + this._recorder.setUIState(state, this); + this._pollRecorderModeTimer = this._recorder.injectedScript.builtinSetTimeout(() => this._pollRecorderMode(), pollPeriod); + } + + async performAction(action: actions.PerformOnRecordAction) { + await this._embedder.__pw_recorderPerformAction(action); + } + + async recordAction(action: actions.Action): Promise<void> { + await this._embedder.__pw_recorderRecordAction(action); + } + + async setSelector(selector: string): Promise<void> { + await this._embedder.__pw_recorderSetSelector(selector); + } + + async setMode(mode: Mode): Promise<void> { + await this._embedder.__pw_recorderSetMode(mode); + } + + async setOverlayState(state: OverlayState): Promise<void> { + await this._embedder.__pw_recorderSetOverlayState(state); + } +} + +export default PollingRecorder; diff --git a/packages/playwright-core/src/server/injected/recorder/recorder.ts b/packages/playwright-core/src/server/injected/recorder/recorder.ts index dfbd4608f0..cdc29a1050 100644 --- a/packages/playwright-core/src/server/injected/recorder/recorder.ts +++ b/packages/playwright-core/src/server/injected/recorder/recorder.ts @@ -14,16 +14,15 @@ * limitations under the License. */ -import type * as actions from '../../recorder/recorderActions'; +import type * as actions from '@recorder/actions'; import type { InjectedScript } from '../injectedScript'; import type { Point } from '../../../common/types'; import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes'; import type { ElementText } from '../selectorUtils'; import type { Highlight, HighlightOptions } from '../highlight'; import clipPaths from './clipPaths'; -import type { SimpleDomNode } from '../simpleDom'; -interface RecorderDelegate { +export interface RecorderDelegate { performAction?(action: actions.PerformOnRecordAction): Promise<void>; recordAction?(action: actions.Action): Promise<void>; setSelector?(selector: string): Promise<void>; @@ -206,7 +205,7 @@ class InspectTool implements RecorderTool { class RecordActionTool implements RecorderTool { private _recorder: Recorder; - private _performingAction = false; + private _performingActions = new Set<actions.PerformOnRecordAction>(); private _hoveredModel: HighlightModel | null = null; private _hoveredElement: HTMLElement | null = null; private _activeModel: HighlightModel | null = null; @@ -334,21 +333,21 @@ class RecordActionTool implements RecorderTool { onPointerDown(event: PointerEvent) { if (this._shouldIgnoreMouseEvent(event)) return; - if (!this._performingAction) + if (!this._performingActions.size) consumeEvent(event); } onPointerUp(event: PointerEvent) { if (this._shouldIgnoreMouseEvent(event)) return; - if (!this._performingAction) + if (!this._performingActions.size) consumeEvent(event); } onMouseDown(event: MouseEvent) { if (this._shouldIgnoreMouseEvent(event)) return; - if (!this._performingAction) + if (!this._performingActions.size) consumeEvent(event); this._activeModel = this._hoveredModel; } @@ -356,7 +355,7 @@ class RecordActionTool implements RecorderTool { onMouseUp(event: MouseEvent) { if (this._shouldIgnoreMouseEvent(event)) return; - if (!this._performingAction) + if (!this._performingActions.size) consumeEvent(event); } @@ -509,9 +508,16 @@ class RecordActionTool implements RecorderTool { private _actionInProgress(event: Event): boolean { // If Playwright is performing action for us, bail. - if (this._performingAction) - return true; - // Consume as the first thing. + const isKeyEvent = event instanceof KeyboardEvent; + const isMouseOrPointerEvent = event instanceof MouseEvent || event instanceof PointerEvent; + for (const action of this._performingActions) { + if (isKeyEvent && action.name === 'press' && event.key === action.key) + return true; + if (isMouseOrPointerEvent && (action.name === 'click' || action.name === 'check' || action.name === 'uncheck')) + return true; + } + + // Consume event if action is not being executed. consumeEvent(event); return false; } @@ -535,9 +541,9 @@ class RecordActionTool implements RecorderTool { this._hoveredModel = null; this._activeModel = null; this._recorder.updateHighlight(null, false); - this._performingAction = true; + this._performingActions.add(action); void this._recorder.performAction(action).then(() => { - this._performingAction = false; + this._performingActions.delete(action); // If that was a keyboard action, it similarly requires new selectors for active model. this._onFocus(false); @@ -1039,9 +1045,12 @@ export class Recorder { this.highlight.install(); // some frameworks erase the DOM on hydration, this ensures it's reattached - const recreationInterval = setInterval(() => { + let recreationInterval: number | undefined; + const recreate = () => { this.highlight.install(); - }, 500); + recreationInterval = this.injectedScript.builtinSetTimeout(recreate, 500); + }; + recreationInterval = this.injectedScript.builtinSetTimeout(recreate, 500); this._listeners.push(() => clearInterval(recreationInterval)); this.overlay?.install(); @@ -1454,73 +1463,3 @@ function createSvgElement(doc: Document, { tagName, attrs, children }: SvgJson): return elem; } - -interface Embedder { - __pw_recorderPerformAction(action: actions.PerformOnRecordAction, simpleDomNode?: SimpleDomNode): Promise<void>; - __pw_recorderRecordAction(action: actions.Action, simpleDomNode?: SimpleDomNode): Promise<void>; - __pw_recorderState(): Promise<UIState>; - __pw_recorderSetSelector(selector: string): Promise<void>; - __pw_recorderSetMode(mode: Mode): Promise<void>; - __pw_recorderSetOverlayState(state: OverlayState): Promise<void>; - __pw_refreshOverlay(): void; -} - -export class PollingRecorder implements RecorderDelegate { - private _recorder: Recorder; - private _embedder: Embedder; - private _pollRecorderModeTimer: number | undefined; - - constructor(injectedScript: InjectedScript) { - this._recorder = new Recorder(injectedScript); - this._embedder = injectedScript.window as any; - - injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners()); - - const refreshOverlay = () => { - this._pollRecorderMode().catch(e => console.log(e)); // eslint-disable-line no-console - }; - this._embedder.__pw_refreshOverlay = refreshOverlay; - refreshOverlay(); - } - - private async _pollRecorderMode() { - const pollPeriod = 1000; - if (this._pollRecorderModeTimer) - clearTimeout(this._pollRecorderModeTimer); - const state = await this._embedder.__pw_recorderState().catch(() => {}); - if (!state) { - this._pollRecorderModeTimer = this._recorder.injectedScript.builtinSetTimeout(() => this._pollRecorderMode(), pollPeriod); - return; - } - const win = this._recorder.document.defaultView!; - if (win.top !== win) { - // Only show action point in the main frame, since it is relative to the page's viewport. - // Otherwise we'll see multiple action points at different locations. - state.actionPoint = undefined; - } - this._recorder.setUIState(state, this); - this._pollRecorderModeTimer = this._recorder.injectedScript.builtinSetTimeout(() => this._pollRecorderMode(), pollPeriod); - } - - async performAction(action: actions.PerformOnRecordAction, simpleDomNode?: SimpleDomNode) { - await this._embedder.__pw_recorderPerformAction(action, simpleDomNode); - } - - async recordAction(action: actions.Action, simpleDomNode?: SimpleDomNode): Promise<void> { - await this._embedder.__pw_recorderRecordAction(action, simpleDomNode); - } - - async setSelector(selector: string): Promise<void> { - await this._embedder.__pw_recorderSetSelector(selector); - } - - async setMode(mode: Mode): Promise<void> { - await this._embedder.__pw_recorderSetMode(mode); - } - - async setOverlayState(state: OverlayState): Promise<void> { - await this._embedder.__pw_recorderSetOverlayState(state); - } -} - -export default PollingRecorder; diff --git a/packages/playwright-core/src/server/injected/webSocketMock.ts b/packages/playwright-core/src/server/injected/webSocketMock.ts new file mode 100644 index 0000000000..69d6bc0585 --- /dev/null +++ b/packages/playwright-core/src/server/injected/webSocketMock.ts @@ -0,0 +1,359 @@ +/** + * 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. + */ + +export type WebSocketMessage = string | ArrayBufferLike | Blob | ArrayBufferView; +export type WSData = { data: string, isBase64: boolean }; + +export type OnCreatePayload = { type: 'onCreate', id: string, url: string }; +export type OnMessageFromPagePayload = { type: 'onMessageFromPage', id: string, data: WSData }; +export type OnClosePagePayload = { type: 'onClosePage', id: string, code: number | undefined, reason: string | undefined, wasClean: boolean }; +export type OnMessageFromServerPayload = { type: 'onMessageFromServer', id: string, data: WSData }; +export type OnCloseServerPayload = { type: 'onCloseServer', id: string, code: number | undefined, reason: string | undefined, wasClean: boolean }; +export type BindingPayload = OnCreatePayload | OnMessageFromPagePayload | OnMessageFromServerPayload | OnClosePagePayload | OnCloseServerPayload; + +export type ConnectRequest = { type: 'connect', id: string }; +export type PassthroughRequest = { type: 'passthrough', id: string }; +export type EnsureOpenedRequest = { type: 'ensureOpened', id: string }; +export type SendToPageRequest = { type: 'sendToPage', id: string, data: WSData }; +export type SendToServerRequest = { type: 'sendToServer', id: string, data: WSData }; +export type ClosePageRequest = { type: 'closePage', id: string, code: number | undefined, reason: string | undefined, wasClean: boolean }; +export type CloseServerRequest = { type: 'closeServer', id: string, code: number | undefined, reason: string | undefined, wasClean: boolean }; +export type APIRequest = ConnectRequest | PassthroughRequest | EnsureOpenedRequest | SendToPageRequest | SendToServerRequest | ClosePageRequest | CloseServerRequest; + +// eslint-disable-next-line no-restricted-globals +type GlobalThis = typeof globalThis; + +export function inject(globalThis: GlobalThis) { + if ((globalThis as any).__pwWebSocketDispatch) + return; + + function generateId() { + const bytes = new Uint8Array(32); + globalThis.crypto.getRandomValues(bytes); + const hex = '0123456789abcdef'; + return [...bytes].map(value => { + const high = Math.floor(value / 16); + const low = value % 16; + return hex[high] + hex[low]; + }).join(''); + } + + function bufferToData(b: Uint8Array): WSData { + let s = ''; + for (let i = 0; i < b.length; i++) + s += String.fromCharCode(b[i]); + return { data: globalThis.btoa(s), isBase64: true }; + } + + function stringToBuffer(s: string): ArrayBuffer { + s = globalThis.atob(s); + const b = new Uint8Array(s.length); + for (let i = 0; i < s.length; i++) + b[i] = s.charCodeAt(i); + return b.buffer; + } + + // Note: this function tries to be synchronous when it can to preserve the ability to send + // multiple messages synchronously in the same order and then synchronously close. + function messageToData(message: WebSocketMessage, cb: (data: WSData) => any) { + if (message instanceof globalThis.Blob) + return message.arrayBuffer().then(buffer => cb(bufferToData(new Uint8Array(buffer)))); + if (typeof message === 'string') + return cb({ data: message, isBase64: false }); + if (ArrayBuffer.isView(message)) + return cb(bufferToData(new Uint8Array(message.buffer, message.byteOffset, message.byteLength))); + return cb(bufferToData(new Uint8Array(message))); + } + + function dataToMessage(data: WSData, binaryType: 'blob' | 'arraybuffer'): WebSocketMessage { + if (!data.isBase64) + return data.data; + const buffer = stringToBuffer(data.data); + return binaryType === 'arraybuffer' ? buffer : new Blob([buffer]); + } + + const binding = (globalThis as any).__pwWebSocketBinding as (message: BindingPayload) => void; + const NativeWebSocket: typeof WebSocket = globalThis.WebSocket; + const idToWebSocket = new Map<string, WebSocketMock>(); + (globalThis as any).__pwWebSocketDispatch = (request: APIRequest) => { + const ws = idToWebSocket.get(request.id); + if (!ws) + return; + if (request.type === 'connect') + ws._apiConnect(); + if (request.type === 'passthrough') + ws._apiPassThrough(); + if (request.type === 'ensureOpened') + ws._apiEnsureOpened(); + if (request.type === 'sendToPage') + ws._apiSendToPage(dataToMessage(request.data, ws.binaryType)); + if (request.type === 'closePage') + ws._apiClosePage(request.code, request.reason, request.wasClean); + if (request.type === 'sendToServer') + ws._apiSendToServer(dataToMessage(request.data, ws.binaryType)); + if (request.type === 'closeServer') + ws._apiCloseServer(request.code, request.reason, request.wasClean); + }; + + class WebSocketMock extends EventTarget { + static readonly CONNECTING: 0 = 0; // WebSocket.CONNECTING + static readonly OPEN: 1 = 1; // WebSocket.OPEN + static readonly CLOSING: 2 = 2; // WebSocket.CLOSING + static readonly CLOSED: 3 = 3; // WebSocket.CLOSED + + CONNECTING: 0 = 0; // WebSocket.CONNECTING + OPEN: 1 = 1; // WebSocket.OPEN + CLOSING: 2 = 2; // WebSocket.CLOSING + CLOSED: 3 = 3; // WebSocket.CLOSED + + private _oncloseListener: WebSocket['onclose'] = null; + private _onerrorListener: WebSocket['onerror'] = null; + private _onmessageListener: WebSocket['onmessage'] = null; + private _onopenListener: WebSocket['onopen'] = null; + + bufferedAmount: number = 0; + extensions: string = ''; + protocol: string = ''; + readyState: number = 0; + readonly url: string; + + private _id: string; + private _origin: string = ''; + private _protocols?: string | string[]; + private _ws?: WebSocket; + private _passthrough = false; + private _wsBufferedMessages: WebSocketMessage[] = []; + private _binaryType: BinaryType = 'blob'; + + constructor(url: string | URL, protocols?: string | string[]) { + super(); + + this.url = typeof url === 'string' ? url : url.href; + try { + this._origin = new URL(url).origin; + } catch { + } + this._protocols = protocols; + + this._id = generateId(); + idToWebSocket.set(this._id, this); + binding({ type: 'onCreate', id: this._id, url: this.url }); + } + + // --- native WebSocket implementation --- + + get binaryType() { + return this._binaryType; + } + + set binaryType(type) { + this._binaryType = type; + if (this._ws) + this._ws.binaryType = type; + } + + get onclose() { + return this._oncloseListener; + } + + set onclose(listener) { + if (this._oncloseListener) + this.removeEventListener('close', this._oncloseListener as any); + this._oncloseListener = listener; + if (this._oncloseListener) + this.addEventListener('close', this._oncloseListener as any); + } + + get onerror() { + return this._onerrorListener; + } + + set onerror(listener) { + if (this._onerrorListener) + this.removeEventListener('error', this._onerrorListener); + this._onerrorListener = listener; + if (this._onerrorListener) + this.addEventListener('error', this._onerrorListener); + } + + get onopen() { + return this._onopenListener; + } + + set onopen(listener) { + if (this._onopenListener) + this.removeEventListener('open', this._onopenListener); + this._onopenListener = listener; + if (this._onopenListener) + this.addEventListener('open', this._onopenListener); + } + + get onmessage() { + return this._onmessageListener; + } + + set onmessage(listener) { + if (this._onmessageListener) + this.removeEventListener('message', this._onmessageListener as any); + this._onmessageListener = listener; + if (this._onmessageListener) + this.addEventListener('message', this._onmessageListener as any); + } + + send(message: WebSocketMessage): void { + if (this.readyState === WebSocketMock.CONNECTING) + throw new DOMException(`Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.`); + if (this.readyState !== WebSocketMock.OPEN) + throw new DOMException(`WebSocket is already in CLOSING or CLOSED state.`); + if (this._passthrough) { + if (this._ws) + this._apiSendToServer(message); + } else { + messageToData(message, data => binding({ type: 'onMessageFromPage', id: this._id, data })); + } + } + + close(code?: number, reason?: string): void { + if (code !== undefined && code !== 1000 && (code < 3000 || code > 4999)) + throw new DOMException(`Failed to execute 'close' on 'WebSocket': The close code must be either 1000, or between 3000 and 4999. ${code} is neither.`); + if (this.readyState === WebSocketMock.OPEN || this.readyState === WebSocketMock.CONNECTING) + this.readyState = WebSocketMock.CLOSING; + if (this._passthrough) + this._apiCloseServer(code, reason, true); + else + binding({ type: 'onClosePage', id: this._id, code, reason, wasClean: true }); + } + + // --- methods called from the routing API --- + + _apiEnsureOpened() { + // This is called at the end of the route handler. If we did not connect to the server, + // assume that websocket will be fully mocked. In this case, pretend that server + // connection is established right away. + if (!this._ws) + this._ensureOpened(); + } + + _apiSendToPage(message: WebSocketMessage) { + // Calling "sendToPage()" from the route handler. Allow this for easier testing. + this._ensureOpened(); + if (this.readyState !== WebSocketMock.OPEN) + throw new DOMException(`WebSocket is already in CLOSING or CLOSED state.`); + this.dispatchEvent(new MessageEvent('message', { data: message, origin: this._origin, cancelable: true })); + } + + _apiSendToServer(message: WebSocketMessage) { + if (!this._ws) + throw new Error('Cannot send a message before connecting to the server'); + if (this._ws.readyState === WebSocketMock.CONNECTING) + this._wsBufferedMessages.push(message); + else + this._ws.send(message); + } + + _apiConnect() { + if (this._ws) + throw new Error('Can only connect to the server once'); + + this._ws = new NativeWebSocket(this.url, this._protocols); + this._ws.binaryType = this._binaryType; + + this._ws.onopen = () => { + for (const message of this._wsBufferedMessages) + this._ws!.send(message); + this._wsBufferedMessages = []; + this._ensureOpened(); + }; + + this._ws.onclose = event => { + this._onWSClose(event.code, event.reason, event.wasClean); + }; + + this._ws.onmessage = event => { + if (this._passthrough) + this._apiSendToPage(event.data); + else + messageToData(event.data, data => binding({ type: 'onMessageFromServer', id: this._id, data })); + }; + + this._ws.onerror = () => { + // We do not expose errors in the API, so short-curcuit the error event. + const event = new Event('error', { cancelable: true }); + this.dispatchEvent(event); + }; + } + + // This method connects to the server, and passes all messages through, + // as if WebSocketMock was not engaged. + _apiPassThrough() { + this._passthrough = true; + this._apiConnect(); + } + + _apiCloseServer(code: number | undefined, reason: string | undefined, wasClean: boolean) { + if (!this._ws) { + // Short-curcuit when there is no server. + this._onWSClose(code, reason, wasClean); + return; + } + if (this._ws.readyState === WebSocketMock.CONNECTING || this._ws.readyState === WebSocketMock.OPEN) + this._ws.close(code, reason); + } + + _apiClosePage(code: number | undefined, reason: string | undefined, wasClean: boolean) { + if (this.readyState === WebSocketMock.CLOSED) + return; + this.readyState = WebSocketMock.CLOSED; + this.dispatchEvent(new CloseEvent('close', { code, reason, wasClean, cancelable: true })); + this._maybeCleanup(); + if (this._passthrough) + this._apiCloseServer(code, reason, wasClean); + else + binding({ type: 'onClosePage', id: this._id, code, reason, wasClean }); + } + + // --- internals --- + + _ensureOpened() { + if (this.readyState !== WebSocketMock.CONNECTING) + return; + this.readyState = WebSocketMock.OPEN; + this.dispatchEvent(new Event('open', { cancelable: true })); + } + + private _onWSClose(code: number | undefined, reason: string | undefined, wasClean: boolean) { + if (this._passthrough) + this._apiClosePage(code, reason, wasClean); + else + binding({ type: 'onCloseServer', id: this._id, code, reason, wasClean }); + if (this._ws) { + this._ws.onopen = null; + this._ws.onclose = null; + this._ws.onmessage = null; + this._ws.onerror = null; + this._ws = undefined; + this._wsBufferedMessages = []; + } + this._maybeCleanup(); + } + + private _maybeCleanup() { + if (this.readyState === WebSocketMock.CLOSED && !this._ws) + idToWebSocket.delete(this._id); + } + } + globalThis.WebSocket = class WebSocket extends WebSocketMock {}; +} diff --git a/packages/playwright-core/src/server/instrumentation.ts b/packages/playwright-core/src/server/instrumentation.ts index b4628ac904..2a384fb169 100644 --- a/packages/playwright-core/src/server/instrumentation.ts +++ b/packages/playwright-core/src/server/instrumentation.ts @@ -20,7 +20,6 @@ import type { APIRequestContext } from './fetch'; import type { Browser } from './browser'; import type { BrowserContext } from './browserContext'; import type { BrowserType } from './browserType'; -import type { ElementHandle } from './dom'; import type { Frame } from './frames'; import type { Page } from './page'; import type { Playwright } from './playwright'; @@ -35,6 +34,8 @@ export type Attribution = { }; import type { CallMetadata } from '@protocol/callMetadata'; +import type { Dialog } from './dialog'; +import type { Download } from './download'; export type { CallMetadata } from '@protocol/callMetadata'; export class SdkObject extends EventEmitter { @@ -55,24 +56,28 @@ export interface Instrumentation { addListener(listener: InstrumentationListener, context: BrowserContext | APIRequestContext | null): void; removeListener(listener: InstrumentationListener): void; onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>; - onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata, element: ElementHandle): Promise<void>; + onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>; onCallLog(sdkObject: SdkObject, metadata: CallMetadata, logName: string, message: string): void; onAfterCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>; onPageOpen(page: Page): void; onPageClose(page: Page): void; onBrowserOpen(browser: Browser): void; onBrowserClose(browser: Browser): void; + onDialog(dialog: Dialog): void; + onDownload(page: Page, download: Download): void; } export interface InstrumentationListener { onBeforeCall?(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>; - onBeforeInputAction?(sdkObject: SdkObject, metadata: CallMetadata, element: ElementHandle): Promise<void>; + onBeforeInputAction?(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>; onCallLog?(sdkObject: SdkObject, metadata: CallMetadata, logName: string, message: string): void; onAfterCall?(sdkObject: SdkObject, metadata: CallMetadata): Promise<void>; onPageOpen?(page: Page): void; onPageClose?(page: Page): void; onBrowserOpen?(browser: Browser): void; onBrowserClose?(browser: Browser): void; + onDialog?(dialog: Dialog): void; + onDownload?(page: Page, download: Download): void; } export function createInstrumentation(): Instrumentation { diff --git a/packages/playwright-core/src/server/javascript.ts b/packages/playwright-core/src/server/javascript.ts index 663df78b5b..dbbe89d5c2 100644 --- a/packages/playwright-core/src/server/javascript.ts +++ b/packages/playwright-core/src/server/javascript.ts @@ -53,12 +53,10 @@ export type SmartHandle<T> = T extends Node ? dom.ElementHandle<T> : JSHandle<T> export interface ExecutionContextDelegate { rawEvaluateJSON(expression: string): Promise<any>; rawEvaluateHandle(expression: string): Promise<ObjectId>; - rawCallFunctionNoReply(func: Function, ...args: any[]): void; evaluateWithArguments(expression: string, returnByValue: boolean, utilityScript: JSHandle<any>, values: any[], objectIds: ObjectId[]): Promise<any>; getProperties(context: ExecutionContext, objectId: ObjectId): Promise<Map<string, JSHandle>>; createHandle(context: ExecutionContext, remoteObject: RemoteObject): JSHandle; releaseHandle(objectId: ObjectId): Promise<void>; - objectCount(objectId: ObjectId): Promise<number>; } export class ExecutionContext extends SdkObject { @@ -89,10 +87,6 @@ export class ExecutionContext extends SdkObject { return this._raceAgainstContextDestroyed(this._delegate.rawEvaluateHandle(expression)); } - rawCallFunctionNoReply(func: Function, ...args: any[]): void { - this._delegate.rawCallFunctionNoReply(func, ...args); - } - evaluateWithArguments(expression: string, returnByValue: boolean, utilityScript: JSHandle<any>, values: any[], objectIds: ObjectId[]): Promise<any> { return this._raceAgainstContextDestroyed(this._delegate.evaluateWithArguments(expression, returnByValue, utilityScript, values, objectIds)); } @@ -126,10 +120,6 @@ export class ExecutionContext extends SdkObject { return this._utilityScriptPromise; } - async objectCount(objectId: ObjectId): Promise<number> { - return this._delegate.objectCount(objectId); - } - async doSlowMo() { // overridden in FrameExecutionContext } @@ -156,10 +146,6 @@ export class JSHandle<T = any> extends SdkObject { (globalThis as any).leakedJSHandles.set(this, new Error('Leaked JSHandle')); } - callFunctionNoReply(func: Function, arg: any) { - this._context.rawCallFunctionNoReply(func, this, arg); - } - async evaluate<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg?: Arg): Promise<R> { return evaluate(this._context, true /* returnByValue */, pageFunction, this, arg); } @@ -246,12 +232,6 @@ export class JSHandle<T = any> extends SdkObject { if (this._previewCallback) this._previewCallback(preview); } - - async objectCount(): Promise<number> { - if (!this._objectId) - throw new Error('Can only count objects for a handle that points to the constructor prototype'); - return this._context.objectCount(this._objectId); - } } export async function evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> { diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index aeaeb0af88..3fb72bb0c3 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -54,7 +54,7 @@ export interface PageDelegate { reload(): Promise<void>; goBack(): Promise<boolean>; goForward(): Promise<boolean>; - forceGarbageCollection(): Promise<void>; + requestGC(): Promise<void>; addInitScript(initScript: InitScript): Promise<void>; removeNonInternalInitScripts(): Promise<void>; closePage(runBeforeUnload: boolean): Promise<void>; @@ -431,8 +431,8 @@ export class Page extends SdkObject { }), this._timeoutSettings.navigationTimeout(options)); } - forceGarbageCollection(): Promise<void> { - return this._delegate.forceGarbageCollection(); + requestGC(): Promise<void> { + return this._delegate.requestGC(); } registerLocatorHandler(selector: string, noWaitAfter: boolean | undefined) { @@ -473,7 +473,7 @@ export class Page extends SdkObject { progress.throwIfAborted(); if (!handler.noWaitAfter) { progress.log(` locator handler has finished, waiting for ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)} to be hidden`); - await this.mainFrame().waitForSelectorInternal(progress, handler.selector, { state: 'hidden' }); + await this.mainFrame().waitForSelectorInternal(progress, handler.selector, false, { state: 'hidden' }); } else { progress.log(` locator handler has finished`); } diff --git a/packages/playwright-core/src/server/progress.ts b/packages/playwright-core/src/server/progress.ts index 70de43b944..d92c5ed226 100644 --- a/packages/playwright-core/src/server/progress.ts +++ b/packages/playwright-core/src/server/progress.ts @@ -18,7 +18,6 @@ import { TimeoutError } from './errors'; import { assert, monotonicTime } from '../utils'; import type { LogName } from '../utils/debugLogger'; import type { CallMetadata, Instrumentation, SdkObject } from './instrumentation'; -import type { ElementHandle } from './dom'; import { ManualPromise } from '../utils/manualPromise'; export interface Progress { @@ -27,7 +26,6 @@ export interface Progress { isRunning(): boolean; cleanupWhenAborted(cleanup: () => any): void; throwIfAborted(): void; - beforeInputAction(element: ElementHandle): Promise<void>; metadata: CallMetadata; } @@ -89,9 +87,6 @@ export class ProgressController { if (this._state === 'aborted') throw new AbortedError(); }, - beforeInputAction: async (element: ElementHandle) => { - await this.instrumentation.onBeforeInputAction(this.sdkObject, this.metadata, element); - }, metadata: this.metadata }; diff --git a/packages/playwright-core/src/server/recorder.ts b/packages/playwright-core/src/server/recorder.ts index ddaa035811..4aab712b9b 100644 --- a/packages/playwright-core/src/server/recorder.ts +++ b/packages/playwright-core/src/server/recorder.ts @@ -27,7 +27,9 @@ import { Debugger } from './debugger'; import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation'; import { ContextRecorder, generateFrameSelector } from './recorder/contextRecorder'; import type { IRecorderAppFactory, IRecorderApp, IRecorder } from './recorder/recorderFrontend'; -import { buildFullSelector, metadataToCallLog } from './recorder/recorderUtils'; +import { metadataToCallLog } from './recorder/recorderUtils'; +import type * as actions from '@recorder/actions'; +import { buildFullSelector } from '../utils/isomorphic/recorderUtils'; const recorderSymbol = Symbol('recorderSymbol'); @@ -45,32 +47,35 @@ export class Recorder implements InstrumentationListener, IRecorder { private _omitCallTracking = false; private _currentLanguage: Language; - static showInspector(context: BrowserContext, recorderAppFactory: IRecorderAppFactory) { - const params: channels.BrowserContextRecorderSupplementEnableParams = {}; + static async showInspector(context: BrowserContext, params: channels.BrowserContextEnableRecorderParams, recorderAppFactory: IRecorderAppFactory) { if (isUnderTest()) params.language = process.env.TEST_INSPECTOR_LANGUAGE; - Recorder.show(context, recorderAppFactory, params).catch(() => {}); + return await Recorder.show('actions', context, recorderAppFactory, params); } - static show(context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<Recorder> { + static showInspectorNoReply(context: BrowserContext, recorderAppFactory: IRecorderAppFactory) { + Recorder.showInspector(context, {}, recorderAppFactory).catch(() => {}); + } + + static show(codegenMode: 'actions' | 'trace-events', context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextEnableRecorderParams): Promise<Recorder> { let recorderPromise = (context as any)[recorderSymbol] as Promise<Recorder>; if (!recorderPromise) { - recorderPromise = Recorder._create(context, recorderAppFactory, params); + recorderPromise = Recorder._create(codegenMode, context, recorderAppFactory, params); (context as any)[recorderSymbol] = recorderPromise; } return recorderPromise; } - private static async _create(context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<Recorder> { - const recorder = new Recorder(context, params); + private static async _create(codegenMode: 'actions' | 'trace-events', context: BrowserContext, recorderAppFactory: IRecorderAppFactory, params: channels.BrowserContextEnableRecorderParams = {}): Promise<Recorder> { + const recorder = new Recorder(codegenMode, context, params); const recorderApp = await recorderAppFactory(recorder); await recorder._install(recorderApp); return recorder; } - constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams) { + constructor(codegenMode: 'actions' | 'trace-events', context: BrowserContext, params: channels.BrowserContextEnableRecorderParams) { this._mode = params.mode || 'none'; - this._contextRecorder = new ContextRecorder(context, params, {}); + this._contextRecorder = new ContextRecorder(codegenMode, context, params, {}); this._context = context; this._omitCallTracking = !!params.omitCallTracking; this._debugger = context.debugger(); @@ -132,8 +137,9 @@ export class Recorder implements InstrumentationListener, IRecorder { this._context.instrumentation.removeListener(this); this._recorderApp?.close().catch(() => {}); }); - this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[] }) => { + this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[], actions: actions.ActionInContext[] }) => { this._recorderSources = data.sources; + recorderApp.setActions(data.actions, data.sources); this._pushAllSources(); }); diff --git a/packages/playwright-core/src/server/recorder/DEPS.list b/packages/playwright-core/src/server/recorder/DEPS.list index f3bbfc23bf..85ae7c9152 100644 --- a/packages/playwright-core/src/server/recorder/DEPS.list +++ b/packages/playwright-core/src/server/recorder/DEPS.list @@ -5,7 +5,7 @@ ../isomorphic/** ../registry/** ../../common/ -../../generated/recorderSource.ts +../../generated/pollingRecorderSource.ts ../../protocol/ ../../utils/** ../../utilsBundle.ts diff --git a/packages/playwright-core/src/server/recorder/contextRecorder.ts b/packages/playwright-core/src/server/recorder/contextRecorder.ts index 71a1d3ec75..933a036233 100644 --- a/packages/playwright-core/src/server/recorder/contextRecorder.ts +++ b/packages/playwright-core/src/server/recorder/contextRecorder.ts @@ -17,16 +17,16 @@ import type * as channels from '@protocol/channels'; import type { Source } from '@recorder/recorderTypes'; import { EventEmitter } from 'events'; -import * as recorderSource from '../../generated/recorderSource'; +import * as recorderSource from '../../generated/pollingRecorderSource'; import { eventsHelper, monotonicTime, quoteCSSAttributeValue, type RegisteredListener } from '../../utils'; import { raceAgainstDeadline } from '../../utils/timeoutRunner'; import { BrowserContext } from '../browserContext'; -import type { ActionInContext, FrameDescription, LanguageGeneratorOptions, Language, LanguageGenerator } from '../codegen/types'; +import type { LanguageGeneratorOptions, Language, LanguageGenerator } from '../codegen/types'; import { languageSet } from '../codegen/languages'; import type { Dialog } from '../dialog'; import { Frame } from '../frames'; import { Page } from '../page'; -import type * as actions from './recorderActions'; +import type * as actions from '@recorder/actions'; import { ThrottledFile } from './throttledFile'; import { RecorderCollection } from './recorderCollection'; import { generateCode } from '../codegen/language'; @@ -34,7 +34,7 @@ import { generateCode } from '../codegen/language'; type BindingSource = { frame: Frame, page: Page }; export interface ContextRecorderDelegate { - rewriteActionInContext?(pageAliases: Map<Page, string>, actionInContext: ActionInContext): Promise<void>; + rewriteActionInContext?(pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext): Promise<void>; } export class ContextRecorder extends EventEmitter { @@ -48,15 +48,17 @@ export class ContextRecorder extends EventEmitter { private _lastDialogOrdinal = -1; private _lastDownloadOrdinal = -1; private _context: BrowserContext; - private _params: channels.BrowserContextRecorderSupplementEnableParams; + private _params: channels.BrowserContextEnableRecorderParams; private _delegate: ContextRecorderDelegate; private _recorderSources: Source[]; private _throttledOutputFile: ThrottledFile | null = null; private _orderedLanguages: LanguageGenerator[] = []; private _listeners: RegisteredListener[] = []; + private _codegenMode: 'actions' | 'trace-events'; - constructor(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams, delegate: ContextRecorderDelegate) { + constructor(codegenMode: 'actions' | 'trace-events', context: BrowserContext, params: channels.BrowserContextEnableRecorderParams, delegate: ContextRecorderDelegate) { super(); + this._codegenMode = codegenMode; this._context = context; this._params = params; this._delegate = delegate; @@ -73,11 +75,11 @@ export class ContextRecorder extends EventEmitter { saveStorage: params.saveStorage, }; - const collection = new RecorderCollection(this._pageAliases, params.mode === 'recording'); - collection.on('change', () => { + this._collection = new RecorderCollection(this._pageAliases); + this._collection.on('change', (actions: actions.ActionInContext[]) => { this._recorderSources = []; for (const languageGenerator of this._orderedLanguages) { - const { header, footer, actionTexts, text } = generateCode(collection.actions(), languageGenerator, languageGeneratorOptions); + const { header, footer, actionTexts, text } = generateCode(actions, languageGenerator, languageGeneratorOptions); const source: Source = { isRecorded: true, label: languageGenerator.name, @@ -95,7 +97,10 @@ export class ContextRecorder extends EventEmitter { if (languageGenerator === this._orderedLanguages[0]) this._throttledOutputFile?.setContent(source.text); } - this.emit(ContextRecorder.Events.Change, { sources: this._recorderSources }); + this.emit(ContextRecorder.Events.Change, { + sources: this._recorderSources, + actions + }); }); context.on(BrowserContext.Events.BeforeClose, () => { this._throttledOutputFile?.flush(); @@ -103,7 +108,7 @@ export class ContextRecorder extends EventEmitter { this._listeners.push(eventsHelper.addEventListener(process, 'exit', () => { this._throttledOutputFile?.flush(); })); - this._collection = collection; + this.setEnabled(true); } setOutput(codegenId: string, outputFile?: string) { @@ -145,6 +150,12 @@ export class ContextRecorder extends EventEmitter { setEnabled(enabled: boolean) { this._collection.setEnabled(enabled); + if (this._codegenMode === 'trace-events') { + if (enabled) + this._context.tracing.startChunk({ name: 'trace', title: 'trace' }).catch(() => {}); + else + this._context.tracing.stopChunk({ mode: 'discard' }).catch(() => {}); + } } dispose() { @@ -161,7 +172,7 @@ export class ContextRecorder extends EventEmitter { name: 'closePage', signals: [], }, - timestamp: monotonicTime() + startTime: monotonicTime() }); this._pageAliases.delete(page); }); @@ -184,7 +195,7 @@ export class ContextRecorder extends EventEmitter { url: page.mainFrame().url(), signals: [], }, - timestamp: monotonicTime() + startTime: monotonicTime() }); } } @@ -197,14 +208,14 @@ export class ContextRecorder extends EventEmitter { } } - private _describeMainFrame(page: Page): FrameDescription { + private _describeMainFrame(page: Page): actions.FrameDescription { return { pageAlias: this._pageAliases.get(page)!, framePath: [], }; } - private async _describeFrame(frame: Frame): Promise<FrameDescription> { + private async _describeFrame(frame: Frame): Promise<actions.FrameDescription> { return { pageAlias: this._pageAliases.get(frame._page)!, framePath: await generateFrameSelector(frame), @@ -215,13 +226,13 @@ export class ContextRecorder extends EventEmitter { return this._params.testIdAttributeName || this._context.selectors().testIdAttributeName() || 'data-testid'; } - private async _createActionInContext(frame: Frame, action: actions.Action): Promise<ActionInContext> { + private async _createActionInContext(frame: Frame, action: actions.Action): Promise<actions.ActionInContext> { const frameDescription = await this._describeFrame(frame); - const actionInContext: ActionInContext = { + const actionInContext: actions.ActionInContext = { frame: frameDescription, action, description: undefined, - timestamp: monotonicTime() + startTime: monotonicTime() }; await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext); return actionInContext; diff --git a/packages/playwright-core/src/server/recorder/recorderApp.ts b/packages/playwright-core/src/server/recorder/recorderApp.ts index 67d8b6e8dc..30149f9816 100644 --- a/packages/playwright-core/src/server/recorder/recorderApp.ts +++ b/packages/playwright-core/src/server/recorder/recorderApp.ts @@ -20,29 +20,17 @@ import type { Page } from '../page'; import { ProgressController } from '../progress'; import { EventEmitter } from 'events'; import { serverSideCallMetadata } from '../instrumentation'; -import type { CallLog, EventData, Mode, Source } from '@recorder/recorderTypes'; +import type { CallLog, Mode, Source } from '@recorder/recorderTypes'; import { isUnderTest } from '../../utils'; import { mime } from '../../utilsBundle'; import { syncLocalStorageWithSettings } from '../launchApp'; import type { BrowserContext } from '../browserContext'; import { launchApp } from '../launchApp'; import type { IRecorder, IRecorderApp, IRecorderAppFactory } from './recorderFrontend'; - -declare global { - interface Window { - playwrightSetFile: (file: string) => void; - playwrightSetMode: (mode: Mode) => void; - playwrightSetPaused: (paused: boolean) => void; - playwrightSetSources: (sources: Source[]) => void; - playwrightSetOverlayVisible: (visible: boolean) => void; - playwrightSetSelector: (selector: string, focus?: boolean) => void; - playwrightUpdateLogs: (callLogs: CallLog[]) => void; - dispatch(data: EventData): Promise<void>; - saveSettings?(): Promise<void>; - } -} +import type * as actions from '@recorder/actions'; export class EmptyRecorderApp extends EventEmitter implements IRecorderApp { + wsEndpointForTest: undefined; async close(): Promise<void> {} async setPaused(paused: boolean): Promise<void> {} async setMode(mode: Mode): Promise<void> {} @@ -50,11 +38,12 @@ export class EmptyRecorderApp extends EventEmitter implements IRecorderApp { async setSelector(selector: string, userGesture?: boolean): Promise<void> {} async updateCallLogs(callLogs: CallLog[]): Promise<void> {} async setSources(sources: Source[]): Promise<void> {} + async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void> {} } export class RecorderApp extends EventEmitter implements IRecorderApp { private _page: Page; - readonly wsEndpoint: string | undefined; + readonly wsEndpointForTest: string | undefined; private _recorder: IRecorder; constructor(recorder: IRecorder, page: Page, wsEndpoint: string | undefined) { @@ -62,7 +51,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { this.setMaxListeners(0); this._recorder = recorder; this._page = page; - this.wsEndpoint = wsEndpoint; + this.wsEndpointForTest = wsEndpoint; } async close() { @@ -80,7 +69,6 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { const file = require.resolve('../../vite/recorder/' + uri); fs.promises.readFile(file).then(buffer => { route.fulfill({ - requestUrl: route.request().url(), status: 200, headers: [ { name: 'Content-Type', value: mime.getType(path.extname(file)) || 'application/octet-stream' } @@ -122,9 +110,8 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { persistentContextOptions: { noDefaultViewport: true, headless: !!process.env.PWTEST_CLI_HEADLESS || (isUnderTest() && !headed), - useWebSocket: !!process.env.PWTEST_RECORDER_PORT, + useWebSocket: isUnderTest(), handleSIGINT: false, - args: process.env.PWTEST_RECORDER_PORT ? [`--remote-debugging-port=${process.env.PWTEST_RECORDER_PORT}`] : [], executablePath: inspectedContext._browser.options.isChromium ? inspectedContext._browser.options.customExecutablePath : undefined, } }); @@ -162,8 +149,13 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { }).toString(), { isFunction: true }, sources).catch(() => {}); // Testing harness for runCLI mode. - if (process.env.PWTEST_CLI_IS_UNDER_TEST && sources.length) - (process as any)._didSetSourcesForTest(sources[0].text); + if (process.env.PWTEST_CLI_IS_UNDER_TEST && sources.length) { + if ((process as any)._didSetSourcesForTest(sources[0].text)) + this.close(); + } + } + + async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void> { } async setSelector(selector: string, userGesture?: boolean): Promise<void> { diff --git a/packages/playwright-core/src/server/recorder/recorderCollection.ts b/packages/playwright-core/src/server/recorder/recorderCollection.ts index e9c2b31427..b5216d7382 100644 --- a/packages/playwright-core/src/server/recorder/recorderCollection.ts +++ b/packages/playwright-core/src/server/recorder/recorderCollection.ts @@ -17,67 +17,70 @@ import { EventEmitter } from 'events'; import type { Frame } from '../frames'; import type { Page } from '../page'; -import type { Signal } from './recorderActions'; -import type { ActionInContext } from '../codegen/types'; +import type { Signal } from '../../../../recorder/src/actions'; +import type * as actions from '@recorder/actions'; import { monotonicTime } from '../../utils/time'; -import { callMetadataForAction } from './recorderUtils'; +import { callMetadataForAction, collapseActions } from './recorderUtils'; import { serializeError } from '../errors'; import { performAction } from './recorderRunner'; import type { CallMetadata } from '@protocol/callMetadata'; import { isUnderTest } from '../../utils/debug'; export class RecorderCollection extends EventEmitter { - private _actions: ActionInContext[] = []; - private _enabled: boolean; + private _actions: actions.ActionInContext[] = []; + private _enabled = false; private _pageAliases: Map<Page, string>; - constructor(pageAliases: Map<Page, string>, enabled: boolean) { + constructor(pageAliases: Map<Page, string>) { super(); - this._enabled = enabled; this._pageAliases = pageAliases; - this.restart(); } restart() { this._actions = []; - this.emit('change'); - } - - actions() { - return this._actions; + this._fireChange(); } setEnabled(enabled: boolean) { this._enabled = enabled; } - async performAction(actionInContext: ActionInContext) { + async performAction(actionInContext: actions.ActionInContext) { await this._addAction(actionInContext, async callMetadata => { await performAction(callMetadata, this._pageAliases, actionInContext); }); } - addRecordedAction(actionInContext: ActionInContext) { + addRecordedAction(actionInContext: actions.ActionInContext) { if (['openPage', 'closePage'].includes(actionInContext.action.name)) { this._actions.push(actionInContext); - this.emit('change'); + this._fireChange(); return; } this._addAction(actionInContext).catch(() => {}); } - private async _addAction(actionInContext: ActionInContext, callback?: (callMetadata: CallMetadata) => Promise<void>) { + private async _addAction(actionInContext: actions.ActionInContext, callback?: (callMetadata: CallMetadata) => Promise<void>) { if (!this._enabled) return; + if (actionInContext.action.name === 'openPage' || actionInContext.action.name === 'closePage') { + this._actions.push(actionInContext); + this._fireChange(); + return; + } const { callMetadata, mainFrame } = callMetadataForAction(this._pageAliases, actionInContext); await mainFrame.instrumentation.onBeforeCall(mainFrame, callMetadata); this._actions.push(actionInContext); - this.emit('change'); + this._fireChange(); const error = await callback?.(callMetadata).catch((e: Error) => e); callMetadata.endTime = monotonicTime(); + actionInContext.endTime = callMetadata.endTime; callMetadata.error = error ? serializeError(error) : undefined; - await mainFrame.instrumentation.onAfterCall(mainFrame, callMetadata); + // Do not wait for onAfterCall so that performAction returned immediately after the action. + mainFrame.instrumentation.onAfterCall(mainFrame, callMetadata).then(() => { + this._fireChange(); + }).catch(() => {}); } signal(pageAlias: string, frame: Frame, signal: Signal) { @@ -94,7 +97,7 @@ export class RecorderCollection extends EventEmitter { generateGoto = true; else if (lastAction.action.name !== 'click' && lastAction.action.name !== 'press') generateGoto = true; - else if (timestamp - lastAction.timestamp > signalThreshold) + else if (timestamp - lastAction.startTime > signalThreshold) generateGoto = true; if (generateGoto) { @@ -108,7 +111,8 @@ export class RecorderCollection extends EventEmitter { url: frame.url(), signals: [], }, - timestamp + startTime: timestamp, + endTime: timestamp, }); } return; @@ -116,8 +120,14 @@ export class RecorderCollection extends EventEmitter { if (this._actions.length) { this._actions[this._actions.length - 1].action.signals.push(signal); - this.emit('change'); + this._fireChange(); return; } } + + private _fireChange() { + if (!this._enabled) + return; + this.emit('change', collapseActions(this._actions)); + } } diff --git a/packages/playwright-core/src/server/recorder/recorderFrontend.ts b/packages/playwright-core/src/server/recorder/recorderFrontend.ts index 162c9f9964..97df1d3ceb 100644 --- a/packages/playwright-core/src/server/recorder/recorderFrontend.ts +++ b/packages/playwright-core/src/server/recorder/recorderFrontend.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import type * as actions from '@recorder/actions'; import type { CallLog, Mode, Source } from '@recorder/recorderTypes'; import type { EventEmitter } from 'events'; @@ -23,6 +24,7 @@ export interface IRecorder { } export interface IRecorderApp extends EventEmitter { + readonly wsEndpointForTest: string | undefined; close(): Promise<void>; setPaused(paused: boolean): Promise<void>; setMode(mode: Mode): Promise<void>; @@ -30,6 +32,7 @@ export interface IRecorderApp extends EventEmitter { setSelector(selector: string, userGesture?: boolean): Promise<void>; updateCallLogs(callLogs: CallLog[]): Promise<void>; setSources(sources: Source[]): Promise<void>; + setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void>; } export type IRecorderAppFactory = (recorder: IRecorder) => Promise<IRecorderApp>; diff --git a/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts b/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts index a9fd766141..ab67fe562c 100644 --- a/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts +++ b/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts @@ -21,74 +21,106 @@ import type { IRecorder, IRecorderApp, IRecorderAppFactory } from './recorderFro import { installRootRedirect, openTraceViewerApp, startTraceViewerServer } from '../trace/viewer/traceViewer'; import type { TraceViewerServerOptions } from '../trace/viewer/traceViewer'; import type { BrowserContext } from '../browserContext'; -import { gracefullyProcessExitDoNotHang } from '../../utils/processLauncher'; -import type { Transport } from '../../utils/httpServer'; +import type { HttpServer, Transport } from '../../utils/httpServer'; +import type { Page } from '../page'; +import { ManualPromise } from '../../utils/manualPromise'; +import type * as actions from '@recorder/actions'; export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp { - private _recorder: IRecorder; - private _transport: Transport; + readonly wsEndpointForTest: string | undefined; + private _transport: RecorderTransport; + private _tracePage: Page; + private _traceServer: HttpServer; static factory(context: BrowserContext): IRecorderAppFactory { return async (recorder: IRecorder) => { const transport = new RecorderTransport(); const trace = path.join(context._browser.options.tracesDir, 'trace'); - await openApp(trace, { transport }); - return new RecorderInTraceViewer(context, recorder, transport); + const { wsEndpointForTest, tracePage, traceServer } = await openApp(trace, { transport, headless: !context._browser.options.headful }); + return new RecorderInTraceViewer(transport, tracePage, traceServer, wsEndpointForTest); }; } - constructor(context: BrowserContext, recorder: IRecorder, transport: Transport) { + constructor(transport: RecorderTransport, tracePage: Page, traceServer: HttpServer, wsEndpointForTest: string | undefined) { super(); - this._recorder = recorder; this._transport = transport; + this._transport.eventSink.resolve(this); + this._tracePage = tracePage; + this._traceServer = traceServer; + this.wsEndpointForTest = wsEndpointForTest; + this._tracePage.once('close', () => { + this.close(); + }); } async close(): Promise<void> { - this._transport.sendEvent?.('close', {}); + await this._tracePage.context().close({ reason: 'Recorder window closed' }); + await this._traceServer.stop(); } async setPaused(paused: boolean): Promise<void> { - this._transport.sendEvent?.('setPaused', { paused }); + this._transport.deliverEvent('setPaused', { paused }); } async setMode(mode: Mode): Promise<void> { - this._transport.sendEvent?.('setMode', { mode }); + this._transport.deliverEvent('setMode', { mode }); } async setFile(file: string): Promise<void> { - this._transport.sendEvent?.('setFileIfNeeded', { file }); + this._transport.deliverEvent('setFileIfNeeded', { file }); } async setSelector(selector: string, userGesture?: boolean): Promise<void> { - this._transport.sendEvent?.('setSelector', { selector, userGesture }); + this._transport.deliverEvent('setSelector', { selector, userGesture }); } async updateCallLogs(callLogs: CallLog[]): Promise<void> { - this._transport.sendEvent?.('updateCallLogs', { callLogs }); + this._transport.deliverEvent('updateCallLogs', { callLogs }); } async setSources(sources: Source[]): Promise<void> { - this._transport.sendEvent?.('setSources', { sources }); + this._transport.deliverEvent('setSources', { sources }); + if (process.env.PWTEST_CLI_IS_UNDER_TEST && sources.length) { + if ((process as any)._didSetSourcesForTest(sources[0].text)) + this.close(); + } + } + + async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void> { + this._transport.deliverEvent('setActions', { actions, sources }); } } -async function openApp(trace: string, options?: TraceViewerServerOptions & { headless?: boolean }) { - const server = await startTraceViewerServer(options); - await installRootRedirect(server, [trace], { ...options, webApp: 'recorder.html' }); - const page = await openTraceViewerApp(server.urlPrefix('precise'), 'chromium', options); - page.on('close', () => gracefullyProcessExitDoNotHang(0)); +async function openApp(trace: string, options?: TraceViewerServerOptions & { headless?: boolean }): Promise<{ wsEndpointForTest: string | undefined, tracePage: Page, traceServer: HttpServer }> { + const traceServer = await startTraceViewerServer(options); + await installRootRedirect(traceServer, [trace], { ...options, webApp: 'recorder.html' }); + const page = await openTraceViewerApp(traceServer.urlPrefix('precise'), 'chromium', options); + return { wsEndpointForTest: page.context()._browser.options.wsEndpoint, tracePage: page, traceServer }; } class RecorderTransport implements Transport { + private _connected = new ManualPromise<void>(); + readonly eventSink = new ManualPromise<EventEmitter>(); + constructor() { } - async dispatch(method: string, params: any) { + onconnect() { + this._connected.resolve(); + } + + async dispatch(method: string, params: any): Promise<any> { + const eventSink = await this.eventSink; + eventSink.emit('event', { event: method, params }); } onclose() { } + deliverEvent(method: string, params: any) { + this._connected.then(() => this.sendEvent?.(method, params)); + } + sendEvent?: (method: string, params: any) => void; close?: () => void; } diff --git a/packages/playwright-core/src/server/recorder/recorderRunner.ts b/packages/playwright-core/src/server/recorder/recorderRunner.ts index f5358d6097..b0f476ffb1 100644 --- a/packages/playwright-core/src/server/recorder/recorderRunner.ts +++ b/packages/playwright-core/src/server/recorder/recorderRunner.ts @@ -16,14 +16,14 @@ import { serializeExpectedTextValues } from '../../utils'; import { toKeyboardModifiers } from '../codegen/language'; -import type { ActionInContext } from '../codegen/types'; import type { CallMetadata } from '../instrumentation'; import type { Page } from '../page'; -import type * as actions from './recorderActions'; +import type * as actions from '@recorder/actions'; import type * as types from '../types'; -import { buildFullSelector, mainFrameForAction } from './recorderUtils'; +import { mainFrameForAction } from './recorderUtils'; +import { buildFullSelector } from '../../utils/isomorphic/recorderUtils'; -export async function performAction(callMetadata: CallMetadata, pageAliases: Map<Page, string>, actionInContext: ActionInContext) { +export async function performAction(callMetadata: CallMetadata, pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext) { const mainFrame = mainFrameForAction(pageAliases, actionInContext); const { action } = actionInContext; diff --git a/packages/playwright-core/src/server/recorder/recorderUtils.ts b/packages/playwright-core/src/server/recorder/recorderUtils.ts index ac6c970489..c40a6ac201 100644 --- a/packages/playwright-core/src/server/recorder/recorderUtils.ts +++ b/packages/playwright-core/src/server/recorder/recorderUtils.ts @@ -17,12 +17,10 @@ import type { CallMetadata } from '../instrumentation'; import type { CallLog, CallLogStatus } from '@recorder/recorderTypes'; import type { Page } from '../page'; -import type { ActionInContext } from '../codegen/types'; import type { Frame } from '../frames'; -import type * as actions from './recorderActions'; -import { toKeyboardModifiers } from '../codegen/language'; -import { serializeExpectedTextValues } from '../../utils/expectUtils'; -import { createGuid, monotonicTime } from '../../utils'; +import type * as actions from '@recorder/actions'; +import { createGuid } from '../../utils'; +import { buildFullSelector, traceParamsForAction } from '../../utils/isomorphic/recorderUtils'; export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog { let title = metadata.apiName || metadata.method; @@ -52,11 +50,7 @@ export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus) return callLog; } -export function buildFullSelector(framePath: string[], selector: string) { - return [...framePath, selector].join(' >> internal:control=enter-frame >> '); -} - -export function mainFrameForAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext): Frame { +export function mainFrameForAction(pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext): Frame { const pageAlias = actionInContext.frame.pageAlias; const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0]; if (!page) @@ -64,7 +58,7 @@ export function mainFrameForAction(pageAliases: Map<Page, string>, actionInConte return page.mainFrame(); } -export async function frameForAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext, action: actions.ActionWithSelector): Promise<Frame> { +export async function frameForAction(pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext, action: actions.ActionWithSelector): Promise<Frame> { const pageAlias = actionInContext.frame.pageAlias; const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0]; if (!page) @@ -76,76 +70,40 @@ export async function frameForAction(pageAliases: Map<Page, string>, actionInCon return result.frame; } -export function traceParamsForAction(actionInContext: ActionInContext) { - const { action } = actionInContext; - - switch (action.name) { - case 'navigate': return { url: action.url }; - case 'openPage': return {}; - case 'closePage': return {}; - } - - const selector = buildFullSelector(actionInContext.frame.framePath, action.selector); - switch (action.name) { - case 'click': return { selector, clickCount: action.clickCount }; - case 'press': { - const modifiers = toKeyboardModifiers(action.modifiers); - const shortcut = [...modifiers, action.key].join('+'); - return { selector, key: shortcut }; - } - case 'fill': return { selector, text: action.text }; - case 'setInputFiles': return { selector, files: action.files }; - case 'check': return { selector }; - case 'uncheck': return { selector }; - case 'select': return { selector, values: action.options.map(value => ({ value })) }; - case 'assertChecked': { - return { - selector, - expression: 'to.be.checked', - isNot: !action.checked, - }; - } - case 'assertText': { - return { - selector, - expression: 'to.have.text', - expectedText: serializeExpectedTextValues([action.text], { matchSubstring: true, normalizeWhiteSpace: true }), - isNot: false, - }; - } - case 'assertValue': { - return { - selector, - expression: 'to.have.value', - expectedValue: action.value, - isNot: false, - }; - } - case 'assertVisible': { - return { - selector, - expression: 'to.be.visible', - isNot: false, - }; - } - } -} - -export function callMetadataForAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext): { callMetadata: CallMetadata, mainFrame: Frame } { +export function callMetadataForAction(pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext): { callMetadata: CallMetadata, mainFrame: Frame } { const mainFrame = mainFrameForAction(pageAliases, actionInContext); - const { action } = actionInContext; + const { method, params } = traceParamsForAction(actionInContext); + const callMetadata: CallMetadata = { id: `call@${createGuid()}`, - apiName: 'frame.' + action.name, + apiName: 'page.' + method, objectId: mainFrame.guid, pageId: mainFrame._page.guid, frameId: mainFrame.guid, - startTime: monotonicTime(), + startTime: actionInContext.startTime, endTime: 0, type: 'Frame', - method: action.name, - params: traceParamsForAction(actionInContext), + method, + params, log: [], }; return { callMetadata, mainFrame }; } + +export function collapseActions(actions: actions.ActionInContext[]): actions.ActionInContext[] { + const result: actions.ActionInContext[] = []; + for (const action of actions) { + const lastAction = result[result.length - 1]; + const isSameAction = lastAction && lastAction.action.name === action.action.name && lastAction.frame.pageAlias === action.frame.pageAlias && lastAction.frame.framePath.join('|') === action.frame.framePath.join('|'); + const isSameSelector = lastAction && 'selector' in lastAction.action && 'selector' in action.action && action.action.selector === lastAction.action.selector; + const shouldMerge = isSameAction && (action.action.name === 'navigate' || (action.action.name === 'fill' && isSameSelector)); + if (!shouldMerge) { + result.push(action); + continue; + } + const startTime = result[result.length - 1].startTime; + result[result.length - 1] = action; + result[result.length - 1].startTime = startTime; + } + return result; +} diff --git a/packages/playwright-core/src/server/registry/index.ts b/packages/playwright-core/src/server/registry/index.ts index 4e942967a4..7778d81218 100644 --- a/packages/playwright-core/src/server/registry/index.ts +++ b/packages/playwright-core/src/server/registry/index.ts @@ -100,6 +100,8 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { 'mac13-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', 'mac14': 'builds/chromium/%s/chromium-mac.zip', 'mac14-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', + 'mac15': 'builds/chromium/%s/chromium-mac.zip', + 'mac15-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip', 'win64': 'builds/chromium/%s/chromium-win64.zip', }, 'chromium-tip-of-tree': { @@ -127,6 +129,8 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { 'mac13-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', 'mac14': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', 'mac14-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', + 'mac15': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip', + 'mac15-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip', 'win64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-win64.zip', }, 'firefox': { @@ -154,6 +158,8 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { 'mac13-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip', 'mac14': 'builds/firefox/%s/firefox-mac.zip', 'mac14-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip', + 'mac15': 'builds/firefox/%s/firefox-mac.zip', + 'mac15-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip', 'win64': 'builds/firefox/%s/firefox-win64.zip', }, 'firefox-beta': { @@ -181,6 +187,8 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { 'mac13-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip', 'mac14': 'builds/firefox-beta/%s/firefox-beta-mac.zip', 'mac14-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip', + 'mac15': 'builds/firefox-beta/%s/firefox-beta-mac.zip', + 'mac15-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip', 'win64': 'builds/firefox-beta/%s/firefox-beta-win64.zip', }, 'webkit': { @@ -208,6 +216,8 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { 'mac13-arm64': 'builds/webkit/%s/webkit-mac-13-arm64.zip', 'mac14': 'builds/webkit/%s/webkit-mac-14.zip', 'mac14-arm64': 'builds/webkit/%s/webkit-mac-14-arm64.zip', + 'mac15': 'builds/webkit/%s/webkit-mac-15.zip', + 'mac15-arm64': 'builds/webkit/%s/webkit-mac-15-arm64.zip', 'win64': 'builds/webkit/%s/webkit-win64.zip', }, 'ffmpeg': { @@ -235,6 +245,8 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { 'mac13-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip', 'mac14': 'builds/ffmpeg/%s/ffmpeg-mac.zip', 'mac14-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip', + 'mac15': 'builds/ffmpeg/%s/ffmpeg-mac.zip', + 'mac15-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip', 'win64': 'builds/ffmpeg/%s/ffmpeg-win64.zip', }, 'android': { @@ -262,6 +274,8 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { 'mac13-arm64': 'builds/android/%s/android.zip', 'mac14': 'builds/android/%s/android.zip', 'mac14-arm64': 'builds/android/%s/android.zip', + 'mac15': 'builds/android/%s/android.zip', + 'mac15-arm64': 'builds/android/%s/android.zip', 'win64': 'builds/android/%s/android.zip', }, // TODO(bidi): implement downloads. diff --git a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts index 08aaa4f25a..b041ffb829 100644 --- a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts +++ b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts @@ -346,7 +346,7 @@ function rewriteToLocalhostIfNeeded(host: string): string { } export function rewriteOpenSSLErrorIfNeeded(error: Error): Error { - if (error.message !== 'unsupported') + if (error.message !== 'unsupported' && (error as NodeJS.ErrnoException).code !== 'ERR_CRYPTO_UNSUPPORTED_OPERATION') return error; return rewriteErrorMessage(error, [ 'Unsupported TLS certificate.', diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts index d552c397e0..115cd3dd94 100644 --- a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts +++ b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts @@ -24,7 +24,6 @@ import type { SnapshotData } from './snapshotterInjected'; import { frameSnapshotStreamer } from './snapshotterInjected'; import { calculateSha1, createGuid, monotonicTime } from '../../../utils'; import type { FrameSnapshot } from '@trace/snapshot'; -import type { ElementHandle } from '../../dom'; import { mime } from '../../../utilsBundle'; export type SnapshotterBlob = { @@ -105,21 +104,10 @@ export class Snapshotter { eventsHelper.removeEventListeners(this._eventListeners); } - async captureSnapshot(page: Page, callId: string, snapshotName: string, element?: ElementHandle): Promise<void> { + async captureSnapshot(page: Page, callId: string, snapshotName: string): Promise<void> { // Prepare expression synchronously. const expression = `window["${this._snapshotStreamer}"].captureSnapshot(${JSON.stringify(snapshotName)})`; - // In a best-effort manner, without waiting for it, mark target element. - element?.callFunctionNoReply((element: Element, callId: string) => { - const customEvent = new CustomEvent('__playwright_target__', { - bubbles: true, - cancelable: true, - detail: callId, - composed: true, - }); - element.dispatchEvent(customEvent); - }, callId); - // In each frame, in a non-stalling manner, capture the snapshots. const snapshots = page.frames().map(async frame => { const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => debugLogger.log('error', e)) as SnapshotData; diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts index e881d52313..47385c8323 100644 --- a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts +++ b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts @@ -139,12 +139,19 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript: } private _refreshListeners() { - (document as any).addEventListener('__playwright_target__', (event: CustomEvent) => { + (document as any).addEventListener('__playwright_mark_target__', (event: CustomEvent) => { if (!event.detail) return; const callId = event.detail as string; (event.composedPath()[0] as any).__playwright_target__ = callId; }); + (document as any).addEventListener('__playwright_unmark_target__', (event: CustomEvent) => { + if (!event.detail) + return; + const callId = event.detail as string; + if ((event.composedPath()[0] as any).__playwright_target__ === callId) + delete (event.composedPath()[0] as any).__playwright_target__; + }); } private _interceptNativeMethod(obj: any, method: string, cb: (thisObj: any, result: any) => void) { diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index b09bbe3134..97476d4b31 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -23,7 +23,6 @@ import { commandsWithTracingSnapshots } from '../../../protocol/debug'; import { assert, createGuid, monotonicTime, SerializedFS, removeFolders, eventsHelper, type RegisteredListener } from '../../../utils'; import { Artifact } from '../../artifact'; import { BrowserContext } from '../../browserContext'; -import type { ElementHandle } from '../../dom'; import type { APIRequestContext } from '../../fetch'; import type { CallMetadata, InstrumentationListener } from '../../instrumentation'; import { SdkObject } from '../../instrumentation'; @@ -38,6 +37,8 @@ import { Snapshotter } from './snapshotter'; import type { ConsoleMessage } from '../../console'; import { Dispatcher } from '../../dispatchers/dispatcher'; import { serializeError } from '../../errors'; +import type { Dialog } from '../../dialog'; +import type { Download } from '../../download'; const version: trace.VERSION = 7; @@ -179,7 +180,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps wallTime: Date.now(), monotonicTime: monotonicTime() }; - this._fs.appendFile(this._state.traceFile, JSON.stringify(event) + '\n'); + this._appendTraceEvent(event); this._context.instrumentation.addListener(this, this._context); this._eventListeners.push( @@ -339,7 +340,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps return { artifact }; } - async _captureSnapshot(snapshotName: string, sdkObject: SdkObject, metadata: CallMetadata, element?: ElementHandle): Promise<void> { + async _captureSnapshot(snapshotName: string, sdkObject: SdkObject, metadata: CallMetadata): Promise<void> { if (!this._snapshotter) return; if (!sdkObject.attribution.page) @@ -348,7 +349,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps return; if (!shouldCaptureSnapshot(metadata)) return; - await this._snapshotter.captureSnapshot(sdkObject.attribution.page, metadata.id, snapshotName, element).catch(() => {}); + await this._snapshotter.captureSnapshot(sdkObject.attribution.page, metadata.id, snapshotName).catch(() => {}); } onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata) { @@ -363,7 +364,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps return this._captureSnapshot(event.beforeSnapshot, sdkObject, metadata); } - onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata, element: ElementHandle) { + onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata) { if (!this._state?.callIds.has(metadata.id)) return Promise.resolve(); // IMPORTANT: no awaits before this._appendTraceEvent in this method. @@ -373,7 +374,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps sdkObject.attribution.page?.temporarilyDisableTracingScreencastThrottling(); event.inputSnapshot = `input@${metadata.id}`; this._appendTraceEvent(event); - return this._captureSnapshot(event.inputSnapshot, sdkObject, metadata, element); + return this._captureSnapshot(event.inputSnapshot, sdkObject, metadata); } onCallLog(sdkObject: SdkObject, metadata: CallMetadata, logName: string, message: string) { @@ -447,6 +448,50 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps this._appendTraceEvent(event); } + onDialog(dialog: Dialog) { + const event: trace.EventTraceEvent = { + type: 'event', + time: monotonicTime(), + class: 'BrowserContext', + method: 'dialog', + params: { pageId: dialog.page().guid, type: dialog.type(), message: dialog.message(), defaultValue: dialog.defaultValue() }, + }; + this._appendTraceEvent(event); + } + + onDownload(page: Page, download: Download) { + const event: trace.EventTraceEvent = { + type: 'event', + time: monotonicTime(), + class: 'BrowserContext', + method: 'download', + params: { pageId: page.guid, url: download.url, suggestedFilename: download.suggestedFilename() }, + }; + this._appendTraceEvent(event); + } + + onPageOpen(page: Page) { + const event: trace.EventTraceEvent = { + type: 'event', + time: monotonicTime(), + class: 'BrowserContext', + method: 'page', + params: { pageId: page.guid, openerPageId: page.opener()?.guid }, + }; + this._appendTraceEvent(event); + } + + onPageClose(page: Page) { + const event: trace.EventTraceEvent = { + type: 'event', + time: monotonicTime(), + class: 'BrowserContext', + method: 'pageClosed', + params: { pageId: page.guid }, + }; + this._appendTraceEvent(event); + } + private _onPageError(error: Error, page: Page) { const event: trace.EventTraceEvent = { type: 'event', diff --git a/packages/playwright-core/src/server/trace/test/inMemorySnapshotter.ts b/packages/playwright-core/src/server/trace/test/inMemorySnapshotter.ts index af07910159..f28c5ef03b 100644 --- a/packages/playwright-core/src/server/trace/test/inMemorySnapshotter.ts +++ b/packages/playwright-core/src/server/trace/test/inMemorySnapshotter.ts @@ -17,11 +17,10 @@ import type { BrowserContext } from '../../browserContext'; import type { Page } from '../../page'; import type { FrameSnapshot } from '@trace/snapshot'; -import type { SnapshotRenderer } from '../../../../../trace-viewer/src/snapshotRenderer'; -import { SnapshotStorage } from '../../../../../trace-viewer/src/snapshotStorage'; +import type { SnapshotRenderer } from '../../../../../trace-viewer/src/sw/snapshotRenderer'; +import { SnapshotStorage } from '../../../../../trace-viewer/src/sw/snapshotStorage'; import type { SnapshotterBlob, SnapshotterDelegate } from '../recorder/snapshotter'; import { Snapshotter } from '../recorder/snapshotter'; -import type { ElementHandle } from '../../dom'; import type { HarTracerDelegate } from '../../har/harTracer'; import { HarTracer } from '../../har/harTracer'; import type * as har from '@trace/har'; @@ -59,11 +58,11 @@ export class InMemorySnapshotter implements SnapshotterDelegate, HarTracerDelega this._harTracer.stop(); } - async captureSnapshot(page: Page, callId: string, snapshotName: string, element?: ElementHandle): Promise<SnapshotRenderer> { + async captureSnapshot(page: Page, callId: string, snapshotName: string): Promise<SnapshotRenderer> { if (this._snapshotReadyPromises.has(snapshotName)) throw new Error('Duplicate snapshot name: ' + snapshotName); - this._snapshotter.captureSnapshot(page, callId, snapshotName, element).catch(() => {}); + this._snapshotter.captureSnapshot(page, callId, snapshotName).catch(() => {}); const promise = new ManualPromise<SnapshotRenderer>(); this._snapshotReadyPromises.set(snapshotName, promise); return promise; diff --git a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts index 6ca0319aa3..a49148e061 100644 --- a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts +++ b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts @@ -40,14 +40,9 @@ export type TraceViewerRedirectOptions = { grep?: string; grepInvert?: string; project?: string[]; - workers?: number | string; - headed?: boolean; - timeout?: number; reporter?: string[]; webApp?: string; isServer?: boolean; - outputDir?: string; - updateSnapshots?: 'all' | 'none' | 'missing'; }; export type TraceViewerAppOptions = { @@ -127,16 +122,6 @@ export async function installRootRedirect(server: HttpServer, traceUrls: string[ params.append('grepInvert', options.grepInvert); for (const project of options.project || []) params.append('project', project); - if (options.workers) - params.append('workers', String(options.workers)); - if (options.timeout) - params.append('timeout', String(options.timeout)); - if (options.headed) - params.append('headed', ''); - if (options.outputDir) - params.append('outputDir', options.outputDir); - if (options.updateSnapshots) - params.append('updateSnapshots', options.updateSnapshots); for (const reporter of options.reporter || []) params.append('reporter', reporter); @@ -223,6 +208,9 @@ class StdinServer implements Transport { process.stdin.on('close', () => gracefullyProcessExitDoNotHang(0)); } + onconnect() { + } + async dispatch(method: string, params: any) { if (method === 'initialize') { if (this._traceUrl) diff --git a/packages/playwright-core/src/server/webkit/wkExecutionContext.ts b/packages/playwright-core/src/server/webkit/wkExecutionContext.ts index 8d6bbff084..52b8ba3677 100644 --- a/packages/playwright-core/src/server/webkit/wkExecutionContext.ts +++ b/packages/playwright-core/src/server/webkit/wkExecutionContext.ts @@ -60,16 +60,6 @@ export class WKExecutionContext implements js.ExecutionContextDelegate { } } - rawCallFunctionNoReply(func: Function, ...args: any[]) { - this._session.send('Runtime.callFunctionOn', { - functionDeclaration: func.toString(), - objectId: args.find(a => a instanceof js.JSHandle)!._objectId!, - arguments: args.map(a => a instanceof js.JSHandle ? { objectId: a._objectId } : { value: a }), - returnByValue: true, - emulateUserGesture: true - }).catch(() => {}); - } - async evaluateWithArguments(expression: string, returnByValue: boolean, utilityScript: js.JSHandle<any>, values: any[], objectIds: string[]): Promise<any> { try { const response = await this._session.send('Runtime.callFunctionOn', { @@ -116,10 +106,6 @@ export class WKExecutionContext implements js.ExecutionContextDelegate { async releaseHandle(objectId: js.ObjectId): Promise<void> { await this._session.send('Runtime.releaseObject', { objectId }); } - - objectCount(objectId: js.ObjectId): Promise<number> { - throw new Error('Method not implemented in WebKit.'); - } } function potentiallyUnserializableValue(remoteObject: Protocol.Runtime.RemoteObject): any { diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 2f579b619b..3ba773241a 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -502,16 +502,15 @@ export class WKPage implements PageDelegate { if (!frame) return; const delegate = new WKExecutionContext(this._session, contextPayload.id); - let worldName: types.World; + let worldName: types.World|null = null; if (contextPayload.type === 'normal') worldName = 'main'; else if (contextPayload.type === 'user' && contextPayload.name === UTILITY_WORLD_NAME) worldName = 'utility'; - else - return; const context = new dom.FrameExecutionContext(delegate, frame, worldName); (context as any)[contextDelegateSymbol] = delegate; - frame._contextCreated(worldName, context); + if (worldName) + frame._contextCreated(worldName, context); this._contextIdToContext.set(contextPayload.id, context); } @@ -768,7 +767,7 @@ export class WKPage implements PageDelegate { }); } - async forceGarbageCollection(): Promise<void> { + async requestGC(): Promise<void> { await this._session.send('Heap.gc'); } @@ -1116,7 +1115,7 @@ export class WKPage implements PageDelegate { const response = request.createResponse(event.response); this._page._frameManager.requestReceivedResponse(response); - if (response.status() === 204) { + if (response.status() === 204 && request.request.isNavigationRequest()) { this._onLoadingFailed(session, { requestId: event.requestId, errorText: 'Aborted: 204 No Content', diff --git a/packages/playwright-core/src/utils/happy-eyeballs.ts b/packages/playwright-core/src/utils/happy-eyeballs.ts index 12f082e5a1..18de1a938c 100644 --- a/packages/playwright-core/src/utils/happy-eyeballs.ts +++ b/packages/playwright-core/src/utils/happy-eyeballs.ts @@ -21,6 +21,7 @@ import * as net from 'net'; import * as tls from 'tls'; import { ManualPromise } from './manualPromise'; import { assert } from './debug'; +import { monotonicTime } from './time'; // Implementation(partial) of Happy Eyeballs 2 algorithm described in // https://www.rfc-editor.org/rfc/rfc8305 @@ -28,6 +29,9 @@ import { assert } from './debug'; // Same as in Chromium (https://source.chromium.org/chromium/chromium/src/+/5666ff4f5077a7e2f72902f3a95f5d553ea0d88d:net/socket/transport_connect_job.cc;l=102) const connectionAttemptDelayMs = 300; +const kDNSLookupAt = Symbol('kDNSLookupAt') +const kTCPConnectionAt = Symbol('kTCPConnectionAt') + class HttpHappyEyeballsAgent extends http.Agent { createConnection(options: http.ClientRequestArgs, oncreate?: (err: Error | null, socket?: net.Socket) => void): net.Socket | undefined { // There is no ambiguity in case of IP address. @@ -107,6 +111,7 @@ export async function createConnectionAsync( const lookup = (options as any).__testHookLookup || lookupAddresses; const hostname = clientRequestArgsToHostName(options); const addresses = await lookup(hostname); + const dnsLookupAt = monotonicTime(); const sockets = new Set<net.Socket>(); let firstError; let errorCount = 0; @@ -132,9 +137,13 @@ export async function createConnectionAsync( port: options.port as number, host: address }); + (socket as any)[kDNSLookupAt] = dnsLookupAt; + // Each socket may fire only one of 'connect', 'timeout' or 'error' events. // None of these events are fired after socket.destroy() is called. socket.on('connect', () => { + (socket as any)[kTCPConnectionAt] = monotonicTime(); + connected.resolve(); oncreate?.(null, socket); // TODO: Cache the result? @@ -189,3 +198,9 @@ function clientRequestArgsToHostName(options: http.ClientRequestArgs): string { throw new Error('Either options.hostname or options.host must be provided'); } +export function timingForSocket(socket: net.Socket | tls.TLSSocket) { + return { + dnsLookupAt: (socket as any)[kDNSLookupAt] as number | undefined, + tcpConnectionAt: (socket as any)[kTCPConnectionAt] as number | undefined, + } +} diff --git a/packages/playwright-core/src/utils/hostPlatform.ts b/packages/playwright-core/src/utils/hostPlatform.ts index faf65bd082..9664418e3d 100644 --- a/packages/playwright-core/src/utils/hostPlatform.ts +++ b/packages/playwright-core/src/utils/hostPlatform.ts @@ -25,6 +25,7 @@ export type HostPlatform = 'win64' | 'mac12' | 'mac12-arm64' | 'mac13' | 'mac13-arm64' | 'mac14' | 'mac14-arm64' | + 'mac15' | 'mac15-arm64' | 'ubuntu18.04-x64' | 'ubuntu18.04-arm64' | 'ubuntu20.04-x64' | 'ubuntu20.04-arm64' | 'ubuntu22.04-x64' | 'ubuntu22.04-arm64' | @@ -47,9 +48,9 @@ function calculatePlatform(): { hostPlatform: HostPlatform, isOfficiallySupporte macVersion = 'mac10.15'; } else { // ver[0] >= 20 - const LAST_STABLE_MAC_MAJOR_VERSION = 14; + const LAST_STABLE_MACOS_MAJOR_VERSION = 15; // Best-effort support for MacOS beta versions. - macVersion = 'mac' + Math.min(ver[0] - 9, LAST_STABLE_MAC_MAJOR_VERSION); + macVersion = 'mac' + Math.min(ver[0] - 9, LAST_STABLE_MACOS_MAJOR_VERSION); // BigSur is the first version that might run on Apple Silicon. if (os.cpus().some(cpu => cpu.model.includes('Apple'))) macVersion += '-arm64'; diff --git a/packages/playwright-core/src/utils/httpServer.ts b/packages/playwright-core/src/utils/httpServer.ts index 24a84ea502..8da2a0e0d0 100644 --- a/packages/playwright-core/src/utils/httpServer.ts +++ b/packages/playwright-core/src/utils/httpServer.ts @@ -27,8 +27,9 @@ export type ServerRouteHandler = (request: http.IncomingMessage, response: http. export type Transport = { sendEvent?: (method: string, params: any) => void; - dispatch: (method: string, params: any) => Promise<any>; close?: () => void; + onconnect: () => void; + dispatch: (method: string, params: any) => Promise<any>; onclose: () => void; }; @@ -82,6 +83,7 @@ export class HttpServer { this._wsGuid = guid || createGuid(); const wss = new wsServer({ server: this._server, path: '/' + this._wsGuid }); wss.on('connection', ws => { + transport.onconnect(); transport.sendEvent = (method, params) => ws.send(JSON.stringify({ method, params })); transport.close = () => ws.close(); ws.on('message', async message => { diff --git a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts index e51fa0b623..56252d02d3 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts @@ -50,16 +50,6 @@ export function asLocators(lang: Language, selector: string, isFrameLocator: boo function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFrameLocator: boolean = false, maxOutputSize = 20): string[] { const parts = [...parsed.parts]; - // frameLocator('iframe').first is actually "iframe >> nth=0 >> internal:control=enter-frame" - // To make it easier to parse, we turn it into "iframe >> internal:control=enter-frame >> nth=0" - for (let index = 0; index < parts.length - 1; index++) { - if (parts[index].name === 'nth' && parts[index + 1].name === 'internal:control' && (parts[index + 1].body as string) === 'enter-frame') { - // Swap nth and enter-frame. - const [nth] = parts.splice(index, 1); - parts.splice(index + 1, 0, nth); - } - } - const tokens: string[][] = []; let nextBase: LocatorBase = isFrameLocator ? 'frame-locator' : 'page'; for (let index = 0; index < parts.length; index++) { @@ -167,15 +157,15 @@ function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFram continue; } } + if (part.name === 'internal:control' && (part.body as string) === 'enter-frame') { + tokens.push([factory.generateLocator(base, 'frame', '')]); + nextBase = 'frame-locator'; + continue; + } let locatorType: LocatorType = 'default'; const nextPart = parts[index + 1]; - if (nextPart && nextPart.name === 'internal:control' && (nextPart.body as string) === 'enter-frame') { - locatorType = 'frame'; - nextBase = 'frame-locator'; - index++; - } const selectorPart = stringifySelector({ parts: [part] }); const locatorPart = factory.generateLocator(base, locatorType, selectorPart); @@ -264,7 +254,7 @@ export class JavaScriptLocatorFactory implements LocatorFactory { return `locator(${this.quote(body as string)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`; return `locator(${this.quote(body as string)})`; case 'frame': - return `frameLocator(${this.quote(body as string)})`; + return `contentFrame()`; case 'nth': return `nth(${body})`; case 'first': @@ -356,7 +346,7 @@ export class PythonLocatorFactory implements LocatorFactory { return `locator(${this.quote(body as string)}, has_not_text=${this.toHasText(options.hasNotText)})`; return `locator(${this.quote(body as string)})`; case 'frame': - return `frame_locator(${this.quote(body as string)})`; + return `content_frame`; case 'nth': return `nth(${body})`; case 'first': @@ -461,7 +451,7 @@ export class JavaLocatorFactory implements LocatorFactory { return `locator(${this.quote(body as string)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options.hasNotText)}))`; return `locator(${this.quote(body as string)})`; case 'frame': - return `frameLocator(${this.quote(body as string)})`; + return `contentFrame()`; case 'nth': return `nth(${body})`; case 'first': @@ -556,7 +546,7 @@ export class CSharpLocatorFactory implements LocatorFactory { return `Locator(${this.quote(body as string)}, new() { ${this.toHasNotText(options.hasNotText)} })`; return `Locator(${this.quote(body as string)})`; case 'frame': - return `FrameLocator(${this.quote(body as string)})`; + return `ContentFrame`; case 'nth': return `Nth(${body})`; case 'first': diff --git a/packages/playwright-core/src/utils/isomorphic/locatorParser.ts b/packages/playwright-core/src/utils/isomorphic/locatorParser.ts index 2ad5fd29c6..e0c10b53fd 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorParser.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorParser.ts @@ -75,6 +75,7 @@ function parseLocator(locator: string, testIdAttributeName: string): { selector: .replace(/has_text/g, 'hastext') .replace(/has_not/g, 'hasnot') .replace(/frame_locator/g, 'framelocator') + .replace(/content_frame/g, 'contentframe') .replace(/[{}\s]/g, '') .replace(/new\(\)/g, '') .replace(/new[\w]+\.[\w]+options\(\)/g, '') @@ -154,6 +155,7 @@ function transform(template: string, params: TemplateParams, testIdAttributeName template = template .replace(/\,set([\w]+)\(([^)]+)\)/g, (_, group1, group2) => ',' + group1.toLowerCase() + '=' + group2.toLowerCase()) .replace(/framelocator\(([^)]+)\)/g, '$1.internal:control=enter-frame') + .replace(/contentframe(\(\))?/g, 'internal:control=enter-frame') .replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2') .replace(/locator\(([^)]+),hasnottext=([^),]+)\)/g, 'locator($1).internal:has-not-text=$2') .replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2') diff --git a/packages/playwright-core/src/utils/isomorphic/recorderUtils.ts b/packages/playwright-core/src/utils/isomorphic/recorderUtils.ts new file mode 100644 index 0000000000..40ce3acd44 --- /dev/null +++ b/packages/playwright-core/src/utils/isomorphic/recorderUtils.ts @@ -0,0 +1,147 @@ +/** + * 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. + */ + +import type * as recorderActions from '@recorder/actions'; +import type * as channels from '@protocol/channels'; +import type * as types from '../../server/types'; + +export function buildFullSelector(framePath: string[], selector: string) { + return [...framePath, selector].join(' >> internal:control=enter-frame >> '); +} + +export function traceParamsForAction(actionInContext: recorderActions.ActionInContext): { method: string, params: any } { + const { action } = actionInContext; + + switch (action.name) { + case 'navigate': { + const params: channels.FrameGotoParams = { + url: action.url, + }; + return { method: 'goto', params }; + } + case 'openPage': + case 'closePage': + throw new Error('Not reached'); + } + + const selector = buildFullSelector(actionInContext.frame.framePath, action.selector); + switch (action.name) { + case 'click': { + const params: channels.FrameClickParams = { + selector, + strict: true, + modifiers: toKeyboardModifiers(action.modifiers), + button: action.button, + clickCount: action.clickCount, + position: action.position, + }; + return { method: 'click', params }; + } + case 'press': { + const params: channels.FramePressParams = { + selector, + strict: true, + key: [...toKeyboardModifiers(action.modifiers), action.key].join('+'), + }; + return { method: 'press', params }; + } + case 'fill': { + const params: channels.FrameFillParams = { + selector, + strict: true, + value: action.text, + }; + return { method: 'fill', params }; + } + case 'setInputFiles': { + const params: channels.FrameSetInputFilesParams = { + selector, + strict: true, + localPaths: action.files, + }; + return { method: 'setInputFiles', params }; + } + case 'check': { + const params: channels.FrameCheckParams = { + selector, + strict: true, + }; + return { method: 'check', params }; + } + case 'uncheck': { + const params: channels.FrameUncheckParams = { + selector, + strict: true, + }; + return { method: 'uncheck', params }; + } + case 'select': { + const params: channels.FrameSelectOptionParams = { + selector, + strict: true, + options: action.options.map(option => ({ value: option })), + }; + return { method: 'selectOption', params }; + } + case 'assertChecked': { + const params: channels.FrameExpectParams = { + selector: action.selector, + expression: 'to.be.checked', + isNot: !action.checked, + }; + return { method: 'expect', params }; + } + case 'assertText': { + const params: channels.FrameExpectParams = { + selector, + expression: 'to.have.text', + expectedText: [], + isNot: false, + }; + return { method: 'expect', params }; + } + case 'assertValue': { + const params: channels.FrameExpectParams = { + selector, + expression: 'to.have.value', + expectedValue: undefined, + isNot: false, + }; + return { method: 'expect', params }; + } + case 'assertVisible': { + const params: channels.FrameExpectParams = { + selector, + expression: 'to.be.visible', + isNot: false, + }; + return { method: 'expect', params }; + } + } +} + +export function toKeyboardModifiers(modifiers: number): types.SmartKeyboardModifier[] { + const result: types.SmartKeyboardModifier[] = []; + if (modifiers & 1) + result.push('Alt'); + if (modifiers & 2) + result.push('ControlOrMeta'); + if (modifiers & 4) + result.push('ControlOrMeta'); + if (modifiers & 8) + result.push('Shift'); + return result; +} diff --git a/packages/playwright-core/types/protocol.d.ts b/packages/playwright-core/types/protocol.d.ts index caadb2a577..14416bde1e 100644 --- a/packages/playwright-core/types/protocol.d.ts +++ b/packages/playwright-core/types/protocol.d.ts @@ -840,7 +840,7 @@ CORS RFC1918 enforcement. resourceIPAddressSpace?: Network.IPAddressSpace; clientSecurityState?: Network.ClientSecurityState; } - export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"UntrustworthyReportingOrigin"|"InsecureContext"|"InvalidHeader"|"InvalidRegisterTriggerHeader"|"SourceAndTriggerHeaders"|"SourceIgnored"|"TriggerIgnored"|"OsSourceIgnored"|"OsTriggerIgnored"|"InvalidRegisterOsSourceHeader"|"InvalidRegisterOsTriggerHeader"|"WebAndOsHeaders"|"NoWebOrOsSupport"|"NavigationRegistrationWithoutTransientUserActivation"|"InvalidInfoHeader"|"NoRegisterSourceHeader"|"NoRegisterTriggerHeader"|"NoRegisterOsSourceHeader"|"NoRegisterOsTriggerHeader"; + export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"UntrustworthyReportingOrigin"|"InsecureContext"|"InvalidHeader"|"InvalidRegisterTriggerHeader"|"SourceAndTriggerHeaders"|"SourceIgnored"|"TriggerIgnored"|"OsSourceIgnored"|"OsTriggerIgnored"|"InvalidRegisterOsSourceHeader"|"InvalidRegisterOsTriggerHeader"|"WebAndOsHeaders"|"NoWebOrOsSupport"|"NavigationRegistrationWithoutTransientUserActivation"|"InvalidInfoHeader"|"NoRegisterSourceHeader"|"NoRegisterTriggerHeader"|"NoRegisterOsSourceHeader"|"NoRegisterOsTriggerHeader"|"NavigationRegistrationUniqueScopeAlreadySet"; export type SharedDictionaryError = "UseErrorCrossOriginNoCorsRequest"|"UseErrorDictionaryLoadFailure"|"UseErrorMatchingDictionaryNotUsed"|"UseErrorUnexpectedContentDictionaryHeader"|"WriteErrorCossOriginNoCorsRequest"|"WriteErrorDisallowedBySettings"|"WriteErrorExpiredResponse"|"WriteErrorFeatureDisabled"|"WriteErrorInsufficientResources"|"WriteErrorInvalidMatchField"|"WriteErrorInvalidStructuredHeader"|"WriteErrorNavigationRequest"|"WriteErrorNoMatchField"|"WriteErrorNonListMatchDestField"|"WriteErrorNonSecureContext"|"WriteErrorNonStringIdField"|"WriteErrorNonStringInMatchDestList"|"WriteErrorNonStringMatchField"|"WriteErrorNonTokenTypeField"|"WriteErrorRequestAborted"|"WriteErrorShuttingDown"|"WriteErrorTooLongIdField"|"WriteErrorUnsupportedType"; /** * Details for issues around "Attribution Reporting API" usage. @@ -1534,7 +1534,7 @@ events afterwards if enabled and recording. */ windowState?: WindowState; } - export type PermissionType = "accessibilityEvents"|"audioCapture"|"backgroundSync"|"backgroundFetch"|"capturedSurfaceControl"|"clipboardReadWrite"|"clipboardSanitizedWrite"|"displayCapture"|"durableStorage"|"flash"|"geolocation"|"idleDetection"|"localFonts"|"midi"|"midiSysex"|"nfc"|"notifications"|"paymentHandler"|"periodicBackgroundSync"|"protectedMediaIdentifier"|"sensors"|"storageAccess"|"speakerSelection"|"topLevelStorageAccess"|"videoCapture"|"videoCapturePanTiltZoom"|"wakeLockScreen"|"wakeLockSystem"|"windowManagement"; + export type PermissionType = "accessibilityEvents"|"audioCapture"|"backgroundSync"|"backgroundFetch"|"capturedSurfaceControl"|"clipboardReadWrite"|"clipboardSanitizedWrite"|"displayCapture"|"durableStorage"|"flash"|"geolocation"|"idleDetection"|"localFonts"|"midi"|"midiSysex"|"nfc"|"notifications"|"paymentHandler"|"periodicBackgroundSync"|"protectedMediaIdentifier"|"sensors"|"storageAccess"|"speakerSelection"|"topLevelStorageAccess"|"videoCapture"|"videoCapturePanTiltZoom"|"wakeLockScreen"|"wakeLockSystem"|"webAppInstallation"|"windowManagement"; export type PermissionSetting = "granted"|"denied"|"prompt"; /** * Definition of PermissionDescriptor defined in the Permissions API: @@ -3561,7 +3561,7 @@ front-end. /** * Pseudo element type. */ - export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scroll-next-button"|"scroll-prev-button"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new"; + export type PseudoType = "first-line"|"first-letter"|"before"|"after"|"marker"|"backdrop"|"column"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scroll-next-button"|"scroll-prev-button"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-old"|"view-transition-new"|"placeholder"|"file-selector-button"|"details-content"|"select-fallback-button"|"select-fallback-button-text"|"picker"; /** * Shadow root type. */ @@ -3710,6 +3710,7 @@ The property is always undefined now. isSVG?: boolean; compatibilityMode?: CompatibilityMode; assignedSlot?: BackendNode; + isScrollable?: boolean; } /** * A structure to hold the top-level node of a detached tree and an array of its retained descendants. @@ -3954,6 +3955,19 @@ The property is always undefined now. * Called when top layer elements are changed. */ export type topLayerElementsUpdatedPayload = void; + /** + * Fired when a node's scrollability state changes. + */ + export type scrollableFlagUpdatedPayload = { + /** + * The id of the node. + */ + nodeId: DOM.NodeId; + /** + * If the node is scrollable. + */ + isScrollable: boolean; + } /** * Called when a pseudo element is removed from an element. */ @@ -8102,8 +8116,25 @@ or hexadecimal (0x prefixed) string. */ size: number; } + /** + * DOM object counter data. + */ + export interface DOMCounter { + /** + * Object name. Note: object names should be presumed volatile and clients should not expect +the returned names to be consistent across runs. + */ + name: string; + /** + * Object count. + */ + count: number; + } + /** + * Retruns current DOM object counters. + */ export type getDOMCountersParameters = { } export type getDOMCountersReturnValue = { @@ -8111,6 +8142,21 @@ or hexadecimal (0x prefixed) string. nodes: number; jsEventListeners: number; } + /** + * Retruns DOM object counters after preparing renderer for leak detection. + */ + export type getDOMCountersForLeakDetectionParameters = { + } + export type getDOMCountersForLeakDetectionReturnValue = { + /** + * DOM object counters. + */ + counters: DOMCounter[]; + } + /** + * Prepares for leak detection by terminating workers, stopping spellcheckers, +dropping non-essential internal caches, running garbage collections, etc. + */ export type prepareForLeakDetectionParameters = { } export type prepareForLeakDetectionReturnValue = { @@ -8902,7 +8948,7 @@ This is a temporary ability and it will be removed in the future. /** * Types of reasons why a cookie should have been blocked by 3PCD but is exempted for the request. */ - export type CookieExemptionReason = "None"|"UserSetting"|"TPCDMetadata"|"TPCDDeprecationTrial"|"TPCDHeuristics"|"EnterprisePolicy"|"StorageAccess"|"TopLevelStorageAccess"|"CorsOptIn"|"Scheme"; + export type CookieExemptionReason = "None"|"UserSetting"|"TPCDMetadata"|"TPCDDeprecationTrial"|"TopLevelTPCDDeprecationTrial"|"TPCDHeuristics"|"EnterprisePolicy"|"StorageAccess"|"TopLevelStorageAccess"|"Scheme"; /** * A cookie which was not stored from a response with the corresponding reason. */ @@ -11452,7 +11498,7 @@ as an ad. * All Permissions Policy features. This enum should match the one defined in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5. */ - export type PermissionsPolicyFeature = "accelerometer"|"all-screens-capture"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"cross-origin-isolated"|"deferred-fetch"|"digital-credentials-get"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"media-playback-while-not-visible"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking"; + export type PermissionsPolicyFeature = "accelerometer"|"all-screens-capture"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"controlled-frame"|"cross-origin-isolated"|"deferred-fetch"|"digital-credentials-get"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"media-playback-while-not-visible"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"popins"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-app-installation"|"web-printing"|"web-share"|"window-management"|"xr-spatial-tracking"; /** * Reason for a permissions policy feature to be disabled. */ @@ -12040,7 +12086,7 @@ https://github.com/WICG/manifest-incubations/blob/gh-pages/scope_extensions-expl /** * List of not restored reasons for back-forward cache. */ - export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebViewSettingsChanged"|"WebViewJavaScriptObjectChanged"|"WebViewMessageListenerInjected"|"WebViewSafeBrowsingAllowlistChanged"|"WebViewDocumentStartJavascriptChanged"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame"|"RequestedByWebViewClient"; + export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebViewSettingsChanged"|"WebViewJavaScriptObjectChanged"|"WebViewMessageListenerInjected"|"WebViewSafeBrowsingAllowlistChanged"|"WebViewDocumentStartJavascriptChanged"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"ContentDiscarded"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame"|"RequestedByWebViewClient"; /** * Types of not restored reasons for back-forward cache. */ @@ -12151,6 +12197,16 @@ dependent on the reason: frameId: FrameId; reason: "remove"|"swap"; } + /** + * Fired before frame subtree is detached. Emitted before any frame of the +subtree is actually detached. + */ + export type frameSubtreeWillBeDetachedPayload = { + /** + * Id of the frame that is the root of the subtree that will be detached. + */ + frameId: FrameId; + } /** * Fired once navigation of the frame has completed. Frame is now associated with the new loader. */ @@ -14250,6 +14306,15 @@ int, only present for source registrations debugData: AttributionReportingAggregatableDebugReportingData[]; aggregationCoordinatorOrigin?: string; } + export interface AttributionScopesData { + values: string[]; + /** + * number instead of integer because not all uint32 can be represented by +int + */ + limit: number; + maxEventStates: number; + } export interface AttributionReportingSourceRegistration { time: Network.TimeSinceEpoch; /** @@ -14273,8 +14338,9 @@ int, only present for source registrations triggerDataMatching: AttributionReportingTriggerDataMatching; destinationLimitPriority: SignedInt64AsBase10; aggregatableDebugReportingConfig: AttributionReportingAggregatableDebugReportingConfig; + scopesData?: AttributionScopesData; } - export type AttributionReportingSourceRegistrationResult = "success"|"internalError"|"insufficientSourceCapacity"|"insufficientUniqueDestinationCapacity"|"excessiveReportingOrigins"|"prohibitedByBrowserPolicy"|"successNoised"|"destinationReportingLimitReached"|"destinationGlobalLimitReached"|"destinationBothLimitsReached"|"reportingOriginsPerSiteLimitReached"|"exceedsMaxChannelCapacity"|"exceedsMaxTriggerStateCardinality"|"destinationPerDayReportingLimitReached"; + export type AttributionReportingSourceRegistrationResult = "success"|"internalError"|"insufficientSourceCapacity"|"insufficientUniqueDestinationCapacity"|"excessiveReportingOrigins"|"prohibitedByBrowserPolicy"|"successNoised"|"destinationReportingLimitReached"|"destinationGlobalLimitReached"|"destinationBothLimitsReached"|"reportingOriginsPerSiteLimitReached"|"exceedsMaxChannelCapacity"|"exceedsMaxScopesChannelCapacity"|"exceedsMaxTriggerStateCardinality"|"exceedsMaxEventStatesLimit"|"destinationPerDayReportingLimitReached"; export type AttributionReportingSourceRegistrationTimeConfig = "include"|"exclude"; export interface AttributionReportingAggregatableValueDictEntry { key: string; @@ -14317,6 +14383,7 @@ int sourceRegistrationTimeConfig: AttributionReportingSourceRegistrationTimeConfig; triggerContextId?: string; aggregatableDebugReportingConfig: AttributionReportingAggregatableDebugReportingConfig; + scopes: string[]; } export type AttributionReportingEventLevelResult = "success"|"successDroppedLowerPriority"|"internalError"|"noCapacityForAttributionDestination"|"noMatchingSources"|"deduplicated"|"excessiveAttributions"|"priorityTooLow"|"neverAttributedSource"|"excessiveReportingOrigins"|"noMatchingSourceFilterData"|"prohibitedByBrowserPolicy"|"noMatchingConfigurations"|"excessiveReports"|"falselyAttributedSource"|"reportWindowPassed"|"notRegistered"|"reportWindowNotStarted"|"noMatchingTriggerData"; export type AttributionReportingAggregatableResult = "success"|"internalError"|"noCapacityForAttributionDestination"|"noMatchingSources"|"excessiveAttributions"|"excessiveReportingOrigins"|"noHistograms"|"insufficientBudget"|"noMatchingSourceFilterData"|"notRegistered"|"prohibitedByBrowserPolicy"|"deduplicated"|"reportWindowPassed"|"excessiveReports"; @@ -17019,7 +17086,7 @@ status is shared by prefetchStatusUpdated and prerenderStatusUpdated. * TODO(https://crbug.com/1384419): revisit the list of PrefetchStatus and filter out the ones that aren't necessary to the developers. */ - export type PrefetchStatus = "PrefetchAllowed"|"PrefetchFailedIneligibleRedirect"|"PrefetchFailedInvalidRedirect"|"PrefetchFailedMIMENotSupported"|"PrefetchFailedNetError"|"PrefetchFailedNon2XX"|"PrefetchFailedPerPageLimitExceeded"|"PrefetchEvictedAfterCandidateRemoved"|"PrefetchEvictedForNewerPrefetch"|"PrefetchHeldback"|"PrefetchIneligibleRetryAfter"|"PrefetchIsPrivacyDecoy"|"PrefetchIsStale"|"PrefetchNotEligibleBrowserContextOffTheRecord"|"PrefetchNotEligibleDataSaverEnabled"|"PrefetchNotEligibleExistingProxy"|"PrefetchNotEligibleHostIsNonUnique"|"PrefetchNotEligibleNonDefaultStoragePartition"|"PrefetchNotEligibleSameSiteCrossOriginPrefetchRequiredProxy"|"PrefetchNotEligibleSchemeIsNotHttps"|"PrefetchNotEligibleUserHasCookies"|"PrefetchNotEligibleUserHasServiceWorker"|"PrefetchNotEligibleBatterySaverEnabled"|"PrefetchNotEligiblePreloadingDisabled"|"PrefetchNotFinishedInTime"|"PrefetchNotStarted"|"PrefetchNotUsedCookiesChanged"|"PrefetchProxyNotAvailable"|"PrefetchResponseUsed"|"PrefetchSuccessfulButNotUsed"|"PrefetchNotUsedProbeFailed"; + export type PrefetchStatus = "PrefetchAllowed"|"PrefetchFailedIneligibleRedirect"|"PrefetchFailedInvalidRedirect"|"PrefetchFailedMIMENotSupported"|"PrefetchFailedNetError"|"PrefetchFailedNon2XX"|"PrefetchEvictedAfterCandidateRemoved"|"PrefetchEvictedForNewerPrefetch"|"PrefetchHeldback"|"PrefetchIneligibleRetryAfter"|"PrefetchIsPrivacyDecoy"|"PrefetchIsStale"|"PrefetchNotEligibleBrowserContextOffTheRecord"|"PrefetchNotEligibleDataSaverEnabled"|"PrefetchNotEligibleExistingProxy"|"PrefetchNotEligibleHostIsNonUnique"|"PrefetchNotEligibleNonDefaultStoragePartition"|"PrefetchNotEligibleSameSiteCrossOriginPrefetchRequiredProxy"|"PrefetchNotEligibleSchemeIsNotHttps"|"PrefetchNotEligibleUserHasCookies"|"PrefetchNotEligibleUserHasServiceWorker"|"PrefetchNotEligibleBatterySaverEnabled"|"PrefetchNotEligiblePreloadingDisabled"|"PrefetchNotFinishedInTime"|"PrefetchNotStarted"|"PrefetchNotUsedCookiesChanged"|"PrefetchProxyNotAvailable"|"PrefetchResponseUsed"|"PrefetchSuccessfulButNotUsed"|"PrefetchNotUsedProbeFailed"; /** * Information of headers to be displayed when the header mismatch occurred. */ @@ -20115,6 +20182,7 @@ Error was thrown. "DOM.inlineStyleInvalidated": DOM.inlineStyleInvalidatedPayload; "DOM.pseudoElementAdded": DOM.pseudoElementAddedPayload; "DOM.topLayerElementsUpdated": DOM.topLayerElementsUpdatedPayload; + "DOM.scrollableFlagUpdated": DOM.scrollableFlagUpdatedPayload; "DOM.pseudoElementRemoved": DOM.pseudoElementRemovedPayload; "DOM.setChildNodes": DOM.setChildNodesPayload; "DOM.shadowRootPopped": DOM.shadowRootPoppedPayload; @@ -20173,6 +20241,7 @@ Error was thrown. "Page.frameAttached": Page.frameAttachedPayload; "Page.frameClearedScheduledNavigation": Page.frameClearedScheduledNavigationPayload; "Page.frameDetached": Page.frameDetachedPayload; + "Page.frameSubtreeWillBeDetached": Page.frameSubtreeWillBeDetachedPayload; "Page.frameNavigated": Page.frameNavigatedPayload; "Page.documentOpened": Page.documentOpenedPayload; "Page.frameResized": Page.frameResizedPayload; @@ -20539,6 +20608,7 @@ Error was thrown. "Log.startViolationsReport": Log.startViolationsReportParameters; "Log.stopViolationsReport": Log.stopViolationsReportParameters; "Memory.getDOMCounters": Memory.getDOMCountersParameters; + "Memory.getDOMCountersForLeakDetection": Memory.getDOMCountersForLeakDetectionParameters; "Memory.prepareForLeakDetection": Memory.prepareForLeakDetectionParameters; "Memory.forciblyPurgeJavaScriptMemory": Memory.forciblyPurgeJavaScriptMemoryParameters; "Memory.setPressureNotificationsSuppressed": Memory.setPressureNotificationsSuppressedParameters; @@ -21148,6 +21218,7 @@ Error was thrown. "Log.startViolationsReport": Log.startViolationsReportReturnValue; "Log.stopViolationsReport": Log.stopViolationsReportReturnValue; "Memory.getDOMCounters": Memory.getDOMCountersReturnValue; + "Memory.getDOMCountersForLeakDetection": Memory.getDOMCountersForLeakDetectionReturnValue; "Memory.prepareForLeakDetection": Memory.prepareForLeakDetectionReturnValue; "Memory.forciblyPurgeJavaScriptMemory": Memory.forciblyPurgeJavaScriptMemoryReturnValue; "Memory.setPressureNotificationsSuppressed": Memory.setPressureNotificationsSuppressedReturnValue; diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index ad102f9271..1f165c3d93 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -29,9 +29,10 @@ type ElementHandleWaitForSelectorOptionsNotHidden = ElementHandleWaitForSelector }; /** - * Page provides methods to interact with a single tab in a {@link Browser}, or an - * [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium. One {@link - * Browser} instance might have multiple {@link Page} instances. + * Page provides methods to interact with a single tab in a [Browser](https://playwright.dev/docs/api/class-browser), + * or an [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium. One + * [Browser](https://playwright.dev/docs/api/class-browser) instance might have multiple + * [Page](https://playwright.dev/docs/api/class-page) instances. * * This example creates a page, navigates it to a URL, and then saves a screenshot: * @@ -72,7 +73,8 @@ type ElementHandleWaitForSelectorOptionsNotHidden = ElementHandleWaitForSelector */ export interface Page { /** - * Returns the value of the `pageFunction` invocation. + * Returns the value of the + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression) invocation. * * If the function passed to the * [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate) returns a [Promise], @@ -88,7 +90,7 @@ export interface Page { * * **Usage** * - * Passing argument to `pageFunction`: + * Passing argument to [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression): * * ```js * const result = await page.evaluate(([x, y]) => { @@ -105,7 +107,7 @@ export interface Page { * console.log(await page.evaluate(`1 + ${x}`)); // prints "11" * ``` * - * {@link ElementHandle} instances can be passed as an argument to the + * [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) instances can be passed as an argument to the * [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate): * * ```js @@ -117,11 +119,13 @@ export interface Page { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression). */ evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>; /** - * Returns the value of the `pageFunction` invocation. + * Returns the value of the + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression) invocation. * * If the function passed to the * [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate) returns a [Promise], @@ -137,7 +141,7 @@ export interface Page { * * **Usage** * - * Passing argument to `pageFunction`: + * Passing argument to [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression): * * ```js * const result = await page.evaluate(([x, y]) => { @@ -154,7 +158,7 @@ export interface Page { * console.log(await page.evaluate(`1 + ${x}`)); // prints "11" * ``` * - * {@link ElementHandle} instances can be passed as an argument to the + * [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) instances can be passed as an argument to the * [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate): * * ```js @@ -166,18 +170,21 @@ export interface Page { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-option-expression). */ evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<R>; /** - * Returns the value of the `pageFunction` invocation as a {@link JSHandle}. + * Returns the value of the + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-handle-option-expression) invocation as a + * [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * The only difference between * [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate) and * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) is that * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) returns - * {@link JSHandle}. + * [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function passed to the * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) returns @@ -198,7 +205,7 @@ export interface Page { * const aHandle = await page.evaluateHandle('document'); // Handle for the 'document' * ``` * - * {@link JSHandle} instances can be passed as an argument to the + * [JSHandle](https://playwright.dev/docs/api/class-jshandle) instances can be passed as an argument to the * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle): * * ```js @@ -209,17 +216,20 @@ export interface Page { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-handle-option-expression). */ evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>; /** - * Returns the value of the `pageFunction` invocation as a {@link JSHandle}. + * Returns the value of the + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-handle-option-expression) invocation as a + * [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * The only difference between * [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate) and * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) is that * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) returns - * {@link JSHandle}. + * [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function passed to the * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) returns @@ -240,7 +250,7 @@ export interface Page { * const aHandle = await page.evaluateHandle('document'); // Handle for the 'document' * ``` * - * {@link JSHandle} instances can be passed as an argument to the + * [JSHandle](https://playwright.dev/docs/api/class-jshandle) instances can be passed as an argument to the * [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle): * * ```js @@ -251,7 +261,8 @@ export interface Page { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-evaluate-handle-option-expression). */ evaluateHandle<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<SmartHandle<R>>; @@ -288,8 +299,11 @@ export interface Page { * [browserContext.addInitScript(script[, arg])](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script) * and [page.addInitScript(script[, arg])](https://playwright.dev/docs/api/class-page#page-add-init-script) is not * defined. + * * @param script Script to be evaluated in the page. - * @param arg Optional argument to pass to `script` (only supported when passing a function). + * @param arg Optional argument to pass to + * [`script`](https://playwright.dev/docs/api/class-page#page-add-init-script-option-script) (only supported when + * passing a function). */ addInitScript<Arg>(script: PageFunction<Arg, any> | { path?: string, content?: string }, arg?: Arg): Promise<void>; @@ -339,12 +353,15 @@ export interface Page { * **NOTE** This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. * Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * * The method finds an element matching the specified selector within the page and passes it as a first argument to - * `pageFunction`. If no elements match the selector, the method throws an error. Returns the value of `pageFunction`. + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). If no + * elements match the selector, the method throws an error. Returns the value of + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression) returns a + * [Promise], then * [page.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-page#page-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -360,7 +377,8 @@ export interface Page { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). * @param options */ $eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], Arg, R>, arg: Arg): Promise<R>; @@ -368,12 +386,15 @@ export interface Page { * **NOTE** This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. * Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * * The method finds an element matching the specified selector within the page and passes it as a first argument to - * `pageFunction`. If no elements match the selector, the method throws an error. Returns the value of `pageFunction`. + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). If no + * elements match the selector, the method throws an error. Returns the value of + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression) returns a + * [Promise], then * [page.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-page#page-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -389,7 +410,8 @@ export interface Page { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). * @param options */ $eval<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E, Arg, R>, arg: Arg): Promise<R>; @@ -397,12 +419,15 @@ export interface Page { * **NOTE** This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. * Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * * The method finds an element matching the specified selector within the page and passes it as a first argument to - * `pageFunction`. If no elements match the selector, the method throws an error. Returns the value of `pageFunction`. + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). If no + * elements match the selector, the method throws an error. Returns the value of + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression) returns a + * [Promise], then * [page.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-page#page-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -418,7 +443,8 @@ export interface Page { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). * @param options */ $eval<K extends keyof HTMLElementTagNameMap, R>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], void, R>, arg?: any): Promise<R>; @@ -426,12 +452,15 @@ export interface Page { * **NOTE** This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. * Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * * The method finds an element matching the specified selector within the page and passes it as a first argument to - * `pageFunction`. If no elements match the selector, the method throws an error. Returns the value of `pageFunction`. + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). If no + * elements match the selector, the method throws an error. Returns the value of + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression) returns a + * [Promise], then * [page.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-page#page-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -447,7 +476,8 @@ export interface Page { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-option-expression). * @param options */ $eval<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E, void, R>, arg?: any): Promise<R>; @@ -455,12 +485,18 @@ export interface Page { /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * * The method finds all elements matching the specified selector within the page and passes an array of matched - * elements as a first argument to `pageFunction`. Returns the result of `pageFunction` invocation. + * elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression). Returns + * the result of + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression) + * invocation. * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression) returns + * a [Promise], then * [page.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -472,18 +508,25 @@ export interface Page { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression). */ $$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], Arg, R>, arg: Arg): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * * The method finds all elements matching the specified selector within the page and passes an array of matched - * elements as a first argument to `pageFunction`. Returns the result of `pageFunction` invocation. + * elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression). Returns + * the result of + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression) + * invocation. * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression) returns + * a [Promise], then * [page.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -495,18 +538,25 @@ export interface Page { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression). */ $$eval<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E[], Arg, R>, arg: Arg): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * * The method finds all elements matching the specified selector within the page and passes an array of matched - * elements as a first argument to `pageFunction`. Returns the result of `pageFunction` invocation. + * elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression). Returns + * the result of + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression) + * invocation. * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression) returns + * a [Promise], then * [page.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -518,18 +568,25 @@ export interface Page { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression). */ $$eval<K extends keyof HTMLElementTagNameMap, R>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], void, R>, arg?: any): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * * The method finds all elements matching the specified selector within the page and passes an array of matched - * elements as a first argument to `pageFunction`. Returns the result of `pageFunction` invocation. + * elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression). Returns + * the result of + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression) + * invocation. * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression) returns + * a [Promise], then * [page.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -541,12 +598,15 @@ export interface Page { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-eval-on-selector-all-option-expression). */ $$eval<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E[], void, R>, arg?: any): Promise<R>; /** - * Returns when the `pageFunction` returns a truthy value. It resolves to a JSHandle of the truthy value. + * Returns when the + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-wait-for-function-option-expression) returns a + * truthy value. It resolves to a JSHandle of the truthy value. * * **Usage** * @@ -577,12 +637,15 @@ export interface Page { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-wait-for-function-option-expression). * @param options */ waitForFunction<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: PageWaitForFunctionOptions): Promise<SmartHandle<R>>; /** - * Returns when the `pageFunction` returns a truthy value. It resolves to a JSHandle of the truthy value. + * Returns when the + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-wait-for-function-option-expression) returns a + * truthy value. It resolves to a JSHandle of the truthy value. * * **Usage** * @@ -613,7 +676,8 @@ export interface Page { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-wait-for-function-option-expression). * @param options */ waitForFunction<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: PageWaitForFunctionOptions): Promise<SmartHandle<R>>; @@ -623,15 +687,21 @@ export interface Page { * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. Read more * about [locators](https://playwright.dev/docs/locators). * - * Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns when element specified by selector satisfies + * [`state`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-state) option. Returns `null` if + * waiting for `hidden` or `detached`. * - * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using {@link Locator} - * objects and web-first assertions makes the code wait-for-selector-free. + * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions makes the code + * wait-for-selector-free. * - * Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If - * at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. - * If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw. + * Wait for the [`selector`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-selector) to + * satisfy [`state`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-state) option (either + * appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-selector) already satisfies + * the condition, the method will return immediately. If the selector doesn't satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-timeout) milliseconds, the + * function will throw. * * **Usage** * @@ -661,15 +731,21 @@ export interface Page { * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. Read more * about [locators](https://playwright.dev/docs/locators). * - * Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns when element specified by selector satisfies + * [`state`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-state) option. Returns `null` if + * waiting for `hidden` or `detached`. * - * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using {@link Locator} - * objects and web-first assertions makes the code wait-for-selector-free. + * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions makes the code + * wait-for-selector-free. * - * Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If - * at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. - * If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw. + * Wait for the [`selector`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-selector) to + * satisfy [`state`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-state) option (either + * appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-selector) already satisfies + * the condition, the method will return immediately. If the selector doesn't satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-timeout) milliseconds, the + * function will throw. * * **Usage** * @@ -699,15 +775,21 @@ export interface Page { * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. Read more * about [locators](https://playwright.dev/docs/locators). * - * Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns when element specified by selector satisfies + * [`state`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-state) option. Returns `null` if + * waiting for `hidden` or `detached`. * - * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using {@link Locator} - * objects and web-first assertions makes the code wait-for-selector-free. + * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions makes the code + * wait-for-selector-free. * - * Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If - * at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. - * If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw. + * Wait for the [`selector`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-selector) to + * satisfy [`state`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-state) option (either + * appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-selector) already satisfies + * the condition, the method will return immediately. If the selector doesn't satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-timeout) milliseconds, the + * function will throw. * * **Usage** * @@ -737,15 +819,21 @@ export interface Page { * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. Read more * about [locators](https://playwright.dev/docs/locators). * - * Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns when element specified by selector satisfies + * [`state`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-state) option. Returns `null` if + * waiting for `hidden` or `detached`. * - * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using {@link Locator} - * objects and web-first assertions makes the code wait-for-selector-free. + * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions makes the code + * wait-for-selector-free. * - * Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If - * at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. - * If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw. + * Wait for the [`selector`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-selector) to + * satisfy [`state`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-state) option (either + * appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-selector) already satisfies + * the condition, the method will return immediately. If the selector doesn't satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-page#page-wait-for-selector-option-timeout) milliseconds, the + * function will throw. * * **Usage** * @@ -772,12 +860,18 @@ export interface Page { waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise<null|ElementHandle<SVGElement | HTMLElement>>; /** - * The method adds a function called `name` on the `window` object of every frame in this page. When called, the - * function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. If the - * `callback` returns a [Promise], it will be awaited. + * The method adds a function called + * [`name`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-name) on the `window` object of + * every frame in this page. When called, the function executes + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-callback) and returns a + * [Promise] which resolves to the return value of + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-callback). If the + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-callback) returns a [Promise], + * it will be awaited. * - * The first argument of the `callback` function contains information about the caller: `{ browserContext: - * BrowserContext, page: Page, frame: Frame }`. + * The first argument of the + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-callback) function contains + * information about the caller: `{ browserContext: BrowserContext, page: Page, frame: Frame }`. * * See * [browserContext.exposeBinding(name, callback[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding) @@ -818,12 +912,18 @@ export interface Page { */ exposeBinding(name: string, playwrightBinding: (source: BindingSource, arg: JSHandle) => any, options: { handle: true }): Promise<void>; /** - * The method adds a function called `name` on the `window` object of every frame in this page. When called, the - * function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. If the - * `callback` returns a [Promise], it will be awaited. + * The method adds a function called + * [`name`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-name) on the `window` object of + * every frame in this page. When called, the function executes + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-callback) and returns a + * [Promise] which resolves to the return value of + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-callback). If the + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-callback) returns a [Promise], + * it will be awaited. * - * The first argument of the `callback` function contains information about the caller: `{ browserContext: - * BrowserContext, page: Page, frame: Frame }`. + * The first argument of the + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-binding-option-callback) function contains + * information about the caller: `{ browserContext: BrowserContext, page: Page, frame: Frame }`. * * See * [browserContext.exposeBinding(name, callback[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding) @@ -922,7 +1022,8 @@ export interface Page { /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. * - * The arguments passed into `console.log` are available on the {@link ConsoleMessage} event handler argument. + * The arguments passed into `console.log` are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -975,6 +1076,7 @@ export interface Page { * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. + * */ on(event: 'dialog', listener: (dialog: Dialog) => any): this; @@ -986,7 +1088,7 @@ export interface Page { /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the - * passed {@link Download} instance. + * passed [Download](https://playwright.dev/docs/api/class-download) instance. */ on(event: 'download', listener: (download: Download) => any): this; @@ -1053,7 +1155,7 @@ export interface Page { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * and * [browserContext.on('request')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request) - * respectively instead of similar methods on the {@link Page}. + * respectively instead of similar methods on the [Page](https://playwright.dev/docs/api/class-page). * * ```js * // Start waiting for popup before clicking. Note no await. @@ -1066,6 +1168,7 @@ export interface Page { * **NOTE** Use * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). + * */ on(event: 'popup', listener: (page: Page) => any): this; @@ -1091,6 +1194,7 @@ export interface Page { * with [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#page-event-request-failed). A request * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. + * */ on(event: 'requestfailed', listener: (request: Request) => any): this; @@ -1107,7 +1211,7 @@ export interface Page { on(event: 'response', listener: (response: Response) => any): this; /** - * Emitted when {@link WebSocket} request is sent. + * Emitted when [WebSocket](https://playwright.dev/docs/api/class-websocket) request is sent. */ on(event: 'websocket', listener: (webSocket: WebSocket) => any): this; @@ -1220,7 +1324,8 @@ export interface Page { /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. * - * The arguments passed into `console.log` are available on the {@link ConsoleMessage} event handler argument. + * The arguments passed into `console.log` are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -1273,6 +1378,7 @@ export interface Page { * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. + * */ addListener(event: 'dialog', listener: (dialog: Dialog) => any): this; @@ -1284,7 +1390,7 @@ export interface Page { /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the - * passed {@link Download} instance. + * passed [Download](https://playwright.dev/docs/api/class-download) instance. */ addListener(event: 'download', listener: (download: Download) => any): this; @@ -1351,7 +1457,7 @@ export interface Page { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * and * [browserContext.on('request')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request) - * respectively instead of similar methods on the {@link Page}. + * respectively instead of similar methods on the [Page](https://playwright.dev/docs/api/class-page). * * ```js * // Start waiting for popup before clicking. Note no await. @@ -1364,6 +1470,7 @@ export interface Page { * **NOTE** Use * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). + * */ addListener(event: 'popup', listener: (page: Page) => any): this; @@ -1389,6 +1496,7 @@ export interface Page { * with [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#page-event-request-failed). A request * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. + * */ addListener(event: 'requestfailed', listener: (request: Request) => any): this; @@ -1405,7 +1513,7 @@ export interface Page { addListener(event: 'response', listener: (response: Response) => any): this; /** - * Emitted when {@link WebSocket} request is sent. + * Emitted when [WebSocket](https://playwright.dev/docs/api/class-websocket) request is sent. */ addListener(event: 'websocket', listener: (webSocket: WebSocket) => any): this; @@ -1613,7 +1721,8 @@ export interface Page { /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. * - * The arguments passed into `console.log` are available on the {@link ConsoleMessage} event handler argument. + * The arguments passed into `console.log` are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -1666,6 +1775,7 @@ export interface Page { * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. + * */ prependListener(event: 'dialog', listener: (dialog: Dialog) => any): this; @@ -1677,7 +1787,7 @@ export interface Page { /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the - * passed {@link Download} instance. + * passed [Download](https://playwright.dev/docs/api/class-download) instance. */ prependListener(event: 'download', listener: (download: Download) => any): this; @@ -1744,7 +1854,7 @@ export interface Page { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * and * [browserContext.on('request')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request) - * respectively instead of similar methods on the {@link Page}. + * respectively instead of similar methods on the [Page](https://playwright.dev/docs/api/class-page). * * ```js * // Start waiting for popup before clicking. Note no await. @@ -1757,6 +1867,7 @@ export interface Page { * **NOTE** Use * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). + * */ prependListener(event: 'popup', listener: (page: Page) => any): this; @@ -1782,6 +1893,7 @@ export interface Page { * with [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#page-event-request-failed). A request * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. + * */ prependListener(event: 'requestfailed', listener: (request: Request) => any): this; @@ -1798,7 +1910,7 @@ export interface Page { prependListener(event: 'response', listener: (response: Response) => any): this; /** - * Emitted when {@link WebSocket} request is sent. + * Emitted when [WebSocket](https://playwright.dev/docs/api/class-websocket) request is sent. */ prependListener(event: 'websocket', listener: (webSocket: WebSocket) => any): this; @@ -1826,7 +1938,8 @@ export interface Page { * handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't * perform any actions, the handler will not be triggered. * - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible - * anymore. You can opt-out of this behavior with `noWaitAfter`. + * anymore. You can opt-out of this behavior with + * [`noWaitAfter`](https://playwright.dev/docs/api/class-page#page-add-locator-handler-option-no-wait-after). * - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. * If your handler takes too long, it might cause timeouts. * - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the @@ -1834,13 +1947,17 @@ export interface Page { * * **NOTE** Running the handler will alter your page state mid-test. For example it will change the currently focused * element and move the mouse. Make sure that actions that run after the handler are self-contained and do not rely on - * the focus and mouse state being unchanged. <br /> <br /> For example, consider a test that calls + * the focus and mouse state being unchanged. + * + * For example, consider a test that calls * [locator.focus([options])](https://playwright.dev/docs/api/class-locator#locator-focus) followed by * [keyboard.press(key[, options])](https://playwright.dev/docs/api/class-keyboard#keyboard-press). If your handler * clicks a button between these two actions, the focused element most likely will be wrong, and key press will happen * on the unexpected element. Use * [locator.press(key[, options])](https://playwright.dev/docs/api/class-locator#locator-press) instead to avoid this - * problem. <br /> <br /> Another example is a series of mouse actions, where + * problem. + * + * Another example is a series of mouse actions, where * [mouse.move(x, y[, options])](https://playwright.dev/docs/api/class-mouse#mouse-move) is followed by * [mouse.down([options])](https://playwright.dev/docs/api/class-mouse#mouse-down). Again, when the handler runs * between these two actions, the mouse position will be wrong during the mouse down. Prefer self-contained actions @@ -1876,8 +1993,9 @@ export interface Page { * ``` * * An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible, - * so the handler is called before every actionability check. It is important to specify `noWaitAfter`, because the - * handler does not hide the `<body>` element. + * so the handler is called before every actionability check. It is important to specify + * [`noWaitAfter`](https://playwright.dev/docs/api/class-page#page-add-locator-handler-option-no-wait-after), because + * the handler does not hide the `<body>` element. * * ```js * // Setup the handler. @@ -1891,7 +2009,7 @@ export interface Page { * ``` * * Handler takes the original locator as an argument. You can also automatically remove the handler after a number of - * invocations by setting `times`: + * invocations by setting [`times`](https://playwright.dev/docs/api/class-page#page-add-locator-handler-option-times): * * ```js * await page.addLocatorHandler(page.getByLabel('Close'), async locator => { @@ -1900,8 +2018,9 @@ export interface Page { * ``` * * @param locator Locator that triggers the handler. - * @param handler Function that should be run once `locator` appears. This function should get rid of the element that blocks actions - * like click. + * @param handler Function that should be run once + * [`locator`](https://playwright.dev/docs/api/class-page#page-add-locator-handler-option-locator) appears. This + * function should get rid of the element that blocks actions like click. * @param options */ addLocatorHandler(locator: Locator, handler: ((locator: Locator) => Promise<any>), options?: { @@ -1979,19 +2098,24 @@ export interface Page { * **NOTE** Use locator-based [locator.check([options])](https://playwright.dev/docs/api/class-locator#locator-check) instead. * Read more about [locators](https://playwright.dev/docs/locators). * - * This method checks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. + * This method checks an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-check-option-selector) by performing the following + * steps: + * 1. Find an element matching [`selector`](https://playwright.dev/docs/api/class-page#page-check-option-selector). + * If there is none, wait until a matching element is attached to the DOM. * 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is * already checked, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-page#page-check-option-force) option is set. If the element + * is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. * 1. Ensure that the element is now checked. If not, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-page#page-check-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -2043,17 +2167,24 @@ export interface Page { * **NOTE** Use locator-based [locator.click([options])](https://playwright.dev/docs/api/class-locator#locator-click) instead. * Read more about [locators](https://playwright.dev/docs/locators). * - * This method clicks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * This method clicks an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-click-option-selector) by performing the following + * steps: + * 1. Find an element matching [`selector`](https://playwright.dev/docs/api/class-page#page-click-option-selector). + * If there is none, wait until a matching element is attached to the DOM. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-page#page-click-option-force) option is set. If the element + * is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the - * element, or the specified `position`. - * 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-page#page-click-option-position). + * 1. Wait for initiated navigations to either succeed or fail, unless + * [`noWaitAfter`](https://playwright.dev/docs/api/class-page#page-click-option-no-wait-after) option is set. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-page#page-click-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -2120,19 +2251,25 @@ export interface Page { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; /** - * If `runBeforeUnload` is `false`, does not run any unload handlers and waits for the page to be closed. If - * `runBeforeUnload` is `true` the method will run unload handlers, but will **not** wait for the page to close. + * If [`runBeforeUnload`](https://playwright.dev/docs/api/class-page#page-close-option-run-before-unload) is `false`, + * does not run any unload handlers and waits for the page to be closed. If + * [`runBeforeUnload`](https://playwright.dev/docs/api/class-page#page-close-option-run-before-unload) is `true` the + * method will run unload handlers, but will **not** wait for the page to close. * * By default, `page.close()` **does not** run `beforeunload` handlers. * - * **NOTE** if `runBeforeUnload` is passed as true, a `beforeunload` dialog might be summoned and should be handled - * manually via [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) event. + * **NOTE** if [`runBeforeUnload`](https://playwright.dev/docs/api/class-page#page-close-option-run-before-unload) is + * passed as true, a `beforeunload` dialog might be summoned and should be handled manually via + * [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) event. + * * @param options */ close(options?: { @@ -2162,18 +2299,26 @@ export interface Page { * **NOTE** Use locator-based [locator.dblclick([options])](https://playwright.dev/docs/api/class-locator#locator-dblclick) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method double clicks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * This method double clicks an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-dblclick-option-selector) by performing the following + * steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-dblclick-option-selector). If there is none, + * wait until a matching element is attached to the DOM. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-page#page-dblclick-option-force) option is set. If the + * element is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to double click in the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-page#page-dblclick-option-position). * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-page#page-dblclick-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **NOTE** `page.dblclick()` dispatches two `click` events and a single `dblclick` event. + * * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -2233,7 +2378,9 @@ export interface Page { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -2253,10 +2400,13 @@ export interface Page { * await page.dispatchEvent('button#submit', 'click'); * ``` * - * Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` - * properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. + * Under the hood, it creates an instance of an event based on the given + * [`type`](https://playwright.dev/docs/api/class-page#page-dispatch-event-option-type), initializes it with + * [`eventInit`](https://playwright.dev/docs/api/class-page#page-dispatch-event-option-event-init) properties and + * dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. * - * Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties: + * Since [`eventInit`](https://playwright.dev/docs/api/class-page#page-dispatch-event-option-event-init) is + * event-specific, please refer to the events documentation for the lists of initial properties: * - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent) * - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent) * - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent) @@ -2436,10 +2586,15 @@ export interface Page { }): Promise<void>; /** - * The method adds a function called `name` on the `window` object of every frame in the page. When called, the - * function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. + * The method adds a function called + * [`name`](https://playwright.dev/docs/api/class-page#page-expose-function-option-name) on the `window` object of + * every frame in the page. When called, the function executes + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-function-option-callback) and returns a + * [Promise] which resolves to the return value of + * [`callback`](https://playwright.dev/docs/api/class-page#page-expose-function-option-callback). * - * If the `callback` returns a [Promise], it will be awaited. + * If the [`callback`](https://playwright.dev/docs/api/class-page#page-expose-function-option-callback) returns a + * [Promise], it will be awaited. * * See * [browserContext.exposeFunction(name, callback)](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-function) @@ -2485,9 +2640,10 @@ export interface Page { * **NOTE** Use locator-based [locator.fill(value[, options])](https://playwright.dev/docs/api/class-locator#locator-fill) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/docs/actionability) checks, - * focuses the element, fills it and triggers an `input` event after filling. Note that you can pass an empty string - * to clear the input field. + * This method waits for an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-fill-option-selector), waits for + * [actionability](https://playwright.dev/docs/actionability) checks, focuses the element, fills it and triggers an `input` event after + * filling. Note that you can pass an empty string to clear the input field. * * If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an * error. However, if the element is inside the `<label>` element that has an associated @@ -2532,8 +2688,10 @@ export interface Page { * **NOTE** Use locator-based [locator.focus([options])](https://playwright.dev/docs/api/class-locator#locator-focus) instead. * Read more about [locators](https://playwright.dev/docs/locators). * - * This method fetches an element with `selector` and focuses it. If there's no element matching `selector`, the - * method waits until a matching element appears in the DOM. + * This method fetches an element with + * [`selector`](https://playwright.dev/docs/api/class-page#page-focus-option-selector) and focuses it. If there's no + * element matching [`selector`](https://playwright.dev/docs/api/class-page#page-focus-option-selector), the method + * waits until a matching element appears in the DOM. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -2554,11 +2712,6 @@ export interface Page { timeout?: number; }): Promise<void>; - /** - * Force the browser to perform garbage collection. - */ - forceGarbageCollection(): Promise<void>; - /** * Returns frame matching the specified criteria. Either `name` or `url` must be specified. * @@ -2775,12 +2928,15 @@ export interface Page { * * **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about * [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled). + * */ disabled?: boolean; /** - * Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a - * regular expression. Note that exact match still trims whitespace. + * Whether [`name`](https://playwright.dev/docs/api/class-page#page-get-by-role-option-name) is matched exactly: + * case-sensitive and whole-string. Defaults to false. Ignored when + * [`name`](https://playwright.dev/docs/api/class-page#page-get-by-role-option-name) is a regular expression. Note + * that exact match still trims whitespace. */ exact?: boolean; @@ -2809,7 +2965,8 @@ export interface Page { /** * Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is - * case-insensitive and searches for a substring, use `exact` to control this behavior. + * case-insensitive and searches for a substring, use + * [`exact`](https://playwright.dev/docs/api/class-page#page-get-by-role-option-exact) to control this behavior. * * Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). */ @@ -3015,7 +3172,8 @@ export interface Page { * The method will throw an error if: * - there's an SSL error (e.g. in case of self-signed certificates). * - target URL is invalid. - * - the `timeout` is exceeded during navigation. + * - the [`timeout`](https://playwright.dev/docs/api/class-page#page-goto-option-timeout) is exceeded during + * navigation. * - the remote server does not respond or is unreachable. * - the main resource failed to load. * @@ -3028,8 +3186,10 @@ export interface Page { * * **NOTE** Headless mode doesn't support navigation to a PDF document. See the * [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295). - * @param url URL to navigate page to. The url should include scheme, e.g. `https://`. When a `baseURL` via the context options - * was provided and the passed URL is a path, it gets merged via the + * + * @param url URL to navigate page to. The url should include scheme, e.g. `https://`. When a + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the context + * options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. * @param options */ @@ -3066,16 +3226,22 @@ export interface Page { * **NOTE** Use locator-based [locator.hover([options])](https://playwright.dev/docs/api/class-locator#locator-hover) instead. * Read more about [locators](https://playwright.dev/docs/locators). * - * This method hovers over an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * This method hovers over an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-hover-option-selector) by performing the following + * steps: + * 1. Find an element matching [`selector`](https://playwright.dev/docs/api/class-page#page-hover-option-selector). + * If there is none, wait until a matching element is attached to the DOM. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-page#page-hover-option-force) option is set. If the element + * is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to hover over the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-page#page-hover-option-position). * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-page#page-hover-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -3125,7 +3291,9 @@ export interface Page { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -3321,8 +3489,9 @@ export interface Page { * **NOTE** Use locator-based [locator.isHidden([options])](https://playwright.dev/docs/api/class-locator#locator-is-hidden) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/docs/actionability#visible). `selector` that - * does not match any elements is considered hidden. + * Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/docs/actionability#visible). + * [`selector`](https://playwright.dev/docs/api/class-page#page-is-hidden-option-selector) that does not match any + * elements is considered hidden. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -3346,8 +3515,9 @@ export interface Page { * **NOTE** Use locator-based [locator.isVisible([options])](https://playwright.dev/docs/api/class-locator#locator-is-visible) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * Returns whether the element is [visible](https://playwright.dev/docs/actionability#visible). `selector` that does not match any elements - * is considered not visible. + * Returns whether the element is [visible](https://playwright.dev/docs/actionability#visible). + * [`selector`](https://playwright.dev/docs/api/class-page#page-is-visible-option-selector) that does not match any + * elements is considered not visible. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -3386,8 +3556,8 @@ export interface Page { * `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article * div` will fail, because the inner locator must be relative and should not use any elements outside the `content`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ has?: Locator; @@ -3395,8 +3565,8 @@ export interface Page { * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the * outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ hasNot?: Locator; @@ -3431,8 +3601,9 @@ export interface Page { * User can inspect selectors or perform manual steps while paused. Resume will continue running the original script * from the place it was paused. * - * **NOTE** This method requires Playwright to be started in a headed mode, with a falsy `headless` value in the - * [browserType.launch([options])](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). + * **NOTE** This method requires Playwright to be started in a headed mode, with a falsy + * [`headless`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-headless) option. + * */ pause(): Promise<void>; @@ -3457,8 +3628,10 @@ export interface Page { * await page.pdf({ path: 'page.pdf' }); * ``` * - * The `width`, `height`, and `margin` options accept values labeled with units. Unlabeled values are treated as - * pixels. + * The [`width`](https://playwright.dev/docs/api/class-page#page-pdf-option-width), + * [`height`](https://playwright.dev/docs/api/class-page#page-pdf-option-height), and + * [`margin`](https://playwright.dev/docs/api/class-page#page-pdf-option-margin) options accept values labeled with + * units. Unlabeled values are treated as pixels. * * A few examples: * - `page.pdf({width: 100})` - prints with width set to 100 pixels @@ -3471,7 +3644,7 @@ export interface Page { * - `cm` - centimeter * - `mm` - millimeter * - * The `format` options are: + * The [`format`](https://playwright.dev/docs/api/class-page#page-pdf-option-format) options are: * - `Letter`: 8.5in x 11in * - `Legal`: 8.5in x 14in * - `Tabloid`: 11in x 17in @@ -3484,8 +3657,11 @@ export interface Page { * - `A5`: 5.83in x 8.27in * - `A6`: 4.13in x 5.83in * - * **NOTE** `headerTemplate` and `footerTemplate` markup have the following limitations: > 1. Script tags inside - * templates are not evaluated. > 2. Page styles are not visible inside templates. + * **NOTE** [`headerTemplate`](https://playwright.dev/docs/api/class-page#page-pdf-option-header-template) and + * [`footerTemplate`](https://playwright.dev/docs/api/class-page#page-pdf-option-footer-template) markup have the + * following limitations: > 1. Script tags inside templates are not evaluated. > 2. Page styles are not visible inside + * templates. + * * @param options */ pdf(options?: { @@ -3495,12 +3671,15 @@ export interface Page { displayHeaderFooter?: boolean; /** - * HTML template for the print footer. Should use the same format as the `headerTemplate`. + * HTML template for the print footer. Should use the same format as the + * [`headerTemplate`](https://playwright.dev/docs/api/class-page#page-pdf-option-header-template). */ footerTemplate?: string; /** - * Paper format. If set, takes priority over `width` or `height` options. Defaults to 'Letter'. + * Paper format. If set, takes priority over + * [`width`](https://playwright.dev/docs/api/class-page#page-pdf-option-width) or + * [`height`](https://playwright.dev/docs/api/class-page#page-pdf-option-height) options. Defaults to 'Letter'. */ format?: string; @@ -3561,14 +3740,18 @@ export interface Page { pageRanges?: string; /** - * The file path to save the PDF to. If `path` is a relative path, then it is resolved relative to the current working - * directory. If no path is provided, the PDF won't be saved to the disk. + * The file path to save the PDF to. If [`path`](https://playwright.dev/docs/api/class-page#page-pdf-option-path) is a + * relative path, then it is resolved relative to the current working directory. If no path is provided, the PDF won't + * be saved to the disk. */ path?: string; /** - * Give any CSS `@page` size declared in the page priority over what is declared in `width` and `height` or `format` - * options. Defaults to `false`, which will scale the content to fit the paper size. + * Give any CSS `@page` size declared in the page priority over what is declared in + * [`width`](https://playwright.dev/docs/api/class-page#page-pdf-option-width) and + * [`height`](https://playwright.dev/docs/api/class-page#page-pdf-option-height) or + * [`format`](https://playwright.dev/docs/api/class-page#page-pdf-option-format) options. Defaults to `false`, which + * will scale the content to fit the paper size. */ preferCSSPageSize?: boolean; @@ -3601,9 +3784,10 @@ export interface Page { * [keyboard.down(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-down) and * [keyboard.up(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-up). * - * `key` can specify the intended + * [`key`](https://playwright.dev/docs/api/class-page#page-press-option-key) can specify the intended * [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character - * to generate the text for. A superset of the `key` values can be found + * to generate the text for. A superset of the + * [`key`](https://playwright.dev/docs/api/class-page#page-press-option-key) values can be found * [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are: * * `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, @@ -3613,10 +3797,11 @@ export interface Page { * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * - * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. + * Holding down `Shift` will type the text that corresponds to the + * [`key`](https://playwright.dev/docs/api/class-page#page-press-option-key) in the upper case. * - * If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective - * texts. + * If [`key`](https://playwright.dev/docs/api/class-page#page-press-option-key) is a single character, it is + * case-sensitive, so the values `a` and `A` will generate different respective texts. * * Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When * specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed. @@ -3707,6 +3892,26 @@ export interface Page { */ removeLocatorHandler(locator: Locator): Promise<void>; + /** + * Request the page to perform garbage collection. Note that there is no guarantee that all unreachable objects will + * be collected. + * + * This is useful to help detect memory leaks. For example, if your page has a large object `'suspect'` that might be + * leaked, you can check that it does not leak by using a + * [`WeakRef`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef). + * + * ```js + * // 1. In your page, save a WeakRef for the "suspect". + * await page.evaluate(() => globalThis.suspectWeakRef = new WeakRef(suspect)); + * // 2. Request garbage collection. + * await page.requestGC(); + * // 3. Check that weak ref does not deref to the original object. + * expect(await page.evaluate(() => !globalThis.suspectWeakRef.deref())).toBe(true); + * ``` + * + */ + requestGC(): Promise<void>; + /** * Routing provides the capability to modify network requests that are made by a page. * @@ -3718,7 +3923,8 @@ export interface Page { * **NOTE** [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route) will not * intercept requests intercepted by Service Worker. See [this](https://github.com/microsoft/playwright/issues/1090) * issue. We recommend disabling Service Workers when using request interception by setting - * `Browser.newContext.serviceWorkers` to `'block'`. + * [`serviceWorkers`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-service-workers) to + * `'block'`. * * **NOTE** [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route) will not * intercept the first request of a popup page. Use @@ -3765,7 +3971,9 @@ export interface Page { * [page.unroute(url[, handler])](https://playwright.dev/docs/api/class-page#page-unroute). * * **NOTE** Enabling routing disables http cache. - * @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a `baseURL` via the context + * + * @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the context * options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. * @param handler handler function to route the request. @@ -3784,7 +3992,9 @@ export interface Page { * * Playwright will not serve requests intercepted by Service Worker from the HAR file. See * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when - * using request interception by setting `Browser.newContext.serviceWorkers` to `'block'`. + * using request interception by setting + * [`serviceWorkers`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-service-workers) to + * `'block'`. * @param har Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a * relative path, then it is resolved relative to the current working directory. * @param options @@ -3826,6 +4036,32 @@ export interface Page { url?: string|RegExp; }): Promise<void>; + /** + * This method allows to modify websocket connections that are made by the page. + * + * Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this + * method before navigating the page. + * + * **Usage** + * + * Below is an example of a simple mock that responds to a single message. See + * [WebSocketRoute](https://playwright.dev/docs/api/class-websocketroute) for more details and examples. + * + * ```js + * await page.routeWebSocket('/ws', ws => { + * ws.onMessage(message => { + * if (message === 'request') + * ws.send('response'); + * }); + * }); + * ``` + * + * @param url Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) context option. + * @param handler Handler function to route the WebSocket. + */ + routeWebSocket(url: string|RegExp|((url: URL) => boolean), handler: ((websocketroute: WebSocketRoute) => Promise<any>|any)): Promise<void>; + /** * Returns the buffer with the captured screenshot. * @param options @@ -3837,8 +4073,10 @@ export interface Page { * [locator.selectOption(values[, options])](https://playwright.dev/docs/api/class-locator#locator-select-option) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/docs/actionability) checks, waits - * until all specified options are present in the `<select>` element and selects these options. + * This method waits for an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-select-option-option-selector), waits for + * [actionability](https://playwright.dev/docs/actionability) checks, waits until all specified options are present in the `<select>` + * element and selects these options. * * If the target element is not a `<select>` element, this method throws an error. However, if the element is inside * the `<label>` element that has an associated @@ -3932,19 +4170,25 @@ export interface Page { * [locator.setChecked(checked[, options])](https://playwright.dev/docs/api/class-locator#locator-set-checked) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method checks or unchecks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. + * This method checks or unchecks an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-set-checked-option-selector) by performing the + * following steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-set-checked-option-selector). If there is none, + * wait until a matching element is attached to the DOM. * 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. * 1. If the element already has the right checked state, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-page#page-set-checked-option-force) option is set. If the + * element is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. * 1. Ensure that the element is now checked or unchecked. If not, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-page#page-set-checked-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param checked Whether to check or uncheck the checkbox. @@ -4039,17 +4283,20 @@ export interface Page { * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) * and * [browserContext.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-navigation-timeout). + * * @param timeout Maximum navigation time in milliseconds */ setDefaultNavigationTimeout(timeout: number): void; /** - * This setting will change the default maximum time for all the methods accepting `timeout` option. + * This setting will change the default maximum time for all the methods accepting + * [`timeout`](https://playwright.dev/docs/api/class-page#page-set-default-timeout-option-timeout) option. * * **NOTE** * [page.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-navigation-timeout) * takes priority over * [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout). + * * @param timeout Maximum time in milliseconds */ setDefaultTimeout(timeout: number): void; @@ -4060,6 +4307,7 @@ export interface Page { * **NOTE** * [page.setExtraHTTPHeaders(headers)](https://playwright.dev/docs/api/class-page#page-set-extra-http-headers) does * not guarantee the order of headers in the outgoing requests. + * * @param headers An object containing additional HTTP headers to be sent with every request. All header values must be strings. */ setExtraHTTPHeaders(headers: { [key: string]: string; }): Promise<void>; @@ -4073,9 +4321,9 @@ export interface Page { * they are resolved relative to the current working directory. For empty array, clears the selected files. For inputs * with a `[webkitdirectory]` attribute, only a single directory path is supported. * - * This method expects `selector` to point to an - * [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside - * the `<label>` element that has an associated + * This method expects [`selector`](https://playwright.dev/docs/api/class-page#page-set-input-files-option-selector) + * to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the + * element is inside the `<label>` element that has an associated * [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. @@ -4176,19 +4424,26 @@ export interface Page { * **NOTE** Use locator-based [locator.tap([options])](https://playwright.dev/docs/api/class-locator#locator-tap) instead. Read * more about [locators](https://playwright.dev/docs/locators). * - * This method taps an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * This method taps an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-tap-option-selector) by performing the following + * steps: + * 1. Find an element matching [`selector`](https://playwright.dev/docs/api/class-page#page-tap-option-selector). + * If there is none, wait until a matching element is attached to the DOM. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-page#page-tap-option-force) option is set. If the element is + * detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.touchscreen](https://playwright.dev/docs/api/class-page#page-touchscreen) to tap the center of the - * element, or the specified `position`. + * element, or the specified [`position`](https://playwright.dev/docs/api/class-page#page-tap-option-position). * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-page#page-tap-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **NOTE** [page.tap(selector[, options])](https://playwright.dev/docs/api/class-page#page-tap) the method will throw - * if `hasTouch` option of the browser context is false. + * if [`hasTouch`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-has-touch) option of the + * browser context is false. + * * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -4238,7 +4493,9 @@ export interface Page { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -4323,19 +4580,25 @@ export interface Page { * **NOTE** Use locator-based [locator.uncheck([options])](https://playwright.dev/docs/api/class-locator#locator-uncheck) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method unchecks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. + * This method unchecks an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-uncheck-option-selector) by performing the following + * steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-page#page-uncheck-option-selector). If there is none, wait + * until a matching element is attached to the DOM. * 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is * already unchecked, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-page#page-uncheck-option-force) option is set. If the element + * is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. * 1. Ensure that the element is now unchecked. If not, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-page#page-uncheck-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -4385,8 +4648,9 @@ export interface Page { /** * Removes a route created with - * [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route). When `handler` is not - * specified, removes all routes for the `url`. + * [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route). When + * [`handler`](https://playwright.dev/docs/api/class-page#page-unroute-option-handler) is not specified, removes all + * routes for the [`url`](https://playwright.dev/docs/api/class-page#page-unroute-option-url). * @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. * @param handler Optional handler function to route the request. */ @@ -4437,7 +4701,8 @@ export interface Page { /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. * - * The arguments passed into `console.log` are available on the {@link ConsoleMessage} event handler argument. + * The arguments passed into `console.log` are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -4490,6 +4755,7 @@ export interface Page { * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. + * */ waitForEvent(event: 'dialog', optionsOrPredicate?: { predicate?: (dialog: Dialog) => boolean | Promise<boolean>, timeout?: number } | ((dialog: Dialog) => boolean | Promise<boolean>)): Promise<Dialog>; @@ -4501,7 +4767,7 @@ export interface Page { /** * Emitted when attachment download started. User can access basic file operations on downloaded content via the - * passed {@link Download} instance. + * passed [Download](https://playwright.dev/docs/api/class-download) instance. */ waitForEvent(event: 'download', optionsOrPredicate?: { predicate?: (download: Download) => boolean | Promise<boolean>, timeout?: number } | ((download: Download) => boolean | Promise<boolean>)): Promise<Download>; @@ -4568,7 +4834,7 @@ export interface Page { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * and * [browserContext.on('request')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request) - * respectively instead of similar methods on the {@link Page}. + * respectively instead of similar methods on the [Page](https://playwright.dev/docs/api/class-page). * * ```js * // Start waiting for popup before clicking. Note no await. @@ -4581,6 +4847,7 @@ export interface Page { * **NOTE** Use * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). + * */ waitForEvent(event: 'popup', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise<boolean>, timeout?: number } | ((page: Page) => boolean | Promise<boolean>)): Promise<Page>; @@ -4606,6 +4873,7 @@ export interface Page { * with [page.on('requestfailed')](https://playwright.dev/docs/api/class-page#page-event-request-failed). A request * will only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network * error net::ERR_FAILED. + * */ waitForEvent(event: 'requestfailed', optionsOrPredicate?: { predicate?: (request: Request) => boolean | Promise<boolean>, timeout?: number } | ((request: Request) => boolean | Promise<boolean>)): Promise<Request>; @@ -4622,7 +4890,7 @@ export interface Page { waitForEvent(event: 'response', optionsOrPredicate?: { predicate?: (response: Response) => boolean | Promise<boolean>, timeout?: number } | ((response: Response) => boolean | Promise<boolean>)): Promise<Response>; /** - * Emitted when {@link WebSocket} request is sent. + * Emitted when [WebSocket](https://playwright.dev/docs/api/class-websocket) request is sent. */ waitForEvent(event: 'websocket', optionsOrPredicate?: { predicate?: (webSocket: WebSocket) => boolean | Promise<boolean>, timeout?: number } | ((webSocket: WebSocket) => boolean | Promise<boolean>)): Promise<WebSocket>; @@ -4698,6 +4966,7 @@ export interface Page { * * **NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL * is considered a navigation. + * * @deprecated This method is inherently racy, please use * [page.waitForURL(url[, options])](https://playwright.dev/docs/api/class-page#page-wait-for-url) instead. * @param options @@ -4752,7 +5021,7 @@ export interface Page { * const request = await requestPromise; * ``` * - * @param urlOrPredicate Request URL string, regex or predicate receiving {@link Request} object. + * @param urlOrPredicate Request URL string, regex or predicate receiving [Request](https://playwright.dev/docs/api/class-request) object. * @param options */ waitForRequest(urlOrPredicate: string|RegExp|((request: Request) => boolean|Promise<boolean>), options?: { @@ -4785,8 +5054,9 @@ export interface Page { * const response = await responsePromise; * ``` * - * @param urlOrPredicate Request URL string, regex or predicate receiving {@link Response} object. When a `baseURL` via the context options - * was provided and the passed URL is a path, it gets merged via the + * @param urlOrPredicate Request URL string, regex or predicate receiving [Response](https://playwright.dev/docs/api/class-response) object. + * When a [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the + * context options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. * @param options */ @@ -4801,10 +5071,11 @@ export interface Page { }): Promise<Response>; /** - * **NOTE** Never wait for timeout in production. Tests that wait for time are inherently flaky. Use {@link Locator} actions - * and web assertions that wait automatically. + * **NOTE** Never wait for timeout in production. Tests that wait for time are inherently flaky. Use + * [Locator](https://playwright.dev/docs/api/class-locator) actions and web assertions that wait automatically. * - * Waits for the given `timeout` in milliseconds. + * Waits for the given [`timeout`](https://playwright.dev/docs/api/class-page#page-wait-for-timeout-option-timeout) in + * milliseconds. * * Note that `page.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going * to be flaky. Use signals such as network events, selectors becoming visible and others instead. @@ -4863,6 +5134,7 @@ export interface Page { * [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) associated with the page. * * **NOTE** This does not contain ServiceWorkers + * */ workers(): Array<Worker>; @@ -4881,7 +5153,8 @@ export interface Page { /** * **NOTE** Only available for Chromium atm. * - * Browser-specific Coverage implementation. See {@link Coverage} for more details. + * Browser-specific Coverage implementation. See [Coverage](https://playwright.dev/docs/api/class-coverage) for more + * details. */ coverage: Coverage; @@ -4908,7 +5181,8 @@ export interface Page { * [page.mainFrame()](https://playwright.dev/docs/api/class-page#page-main-frame) and * [frame.childFrames()](https://playwright.dev/docs/api/class-frame#frame-child-frames) methods. * - * {@link Frame} object's lifecycle is controlled by three events, dispatched on the page object: + * [Frame](https://playwright.dev/docs/api/class-frame) object's lifecycle is controlled by three events, dispatched + * on the page object: * - [page.on('frameattached')](https://playwright.dev/docs/api/class-page#page-event-frame-attached) - fired when * the frame gets attached to the page. A Frame can be attached to the page only once. * - [page.on('framenavigated')](https://playwright.dev/docs/api/class-page#page-event-frame-navigated) - fired when @@ -4939,7 +5213,8 @@ export interface Page { */ export interface Frame { /** - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-option-expression). * * If the function passed to the * [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate) returns a @@ -4968,7 +5243,7 @@ export interface Frame { * console.log(await frame.evaluate('1 + 2')); // prints "3" * ``` * - * {@link ElementHandle} instances can be passed as an argument to the + * [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) instances can be passed as an argument to the * [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate): * * ```js @@ -4980,11 +5255,13 @@ export interface Frame { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-option-expression). */ evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>; /** - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-option-expression). * * If the function passed to the * [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate) returns a @@ -5013,7 +5290,7 @@ export interface Frame { * console.log(await frame.evaluate('1 + 2')); // prints "3" * ``` * - * {@link ElementHandle} instances can be passed as an argument to the + * [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) instances can be passed as an argument to the * [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate): * * ```js @@ -5025,18 +5302,21 @@ export interface Frame { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-option-expression). */ evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<R>; /** - * Returns the return value of `pageFunction` as a {@link JSHandle}. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle-option-expression) as a + * [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * The only difference between * [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate) and * [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle) is * that [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle) - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function, passed to the * [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle), @@ -5057,7 +5337,7 @@ export interface Frame { * const aHandle = await frame.evaluateHandle('document'); // Handle for the 'document'. * ``` * - * {@link JSHandle} instances can be passed as an argument to the + * [JSHandle](https://playwright.dev/docs/api/class-jshandle) instances can be passed as an argument to the * [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle): * * ```js @@ -5070,17 +5350,20 @@ export interface Frame { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle-option-expression). */ evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>; /** - * Returns the return value of `pageFunction` as a {@link JSHandle}. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle-option-expression) as a + * [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * The only difference between * [frame.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate) and * [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle) is * that [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle) - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function, passed to the * [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle), @@ -5101,7 +5384,7 @@ export interface Frame { * const aHandle = await frame.evaluateHandle('document'); // Handle for the 'document'. * ``` * - * {@link JSHandle} instances can be passed as an argument to the + * [JSHandle](https://playwright.dev/docs/api/class-jshandle) instances can be passed as an argument to the * [frame.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle): * * ```js @@ -5114,7 +5397,8 @@ export interface Frame { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-evaluate-handle-option-expression). */ evaluateHandle<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<SmartHandle<R>>; @@ -5124,8 +5408,8 @@ export interface Frame { * * Returns the ElementHandle pointing to the frame element. * - * **NOTE** The use of {@link ElementHandle} is discouraged, use {@link Locator} objects and web-first assertions - * instead. + * **NOTE** The use of [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) is discouraged, use + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions instead. * * The method finds an element matching the specified selector within the frame. If no elements match the selector, * returns `null`. @@ -5139,8 +5423,8 @@ export interface Frame { * * Returns the ElementHandle pointing to the frame element. * - * **NOTE** The use of {@link ElementHandle} is discouraged, use {@link Locator} objects and web-first assertions - * instead. + * **NOTE** The use of [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) is discouraged, use + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions instead. * * The method finds an element matching the specified selector within the frame. If no elements match the selector, * returns `null`. @@ -5155,7 +5439,8 @@ export interface Frame { * * Returns the ElementHandles pointing to the frame elements. * - * **NOTE** The use of {@link ElementHandle} is discouraged, use {@link Locator} objects instead. + * **NOTE** The use of [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) is discouraged, use + * [Locator](https://playwright.dev/docs/api/class-locator) objects instead. * * The method finds all elements matching the specified selector within the frame. If no elements match the selector, * returns empty array. @@ -5168,7 +5453,8 @@ export interface Frame { * * Returns the ElementHandles pointing to the frame elements. * - * **NOTE** The use of {@link ElementHandle} is discouraged, use {@link Locator} objects instead. + * **NOTE** The use of [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) is discouraged, use + * [Locator](https://playwright.dev/docs/api/class-locator) objects instead. * * The method finds all elements matching the specified selector within the frame. If no elements match the selector, * returns empty array. @@ -5180,14 +5466,17 @@ export interface Frame { * **NOTE** This method does not wait for the element to pass the actionability checks and therefore can lead to the flaky * tests. Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). * * The method finds an element matching the specified selector within the frame and passes it as a first argument to - * `pageFunction`. If no elements match the selector, the method throws an error. + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). If no + * elements match the selector, the method throws an error. * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression) returns a + * [Promise], then * [frame.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -5201,7 +5490,8 @@ export interface Frame { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). * @param options */ $eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], Arg, R>, arg: Arg): Promise<R>; @@ -5209,14 +5499,17 @@ export interface Frame { * **NOTE** This method does not wait for the element to pass the actionability checks and therefore can lead to the flaky * tests. Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). * * The method finds an element matching the specified selector within the frame and passes it as a first argument to - * `pageFunction`. If no elements match the selector, the method throws an error. + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). If no + * elements match the selector, the method throws an error. * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression) returns a + * [Promise], then * [frame.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -5230,7 +5523,8 @@ export interface Frame { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). * @param options */ $eval<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E, Arg, R>, arg: Arg): Promise<R>; @@ -5238,14 +5532,17 @@ export interface Frame { * **NOTE** This method does not wait for the element to pass the actionability checks and therefore can lead to the flaky * tests. Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). * * The method finds an element matching the specified selector within the frame and passes it as a first argument to - * `pageFunction`. If no elements match the selector, the method throws an error. + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). If no + * elements match the selector, the method throws an error. * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression) returns a + * [Promise], then * [frame.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -5259,7 +5556,8 @@ export interface Frame { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). * @param options */ $eval<K extends keyof HTMLElementTagNameMap, R>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], void, R>, arg?: any): Promise<R>; @@ -5267,14 +5565,17 @@ export interface Frame { * **NOTE** This method does not wait for the element to pass the actionability checks and therefore can lead to the flaky * tests. Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). * * The method finds an element matching the specified selector within the frame and passes it as a first argument to - * `pageFunction`. If no elements match the selector, the method throws an error. + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). If no + * elements match the selector, the method throws an error. * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression) returns a + * [Promise], then * [frame.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -5288,7 +5589,8 @@ export interface Frame { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-option-expression). * @param options */ $eval<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E, void, R>, arg?: any): Promise<R>; @@ -5296,14 +5598,18 @@ export interface Frame { /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). * * The method finds all elements matching the specified selector within the frame and passes an array of matched - * elements as a first argument to `pageFunction`. + * elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression) + * returns a [Promise], then * [frame.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -5315,20 +5621,25 @@ export interface Frame { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). */ $$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], Arg, R>, arg: Arg): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). * * The method finds all elements matching the specified selector within the frame and passes an array of matched - * elements as a first argument to `pageFunction`. + * elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression) + * returns a [Promise], then * [frame.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -5340,20 +5651,25 @@ export interface Frame { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). */ $$eval<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E[], Arg, R>, arg: Arg): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). * * The method finds all elements matching the specified selector within the frame and passes an array of matched - * elements as a first argument to `pageFunction`. + * elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression) + * returns a [Promise], then * [frame.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -5365,20 +5681,25 @@ export interface Frame { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). */ $$eval<K extends keyof HTMLElementTagNameMap, R>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], void, R>, arg?: any): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). * * The method finds all elements matching the specified selector within the frame and passes an array of matched - * elements as a first argument to `pageFunction`. + * elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). * - * If `pageFunction` returns a [Promise], then + * If [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression) + * returns a [Promise], then * [frame.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -5390,12 +5711,15 @@ export interface Frame { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-eval-on-selector-all-option-expression). */ $$eval<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E[], void, R>, arg?: any): Promise<R>; /** - * Returns when the `pageFunction` returns a truthy value, returns that value. + * Returns when the + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-wait-for-function-option-expression) returns a + * truthy value, returns that value. * * **Usage** * @@ -5424,12 +5748,15 @@ export interface Frame { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-wait-for-function-option-expression). * @param options */ waitForFunction<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg, options?: PageWaitForFunctionOptions): Promise<SmartHandle<R>>; /** - * Returns when the `pageFunction` returns a truthy value, returns that value. + * Returns when the + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-wait-for-function-option-expression) returns a + * truthy value, returns that value. * * **Usage** * @@ -5458,7 +5785,8 @@ export interface Frame { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-frame#frame-wait-for-function-option-expression). * @param options */ waitForFunction<R>(pageFunction: PageFunction<void, R>, arg?: any, options?: PageWaitForFunctionOptions): Promise<SmartHandle<R>>; @@ -5468,15 +5796,21 @@ export interface Frame { * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. Read more * about [locators](https://playwright.dev/docs/locators). * - * Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns when element specified by selector satisfies + * [`state`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-state) option. Returns `null` + * if waiting for `hidden` or `detached`. * - * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using {@link Locator} - * objects and web-first assertions make the code wait-for-selector-free. + * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions make the code + * wait-for-selector-free. * - * Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If - * at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. - * If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw. + * Wait for the [`selector`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-selector) to + * satisfy [`state`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-state) option (either + * appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-selector) already satisfies + * the condition, the method will return immediately. If the selector doesn't satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-timeout) milliseconds, the + * function will throw. * * **Usage** * @@ -5506,15 +5840,21 @@ export interface Frame { * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. Read more * about [locators](https://playwright.dev/docs/locators). * - * Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns when element specified by selector satisfies + * [`state`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-state) option. Returns `null` + * if waiting for `hidden` or `detached`. * - * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using {@link Locator} - * objects and web-first assertions make the code wait-for-selector-free. + * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions make the code + * wait-for-selector-free. * - * Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If - * at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. - * If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw. + * Wait for the [`selector`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-selector) to + * satisfy [`state`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-state) option (either + * appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-selector) already satisfies + * the condition, the method will return immediately. If the selector doesn't satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-timeout) milliseconds, the + * function will throw. * * **Usage** * @@ -5544,15 +5884,21 @@ export interface Frame { * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. Read more * about [locators](https://playwright.dev/docs/locators). * - * Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns when element specified by selector satisfies + * [`state`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-state) option. Returns `null` + * if waiting for `hidden` or `detached`. * - * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using {@link Locator} - * objects and web-first assertions make the code wait-for-selector-free. + * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions make the code + * wait-for-selector-free. * - * Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If - * at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. - * If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw. + * Wait for the [`selector`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-selector) to + * satisfy [`state`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-state) option (either + * appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-selector) already satisfies + * the condition, the method will return immediately. If the selector doesn't satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-timeout) milliseconds, the + * function will throw. * * **Usage** * @@ -5582,15 +5928,21 @@ export interface Frame { * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. Read more * about [locators](https://playwright.dev/docs/locators). * - * Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns when element specified by selector satisfies + * [`state`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-state) option. Returns `null` + * if waiting for `hidden` or `detached`. * - * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using {@link Locator} - * objects and web-first assertions make the code wait-for-selector-free. + * **NOTE** Playwright automatically waits for element to be ready before performing an action. Using + * [Locator](https://playwright.dev/docs/api/class-locator) objects and web-first assertions make the code + * wait-for-selector-free. * - * Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If - * at the moment of calling the method `selector` already satisfies the condition, the method will return immediately. - * If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw. + * Wait for the [`selector`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-selector) to + * satisfy [`state`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-state) option (either + * appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-selector) already satisfies + * the condition, the method will return immediately. If the selector doesn't satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-wait-for-selector-option-timeout) milliseconds, the + * function will throw. * * **Usage** * @@ -5674,19 +6026,25 @@ export interface Frame { * **NOTE** Use locator-based [locator.check([options])](https://playwright.dev/docs/api/class-locator#locator-check) instead. * Read more about [locators](https://playwright.dev/docs/locators). * - * This method checks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. + * This method checks an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-check-option-selector) by performing the following + * steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-check-option-selector). If there is none, wait + * until a matching element is attached to the DOM. * 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is * already checked, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-frame#frame-check-option-force) option is set. If the element + * is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. * 1. Ensure that the element is now checked. If not, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-check-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -5740,17 +6098,25 @@ export interface Frame { * **NOTE** Use locator-based [locator.click([options])](https://playwright.dev/docs/api/class-locator#locator-click) instead. * Read more about [locators](https://playwright.dev/docs/locators). * - * This method clicks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * This method clicks an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-click-option-selector) by performing the following + * steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-click-option-selector). If there is none, wait + * until a matching element is attached to the DOM. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-frame#frame-click-option-force) option is set. If the element + * is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the - * element, or the specified `position`. - * 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-frame#frame-click-option-position). + * 1. Wait for initiated navigations to either succeed or fail, unless + * [`noWaitAfter`](https://playwright.dev/docs/api/class-frame#frame-click-option-no-wait-after) option is set. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-click-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -5817,7 +6183,9 @@ export interface Frame { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -5831,19 +6199,27 @@ export interface Frame { * **NOTE** Use locator-based [locator.dblclick([options])](https://playwright.dev/docs/api/class-locator#locator-dblclick) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method double clicks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * This method double clicks an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-dblclick-option-selector) by performing the + * following steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-dblclick-option-selector). If there is none, + * wait until a matching element is attached to the DOM. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-frame#frame-dblclick-option-force) option is set. If the + * element is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to double click in the center of the - * element, or the specified `position`. if the first click of the `dblclick()` triggers a navigation event, - * this method will throw. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-frame#frame-dblclick-option-position). if the first click + * of the `dblclick()` triggers a navigation event, this method will throw. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-dblclick-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **NOTE** `frame.dblclick()` dispatches two `click` events and a single `dblclick` event. + * * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -5903,7 +6279,9 @@ export interface Frame { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -5923,10 +6301,13 @@ export interface Frame { * await frame.dispatchEvent('button#submit', 'click'); * ``` * - * Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` - * properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. + * Under the hood, it creates an instance of an event based on the given + * [`type`](https://playwright.dev/docs/api/class-frame#frame-dispatch-event-option-type), initializes it with + * [`eventInit`](https://playwright.dev/docs/api/class-frame#frame-dispatch-event-option-event-init) properties and + * dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. * - * Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties: + * Since [`eventInit`](https://playwright.dev/docs/api/class-frame#frame-dispatch-event-option-event-init) is + * event-specific, please refer to the events documentation for the lists of initial properties: * - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent) * - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent) * - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent) @@ -6032,9 +6413,10 @@ export interface Frame { * **NOTE** Use locator-based [locator.fill(value[, options])](https://playwright.dev/docs/api/class-locator#locator-fill) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/docs/actionability) checks, - * focuses the element, fills it and triggers an `input` event after filling. Note that you can pass an empty string - * to clear the input field. + * This method waits for an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-fill-option-selector), waits for + * [actionability](https://playwright.dev/docs/actionability) checks, focuses the element, fills it and triggers an `input` event after + * filling. Note that you can pass an empty string to clear the input field. * * If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an * error. However, if the element is inside the `<label>` element that has an associated @@ -6079,8 +6461,10 @@ export interface Frame { * **NOTE** Use locator-based [locator.focus([options])](https://playwright.dev/docs/api/class-locator#locator-focus) instead. * Read more about [locators](https://playwright.dev/docs/locators). * - * This method fetches an element with `selector` and focuses it. If there's no element matching `selector`, the - * method waits until a matching element appears in the DOM. + * This method fetches an element with + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-focus-option-selector) and focuses it. If there's no + * element matching [`selector`](https://playwright.dev/docs/api/class-frame#frame-focus-option-selector), the method + * waits until a matching element appears in the DOM. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -6305,12 +6689,15 @@ export interface Frame { * * **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about * [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled). + * */ disabled?: boolean; /** - * Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a - * regular expression. Note that exact match still trims whitespace. + * Whether [`name`](https://playwright.dev/docs/api/class-frame#frame-get-by-role-option-name) is matched exactly: + * case-sensitive and whole-string. Defaults to false. Ignored when + * [`name`](https://playwright.dev/docs/api/class-frame#frame-get-by-role-option-name) is a regular expression. Note + * that exact match still trims whitespace. */ exact?: boolean; @@ -6339,7 +6726,8 @@ export interface Frame { /** * Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is - * case-insensitive and searches for a substring, use `exact` to control this behavior. + * case-insensitive and searches for a substring, use + * [`exact`](https://playwright.dev/docs/api/class-frame#frame-get-by-role-option-exact) to control this behavior. * * Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). */ @@ -6485,7 +6873,8 @@ export interface Frame { * The method will throw an error if: * - there's an SSL error (e.g. in case of self-signed certificates). * - target URL is invalid. - * - the `timeout` is exceeded during navigation. + * - the [`timeout`](https://playwright.dev/docs/api/class-frame#frame-goto-option-timeout) is exceeded during + * navigation. * - the remote server does not respond or is unreachable. * - the main resource failed to load. * @@ -6498,6 +6887,7 @@ export interface Frame { * * **NOTE** Headless mode doesn't support navigation to a PDF document. See the * [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295). + * * @param url URL to navigate frame to. The url should include scheme, e.g. `https://`. * @param options */ @@ -6534,16 +6924,23 @@ export interface Frame { * **NOTE** Use locator-based [locator.hover([options])](https://playwright.dev/docs/api/class-locator#locator-hover) instead. * Read more about [locators](https://playwright.dev/docs/locators). * - * This method hovers over an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * This method hovers over an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-hover-option-selector) by performing the following + * steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-hover-option-selector). If there is none, wait + * until a matching element is attached to the DOM. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-frame#frame-hover-option-force) option is set. If the element + * is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to hover over the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-frame#frame-hover-option-position). * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-hover-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -6593,7 +6990,9 @@ export interface Frame { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -6786,8 +7185,9 @@ export interface Frame { * **NOTE** Use locator-based [locator.isHidden([options])](https://playwright.dev/docs/api/class-locator#locator-is-hidden) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/docs/actionability#visible). `selector` that - * does not match any elements is considered hidden. + * Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/docs/actionability#visible). + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-is-hidden-option-selector) that does not match any + * elements is considered hidden. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -6811,8 +7211,9 @@ export interface Frame { * **NOTE** Use locator-based [locator.isVisible([options])](https://playwright.dev/docs/api/class-locator#locator-is-visible) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * Returns whether the element is [visible](https://playwright.dev/docs/actionability#visible). `selector` that does not match any elements - * is considered not visible. + * Returns whether the element is [visible](https://playwright.dev/docs/actionability#visible). + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-is-visible-option-selector) that does not match any + * elements is considered not visible. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -6853,8 +7254,8 @@ export interface Frame { * `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article * div` will fail, because the inner locator must be relative and should not use any elements outside the `content`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ has?: Locator; @@ -6862,8 +7263,8 @@ export interface Frame { * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the * outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ hasNot?: Locator; @@ -6888,6 +7289,7 @@ export interface Frame { * * **NOTE** This value is calculated once when the frame is created, and will not update if the attribute is changed * later. + * */ name(): string; @@ -6905,9 +7307,10 @@ export interface Frame { * **NOTE** Use locator-based [locator.press(key[, options])](https://playwright.dev/docs/api/class-locator#locator-press) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * `key` can specify the intended + * [`key`](https://playwright.dev/docs/api/class-frame#frame-press-option-key) can specify the intended * [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character - * to generate the text for. A superset of the `key` values can be found + * to generate the text for. A superset of the + * [`key`](https://playwright.dev/docs/api/class-frame#frame-press-option-key) values can be found * [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are: * * `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, @@ -6917,10 +7320,11 @@ export interface Frame { * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * - * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. + * Holding down `Shift` will type the text that corresponds to the + * [`key`](https://playwright.dev/docs/api/class-frame#frame-press-option-key) in the upper case. * - * If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective - * texts. + * If [`key`](https://playwright.dev/docs/api/class-frame#frame-press-option-key) is a single character, it is + * case-sensitive, so the values `a` and `A` will generate different respective texts. * * Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When * specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed. @@ -6963,8 +7367,10 @@ export interface Frame { * [locator.selectOption(values[, options])](https://playwright.dev/docs/api/class-locator#locator-select-option) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/docs/actionability) checks, waits - * until all specified options are present in the `<select>` element and selects these options. + * This method waits for an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-select-option-option-selector), waits for + * [actionability](https://playwright.dev/docs/actionability) checks, waits until all specified options are present in the `<select>` + * element and selects these options. * * If the target element is not a `<select>` element, this method throws an error. However, if the element is inside * the `<label>` element that has an associated @@ -7056,19 +7462,25 @@ export interface Frame { * [locator.setChecked(checked[, options])](https://playwright.dev/docs/api/class-locator#locator-set-checked) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method checks or unchecks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. + * This method checks or unchecks an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-set-checked-option-selector) by performing the + * following steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-set-checked-option-selector). If there is + * none, wait until a matching element is attached to the DOM. * 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. * 1. If the element already has the right checked state, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-frame#frame-set-checked-option-force) option is set. If the + * element is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. * 1. Ensure that the element is now checked or unchecked. If not, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-set-checked-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param checked Whether to check or uncheck the checkbox. @@ -7154,9 +7566,9 @@ export interface Frame { * Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then * they are resolved relative to the current working directory. For empty array, clears the selected files. * - * This method expects `selector` to point to an - * [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside - * the `<label>` element that has an associated + * This method expects [`selector`](https://playwright.dev/docs/api/class-frame#frame-set-input-files-option-selector) + * to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the + * element is inside the `<label>` element that has an associated * [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. @@ -7219,18 +7631,25 @@ export interface Frame { * **NOTE** Use locator-based [locator.tap([options])](https://playwright.dev/docs/api/class-locator#locator-tap) instead. Read * more about [locators](https://playwright.dev/docs/locators). * - * This method taps an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * This method taps an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-tap-option-selector) by performing the following + * steps: + * 1. Find an element matching [`selector`](https://playwright.dev/docs/api/class-frame#frame-tap-option-selector). + * If there is none, wait until a matching element is attached to the DOM. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-frame#frame-tap-option-force) option is set. If the element + * is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.touchscreen](https://playwright.dev/docs/api/class-page#page-touchscreen) to tap the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-frame#frame-tap-option-position). * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-tap-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **NOTE** `frame.tap()` requires that the `hasTouch` option of the browser context be set to true. + * * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -7280,7 +7699,9 @@ export interface Frame { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -7365,19 +7786,25 @@ export interface Frame { * **NOTE** Use locator-based [locator.uncheck([options])](https://playwright.dev/docs/api/class-locator#locator-uncheck) * instead. Read more about [locators](https://playwright.dev/docs/locators). * - * This method checks an element matching `selector` by performing the following steps: - * 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM. + * This method checks an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-uncheck-option-selector) by performing the following + * steps: + * 1. Find an element matching + * [`selector`](https://playwright.dev/docs/api/class-frame#frame-uncheck-option-selector). If there is none, + * wait until a matching element is attached to the DOM. * 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is * already unchecked, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-frame#frame-uncheck-option-force) option is set. If the + * element is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. * 1. Ensure that the element is now unchecked. If not, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-frame#frame-uncheck-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param options @@ -7486,6 +7913,7 @@ export interface Frame { * * **NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL * is considered a navigation. + * * @deprecated This method is inherently racy, please use * [frame.waitForURL(url[, options])](https://playwright.dev/docs/api/class-frame#frame-wait-for-url) instead. * @param options @@ -7521,10 +7949,11 @@ export interface Frame { }): Promise<null|Response>; /** - * **NOTE** Never wait for timeout in production. Tests that wait for time are inherently flaky. Use {@link Locator} actions - * and web assertions that wait automatically. + * **NOTE** Never wait for timeout in production. Tests that wait for time are inherently flaky. Use + * [Locator](https://playwright.dev/docs/api/class-locator) actions and web assertions that wait automatically. * - * Waits for the given `timeout` in milliseconds. + * Waits for the given [`timeout`](https://playwright.dev/docs/api/class-frame#frame-wait-for-timeout-option-timeout) + * in milliseconds. * * Note that `frame.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going * to be flaky. Use signals such as network events, selectors becoming visible and others instead. @@ -7594,12 +8023,19 @@ export interface Frame { */ export interface BrowserContext { /** - * The method adds a function called `name` on the `window` object of every frame in every page in the context. When - * called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. - * If the `callback` returns a [Promise], it will be awaited. + * The method adds a function called + * [`name`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-name) on the + * `window` object of every frame in every page in the context. When called, the function executes + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-callback) + * and returns a [Promise] which resolves to the return value of + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-callback). + * If the + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-callback) + * returns a [Promise], it will be awaited. * - * The first argument of the `callback` function contains information about the caller: `{ browserContext: - * BrowserContext, page: Page, frame: Frame }`. + * The first argument of the + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-callback) + * function contains information about the caller: `{ browserContext: BrowserContext, page: Page, frame: Frame }`. * * See [page.exposeBinding(name, callback[, options])](https://playwright.dev/docs/api/class-page#page-expose-binding) * for page-only version. @@ -7635,12 +8071,19 @@ export interface BrowserContext { */ exposeBinding(name: string, playwrightBinding: (source: BindingSource, arg: JSHandle) => any, options: { handle: true }): Promise<void>; /** - * The method adds a function called `name` on the `window` object of every frame in every page in the context. When - * called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. - * If the `callback` returns a [Promise], it will be awaited. + * The method adds a function called + * [`name`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-name) on the + * `window` object of every frame in every page in the context. When called, the function executes + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-callback) + * and returns a [Promise] which resolves to the return value of + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-callback). + * If the + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-callback) + * returns a [Promise], it will be awaited. * - * The first argument of the `callback` function contains information about the caller: `{ browserContext: - * BrowserContext, page: Page, frame: Frame }`. + * The first argument of the + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-binding-option-callback) + * function contains information about the caller: `{ browserContext: BrowserContext, page: Page, frame: Frame }`. * * See [page.exposeBinding(name, callback[, options])](https://playwright.dev/docs/api/class-page#page-expose-binding) * for page-only version. @@ -7705,8 +8148,11 @@ export interface BrowserContext { * [browserContext.addInitScript(script[, arg])](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script) * and [page.addInitScript(script[, arg])](https://playwright.dev/docs/api/class-page#page-add-init-script) is not * defined. + * * @param script Script to be evaluated in all pages in the browser context. - * @param arg Optional argument to pass to `script` (only supported when passing a function). + * @param arg Optional argument to pass to + * [`script`](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script-option-script) + * (only supported when passing a function). */ addInitScript<Arg>(script: PageFunction<Arg, any> | { path?: string, content?: string }, arg?: Arg): Promise<void>; @@ -7755,8 +8201,8 @@ export interface BrowserContext { /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. * - * The arguments passed into `console.log` and the page are available on the {@link ConsoleMessage} event handler - * argument. + * The arguments passed into `console.log` and the page are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -7791,6 +8237,7 @@ export interface BrowserContext { * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. + * */ on(event: 'dialog', listener: (dialog: Dialog) => any): this; @@ -7807,7 +8254,7 @@ export interface BrowserContext { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * and * [browserContext.on('request')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request) - * respectively instead of similar methods on the {@link Page}. + * respectively instead of similar methods on the [Page](https://playwright.dev/docs/api/class-page). * * ```js * const newPagePromise = context.waitForEvent('page'); @@ -7819,6 +8266,7 @@ export interface BrowserContext { * **NOTE** Use * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). + * */ on(event: 'page', listener: (page: Page) => any): this; @@ -7842,6 +8290,7 @@ export interface BrowserContext { * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-finished) * event and not with * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-failed). + * */ on(event: 'requestfailed', listener: (request: Request) => any): this; @@ -7951,8 +8400,8 @@ export interface BrowserContext { /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. * - * The arguments passed into `console.log` and the page are available on the {@link ConsoleMessage} event handler - * argument. + * The arguments passed into `console.log` and the page are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -7987,6 +8436,7 @@ export interface BrowserContext { * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. + * */ addListener(event: 'dialog', listener: (dialog: Dialog) => any): this; @@ -8003,7 +8453,7 @@ export interface BrowserContext { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * and * [browserContext.on('request')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request) - * respectively instead of similar methods on the {@link Page}. + * respectively instead of similar methods on the [Page](https://playwright.dev/docs/api/class-page). * * ```js * const newPagePromise = context.waitForEvent('page'); @@ -8015,6 +8465,7 @@ export interface BrowserContext { * **NOTE** Use * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). + * */ addListener(event: 'page', listener: (page: Page) => any): this; @@ -8038,6 +8489,7 @@ export interface BrowserContext { * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-finished) * event and not with * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-failed). + * */ addListener(event: 'requestfailed', listener: (request: Request) => any): this; @@ -8202,8 +8654,8 @@ export interface BrowserContext { /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. * - * The arguments passed into `console.log` and the page are available on the {@link ConsoleMessage} event handler - * argument. + * The arguments passed into `console.log` and the page are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -8238,6 +8690,7 @@ export interface BrowserContext { * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. + * */ prependListener(event: 'dialog', listener: (dialog: Dialog) => any): this; @@ -8254,7 +8707,7 @@ export interface BrowserContext { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * and * [browserContext.on('request')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request) - * respectively instead of similar methods on the {@link Page}. + * respectively instead of similar methods on the [Page](https://playwright.dev/docs/api/class-page). * * ```js * const newPagePromise = context.waitForEvent('page'); @@ -8266,6 +8719,7 @@ export interface BrowserContext { * **NOTE** Use * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). + * */ prependListener(event: 'page', listener: (page: Page) => any): this; @@ -8289,6 +8743,7 @@ export interface BrowserContext { * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-finished) * event and not with * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-failed). + * */ prependListener(event: 'requestfailed', listener: (request: Request) => any): this; @@ -8439,6 +8894,7 @@ export interface BrowserContext { * Closes the browser context. All the pages that belong to the browser context will be closed. * * **NOTE** The default browser context cannot be closed. + * * @param options */ close(options?: { @@ -8456,10 +8912,16 @@ export interface BrowserContext { cookies(urls?: string|ReadonlyArray<string>): Promise<Array<Cookie>>; /** - * The method adds a function called `name` on the `window` object of every frame in every page in the context. When - * called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. + * The method adds a function called + * [`name`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-function-option-name) on the + * `window` object of every frame in every page in the context. When called, the function executes + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-function-option-callback) + * and returns a [Promise] which resolves to the return value of + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-function-option-callback). * - * If the `callback` returns a [Promise], it will be awaited. + * If the + * [`callback`](https://playwright.dev/docs/api/class-browsercontext#browser-context-expose-function-option-callback) + * returns a [Promise], it will be awaited. * * See [page.exposeFunction(name, callback)](https://playwright.dev/docs/api/class-page#page-expose-function) for * page-only version. @@ -8553,7 +9015,9 @@ export interface BrowserContext { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * will not intercept requests intercepted by Service Worker. See * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when - * using request interception by setting `Browser.newContext.serviceWorkers` to `'block'`. + * using request interception by setting + * [`serviceWorkers`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-service-workers) to + * `'block'`. * * **Usage** * @@ -8597,7 +9061,9 @@ export interface BrowserContext { * [browserContext.unroute(url[, handler])](https://playwright.dev/docs/api/class-browsercontext#browser-context-unroute). * * **NOTE** Enabling routing disables http cache. - * @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a `baseURL` via the context + * + * @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the context * options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. * @param handler handler function to route the request. @@ -8616,7 +9082,9 @@ export interface BrowserContext { * * Playwright will not serve requests intercepted by Service Worker from the HAR file. See * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when - * using request interception by setting `Browser.newContext.serviceWorkers` to `'block'`. + * using request interception by setting + * [`serviceWorkers`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-service-workers) to + * `'block'`. * @param har Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a * relative path, then it is resolved relative to the current working directory. * @param options @@ -8658,6 +9126,34 @@ export interface BrowserContext { url?: string|RegExp; }): Promise<void>; + /** + * This method allows to modify websocket connections that are made by any page in the browser context. + * + * Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this + * method before creating any pages. + * + * **Usage** + * + * Below is an example of a simple handler that blocks some websocket messages. See + * [WebSocketRoute](https://playwright.dev/docs/api/class-websocketroute) for more details and examples. + * + * ```js + * await context.routeWebSocket('/ws', async ws => { + * ws.routeSend(message => { + * if (message === 'to-be-blocked') + * return; + * ws.send(message); + * }); + * await ws.connect(); + * }); + * ``` + * + * @param url Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) context option. + * @param handler Handler function to route the WebSocket. + */ + routeWebSocket(url: string|RegExp|((url: URL) => boolean), handler: ((websocketroute: WebSocketRoute) => Promise<any>|any)): Promise<void>; + /** * **NOTE** Service workers are only supported on Chromium-based browsers. * @@ -8679,12 +9175,15 @@ export interface BrowserContext { * and [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) take * priority over * [browserContext.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-navigation-timeout). + * * @param timeout Maximum navigation time in milliseconds */ setDefaultNavigationTimeout(timeout: number): void; /** - * This setting will change the default maximum time for all the methods accepting `timeout` option. + * This setting will change the default maximum time for all the methods accepting + * [`timeout`](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout-option-timeout) + * option. * * **NOTE** * [page.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-navigation-timeout), @@ -8692,6 +9191,7 @@ export interface BrowserContext { * [browserContext.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-navigation-timeout) * take priority over * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout). + * * @param timeout Maximum time in milliseconds */ setDefaultTimeout(timeout: number): void; @@ -8706,6 +9206,7 @@ export interface BrowserContext { * **NOTE** * [browserContext.setExtraHTTPHeaders(headers)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-extra-http-headers) * does not guarantee the order of headers in the outgoing requests. + * * @param headers An object containing additional HTTP headers to be sent with every request. All header values must be strings. */ setExtraHTTPHeaders(headers: { [key: string]: string; }): Promise<void>; @@ -8722,6 +9223,7 @@ export interface BrowserContext { * **NOTE** Consider using * [browserContext.grantPermissions(permissions[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-grant-permissions) * to grant permissions for the browser context pages to read its geolocation. + * * @param geolocation */ setGeolocation(geolocation: null|{ @@ -8762,8 +9264,10 @@ export interface BrowserContext { */ storageState(options?: { /** - * The file path to save the storage state to. If `path` is a relative path, then it is resolved relative to current - * working directory. If no path is provided, storage state is still returned, but won't be saved to the disk. + * The file path to save the storage state to. If + * [`path`](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state-option-path) is a + * relative path, then it is resolved relative to current working directory. If no path is provided, storage state is + * still returned, but won't be saved to the disk. */ path?: string; }): Promise<{ @@ -8802,7 +9306,9 @@ export interface BrowserContext { /** * Removes a route created with * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route). - * When `handler` is not specified, removes all routes for the `url`. + * When [`handler`](https://playwright.dev/docs/api/class-browsercontext#browser-context-unroute-option-handler) is + * not specified, removes all routes for the + * [`url`](https://playwright.dev/docs/api/class-browsercontext#browser-context-unroute-option-url). * @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route). * @param handler Optional handler function used to register a routing with @@ -8852,8 +9358,8 @@ export interface BrowserContext { /** * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. * - * The arguments passed into `console.log` and the page are available on the {@link ConsoleMessage} event handler - * argument. + * The arguments passed into `console.log` and the page are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -8888,6 +9394,7 @@ export interface BrowserContext { * **NOTE** When no [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) or * [browserContext.on('dialog')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog) * listeners are present, all dialogs are automatically dismissed. + * */ waitForEvent(event: 'dialog', optionsOrPredicate?: { predicate?: (dialog: Dialog) => boolean | Promise<boolean>, timeout?: number } | ((dialog: Dialog) => boolean | Promise<boolean>)): Promise<Dialog>; @@ -8904,7 +9411,7 @@ export interface BrowserContext { * [browserContext.route(url, handler[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) * and * [browserContext.on('request')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request) - * respectively instead of similar methods on the {@link Page}. + * respectively instead of similar methods on the [Page](https://playwright.dev/docs/api/class-page). * * ```js * const newPagePromise = context.waitForEvent('page'); @@ -8916,6 +9423,7 @@ export interface BrowserContext { * **NOTE** Use * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#page-wait-for-load-state) to * wait until the page gets to a particular state (you should not need it in most cases). + * */ waitForEvent(event: 'page', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise<boolean>, timeout?: number } | ((page: Page) => boolean | Promise<boolean>)): Promise<Page>; @@ -8939,6 +9447,7 @@ export interface BrowserContext { * [browserContext.on('requestfinished')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-finished) * event and not with * [browserContext.on('requestfailed')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-request-failed). + * */ waitForEvent(event: 'requestfailed', optionsOrPredicate?: { predicate?: (request: Request) => boolean | Promise<boolean>, timeout?: number } | ((request: Request) => boolean | Promise<boolean>)): Promise<Request>; @@ -8989,7 +9498,8 @@ export interface BrowserContext { /** * A Browser is created via * [browserType.launch([options])](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). An example - * of using a {@link Browser} to create a {@link Page}: + * of using a [Browser](https://playwright.dev/docs/api/class-browser) to create a + * [Page](https://playwright.dev/docs/api/class-page): * * ```js * const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'. @@ -9080,11 +9590,12 @@ export interface Browser { * * **NOTE** This is similar to force quitting the browser. Therefore, you should call * [browserContext.close([options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-close) on - * any {@link BrowserContext}'s you explicitly created earlier with + * any [BrowserContext](https://playwright.dev/docs/api/class-browsercontext)'s you explicitly created earlier with * [browser.newContext([options])](https://playwright.dev/docs/api/class-browser#browser-new-context) **before** * calling [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close). * - * The {@link Browser} object itself is considered to be disposed and cannot be used anymore. + * The [Browser](https://playwright.dev/docs/api/class-browser) object itself is considered to be disposed and cannot + * be used anymore. * @param options */ close(options?: { @@ -9125,12 +9636,13 @@ export interface Browser { /** * Creates a new browser context. It won't share cookies/cache with other browser contexts. * - * **NOTE** If directly using this method to create {@link BrowserContext}s, it is best practice to explicitly close + * **NOTE** If directly using this method to create + * [BrowserContext](https://playwright.dev/docs/api/class-browsercontext)s, it is best practice to explicitly close * the returned context via * [browserContext.close([options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-close) when - * your code is done with the {@link BrowserContext}, and before calling - * [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close). This will ensure the - * `context` is closed gracefully and any artifacts—like HARs and videos—are fully flushed and saved. + * your code is done with the [BrowserContext](https://playwright.dev/docs/api/class-browsercontext), and before + * calling [browser.close([options])](https://playwright.dev/docs/api/class-browser#browser-close). This will ensure + * the `context` is closed gracefully and any artifacts—like HARs and videos—are fully flushed and saved. * * **Usage** * @@ -9204,6 +9716,7 @@ export interface Browser { * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. + * */ clientCertificates?: Array<{ /** @@ -9310,10 +9823,11 @@ export interface Browser { origin?: string; /** - * This option only applies to the requests sent from corresponding {@link APIRequestContext} and does not affect - * requests sent from the browser. `'always'` - `Authorization` header with basic authentication credentials will be - * sent with the each API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response - * with `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. + * This option only applies to the requests sent from corresponding + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) and does not affect requests sent from + * the browser. `'always'` - `Authorization` header with basic authentication credentials will be sent with the each + * API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response with + * `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. */ send?: "unauthorized"|"always"; }; @@ -9421,8 +9935,9 @@ export interface Browser { mode?: "full"|"minimal"; /** - * A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was - * provided and the passed URL is a path, it gets merged via the + * A glob or regex pattern to filter requests that are stored in the HAR. When a + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the context + * options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none. */ urlFilter?: string|RegExp; @@ -9467,7 +9982,7 @@ export interface Browser { /** * Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the - * `viewport` is set. + * [`viewport`](https://playwright.dev/docs/api/class-browser#browser-new-page-option-viewport) is set. */ screen?: { /** @@ -9548,8 +10063,8 @@ export interface Browser { /** * If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on * selectors that imply single target DOM element will throw when more than one element matches the selector. This - * option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See {@link Locator} to - * learn more about the strict mode. + * option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See + * [Locator](https://playwright.dev/docs/api/class-locator) to learn more about the strict mode. */ strictSelectors?: boolean; @@ -9566,7 +10081,7 @@ export interface Browser { userAgent?: string; /** - * @deprecated Use `recordVideo` instead. + * @deprecated Use [`recordVideo`](https://playwright.dev/docs/api/class-browser#browser-new-page-option-record-video) instead. */ videoSize?: { /** @@ -9581,7 +10096,7 @@ export interface Browser { }; /** - * @deprecated Use `recordVideo` instead. + * @deprecated Use [`recordVideo`](https://playwright.dev/docs/api/class-browser#browser-new-page-option-record-video) instead. */ videosPath?: string; @@ -9591,6 +10106,7 @@ export interface Browser { * * **NOTE** The `null` value opts out from the default presets, makes viewport depend on the host window size defined * by the operating system. It makes the execution of the tests non-deterministic. + * */ viewport?: null|{ /** @@ -9681,7 +10197,8 @@ export interface Browser { */ export interface Worker { /** - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-worker#worker-evaluate-option-expression). * * If the function passed to the * [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate) returns a @@ -9696,11 +10213,13 @@ export interface Worker { * `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: * `-0`, `NaN`, `Infinity`, `-Infinity`. * @param pageFunction Function to be evaluated in the worker context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-worker#worker-evaluate-option-expression). */ evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>; /** - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-worker#worker-evaluate-option-expression). * * If the function passed to the * [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate) returns a @@ -9715,19 +10234,22 @@ export interface Worker { * `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: * `-0`, `NaN`, `Infinity`, `-Infinity`. * @param pageFunction Function to be evaluated in the worker context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-worker#worker-evaluate-option-expression). */ evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<R>; /** - * Returns the return value of `pageFunction` as a {@link JSHandle}. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle-option-expression) as a + * [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * The only difference between * [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate) and * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle) * is that * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle) - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function passed to the * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle) @@ -9735,18 +10257,21 @@ export interface Worker { * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle) * would wait for the promise to resolve and return its value. * @param pageFunction Function to be evaluated in the worker context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle-option-expression). */ evaluateHandle<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<SmartHandle<R>>; /** - * Returns the return value of `pageFunction` as a {@link JSHandle}. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle-option-expression) as a + * [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * The only difference between * [worker.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate) and * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle) * is that * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle) - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function passed to the * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle) @@ -9754,7 +10279,8 @@ export interface Worker { * [worker.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle) * would wait for the promise to resolve and return its value. * @param pageFunction Function to be evaluated in the worker context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-worker#worker-evaluate-handle-option-expression). */ evaluateHandle<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<SmartHandle<R>>; /** @@ -9814,12 +10340,14 @@ export interface Worker { */ export interface JSHandle<T = any> { /** - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression). * - * This method passes this handle as the first argument to `pageFunction`. + * This method passes this handle as the first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression). * - * If `pageFunction` returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its - * value. + * If [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression) + * returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its value. * * **Usage** * @@ -9829,16 +10357,19 @@ export interface JSHandle<T = any> { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression). */ evaluate<R, Arg, O extends T = T>(pageFunction: PageFunctionOn<O, Arg, R>, arg: Arg): Promise<R>; /** - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression). * - * This method passes this handle as the first argument to `pageFunction`. + * This method passes this handle as the first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression). * - * If `pageFunction` returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its - * value. + * If [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression) + * returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its value. * * **Usage** * @@ -9848,17 +10379,21 @@ export interface JSHandle<T = any> { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-option-expression). */ evaluate<R, O extends T = T>(pageFunction: PageFunctionOn<O, void, R>, arg?: any): Promise<R>; /** - * Returns the return value of `pageFunction` as a {@link JSHandle}. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression) + * as a [JSHandle](https://playwright.dev/docs/api/class-jshandle). * - * This method passes this handle as the first argument to `pageFunction`. + * This method passes this handle as the first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression). * * The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle` - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would * wait for the promise to resolve and return its value. @@ -9866,16 +10401,20 @@ export interface JSHandle<T = any> { * See [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) for * more details. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression). */ evaluateHandle<R, Arg, O extends T = T>(pageFunction: PageFunctionOn<O, Arg, R>, arg: Arg): Promise<SmartHandle<R>>; /** - * Returns the return value of `pageFunction` as a {@link JSHandle}. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression) + * as a [JSHandle](https://playwright.dev/docs/api/class-jshandle). * - * This method passes this handle as the first argument to `pageFunction`. + * This method passes this handle as the first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression). * * The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle` - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would * wait for the promise to resolve and return its value. @@ -9883,7 +10422,8 @@ export interface JSHandle<T = any> { * See [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) for * more details. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-evaluate-handle-option-expression). */ evaluateHandle<R, O extends T = T>(pageFunction: PageFunctionOn<O, void, R>, arg?: any): Promise<SmartHandle<R>>; @@ -9892,10 +10432,12 @@ export interface JSHandle<T = any> { * * **NOTE** The method will return an empty JSON object if the referenced object is not stringifiable. It will throw * an error if the object has circular references. + * */ jsonValue(): Promise<T>; /** - * Returns either `null` or the object handle itself, if the object handle is an instance of {@link ElementHandle}. + * Returns either `null` or the object handle itself, if the object handle is an instance of + * [ElementHandle](https://playwright.dev/docs/api/class-elementhandle). */ asElement(): T extends Node ? ElementHandle<T> : null; /** @@ -9929,12 +10471,13 @@ export interface JSHandle<T = any> { } /** - * - extends: {@link JSHandle} + * - extends: [JSHandle](https://playwright.dev/docs/api/class-jshandle) * * ElementHandle represents an in-page DOM element. ElementHandles can be created with the * [page.$(selector[, options])](https://playwright.dev/docs/api/class-page#page-query-selector) method. * - * **NOTE** The use of ElementHandle is discouraged, use {@link Locator} objects and web-first assertions instead. + * **NOTE** The use of ElementHandle is discouraged, use [Locator](https://playwright.dev/docs/api/class-locator) + * objects and web-first assertions instead. * * ```js * const hrefElement = await page.$('a'); @@ -9949,8 +10492,9 @@ export interface JSHandle<T = any> { * [page.$eval(selector, pageFunction[, arg, options])](https://playwright.dev/docs/api/class-page#page-eval-on-selector) * and [page.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate) methods. * - * The difference between the {@link Locator} and ElementHandle is that the ElementHandle points to a particular - * element, while {@link Locator} captures the logic of how to retrieve an element. + * The difference between the [Locator](https://playwright.dev/docs/api/class-locator) and ElementHandle is that the + * ElementHandle points to a particular element, while [Locator](https://playwright.dev/docs/api/class-locator) + * captures the logic of how to retrieve an element. * * In the example below, handle points to a particular DOM element on page. If that element changes text or is used by * React to render an entirely different component, handle is still pointing to that very DOM element. This can lead @@ -10017,14 +10561,19 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. * Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). * * The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a - * first argument to `pageFunction`. If no elements match the selector, the method throws an error. + * first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). + * If no elements match the selector, the method throws an error. * - * If `pageFunction` returns a [Promise], then + * If + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression) + * returns a [Promise], then * [elementHandle.$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -10038,21 +10587,27 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). */ $eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], Arg, R>, arg: Arg): Promise<R>; /** * **NOTE** This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. * Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). * * The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a - * first argument to `pageFunction`. If no elements match the selector, the method throws an error. + * first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). + * If no elements match the selector, the method throws an error. * - * If `pageFunction` returns a [Promise], then + * If + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression) + * returns a [Promise], then * [elementHandle.$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -10066,21 +10621,27 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). */ $eval<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E, Arg, R>, arg: Arg): Promise<R>; /** * **NOTE** This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. * Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). * * The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a - * first argument to `pageFunction`. If no elements match the selector, the method throws an error. + * first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). + * If no elements match the selector, the method throws an error. * - * If `pageFunction` returns a [Promise], then + * If + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression) + * returns a [Promise], then * [elementHandle.$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -10094,21 +10655,27 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). */ $eval<K extends keyof HTMLElementTagNameMap, R>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K], void, R>, arg?: any): Promise<R>; /** * **NOTE** This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. * Use * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate), - * other {@link Locator} helper methods or web-first assertions instead. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods or web-first assertions instead. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). * * The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a - * first argument to `pageFunction`. If no elements match the selector, the method throws an error. + * first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). + * If no elements match the selector, the method throws an error. * - * If `pageFunction` returns a [Promise], then + * If + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression) + * returns a [Promise], then * [elementHandle.$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector) * would wait for the promise to resolve and return its value. * @@ -10122,21 +10689,27 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-option-expression). */ $eval<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E, void, R>, arg?: any): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). * * The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array - * of matched elements as a first argument to `pageFunction`. + * of matched elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). * - * If `pageFunction` returns a [Promise], then + * If + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression) + * returns a [Promise], then * [elementHandle.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -10158,20 +10731,26 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). */ $$eval<K extends keyof HTMLElementTagNameMap, R, Arg>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], Arg, R>, arg: Arg): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). * * The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array - * of matched elements as a first argument to `pageFunction`. + * of matched elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). * - * If `pageFunction` returns a [Promise], then + * If + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression) + * returns a [Promise], then * [elementHandle.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -10193,20 +10772,26 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). */ $$eval<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E[], Arg, R>, arg: Arg): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). * * The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array - * of matched elements as a first argument to `pageFunction`. + * of matched elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). * - * If `pageFunction` returns a [Promise], then + * If + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression) + * returns a [Promise], then * [elementHandle.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -10228,20 +10813,26 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). */ $$eval<K extends keyof HTMLElementTagNameMap, R>(selector: K, pageFunction: PageFunctionOn<HTMLElementTagNameMap[K][], void, R>, arg?: any): Promise<R>; /** * **NOTE** In most cases, * [locator.evaluateAll(pageFunction[, arg])](https://playwright.dev/docs/api/class-locator#locator-evaluate-all), - * other {@link Locator} helper methods and web-first assertions do a better job. + * other [Locator](https://playwright.dev/docs/api/class-locator) helper methods and web-first assertions do a better + * job. * - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). * * The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array - * of matched elements as a first argument to `pageFunction`. + * of matched elements as a first argument to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). * - * If `pageFunction` returns a [Promise], then + * If + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression) + * returns a [Promise], then * [elementHandle.$$eval(selector, pageFunction[, arg])](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all) * would wait for the promise to resolve and return its value. * @@ -10263,7 +10854,8 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * @param selector A selector to query for. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-elementhandle#element-handle-eval-on-selector-all-option-expression). */ $$eval<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(selector: string, pageFunction: PageFunctionOn<E[], void, R>, arg?: any): Promise<R>; @@ -10271,13 +10863,20 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** Use web assertions that assert visibility or a locator-based * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. * - * Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns element specified by selector when it satisfies + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-state) + * option. Returns `null` if waiting for `hidden` or `detached`. * - * Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom, - * or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the - * method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the - * function will throw. + * Wait for the + * [`selector`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-selector) + * relative to the element handle to satisfy + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-state) option + * (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-selector) + * already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition + * for the + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-timeout) + * milliseconds, the function will throw. * * **Usage** * @@ -10291,6 +10890,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** This method does not work across navigations, use * [page.waitForSelector(selector[, options])](https://playwright.dev/docs/api/class-page#page-wait-for-selector) * instead. + * * @param selector A selector to query for. * @param options */ @@ -10299,13 +10899,20 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** Use web assertions that assert visibility or a locator-based * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. * - * Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns element specified by selector when it satisfies + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-state) + * option. Returns `null` if waiting for `hidden` or `detached`. * - * Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom, - * or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the - * method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the - * function will throw. + * Wait for the + * [`selector`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-selector) + * relative to the element handle to satisfy + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-state) option + * (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-selector) + * already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition + * for the + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-timeout) + * milliseconds, the function will throw. * * **Usage** * @@ -10319,6 +10926,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** This method does not work across navigations, use * [page.waitForSelector(selector[, options])](https://playwright.dev/docs/api/class-page#page-wait-for-selector) * instead. + * * @param selector A selector to query for. * @param options */ @@ -10327,13 +10935,20 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** Use web assertions that assert visibility or a locator-based * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. * - * Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns element specified by selector when it satisfies + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-state) + * option. Returns `null` if waiting for `hidden` or `detached`. * - * Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom, - * or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the - * method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the - * function will throw. + * Wait for the + * [`selector`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-selector) + * relative to the element handle to satisfy + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-state) option + * (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-selector) + * already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition + * for the + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-timeout) + * milliseconds, the function will throw. * * **Usage** * @@ -10347,6 +10962,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** This method does not work across navigations, use * [page.waitForSelector(selector[, options])](https://playwright.dev/docs/api/class-page#page-wait-for-selector) * instead. + * * @param selector A selector to query for. * @param options */ @@ -10355,13 +10971,20 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** Use web assertions that assert visibility or a locator-based * [locator.waitFor([options])](https://playwright.dev/docs/api/class-locator#locator-wait-for) instead. * - * Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or - * `detached`. + * Returns element specified by selector when it satisfies + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-state) + * option. Returns `null` if waiting for `hidden` or `detached`. * - * Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom, - * or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the - * method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the - * function will throw. + * Wait for the + * [`selector`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-selector) + * relative to the element handle to satisfy + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-state) option + * (either appear/disappear from dom, or become visible/hidden). If at the moment of calling the method + * [`selector`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-selector) + * already satisfies the condition, the method will return immediately. If the selector doesn't satisfy the condition + * for the + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector-option-timeout) + * milliseconds, the function will throw. * * **Usage** * @@ -10375,6 +10998,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * **NOTE** This method does not work across navigations, use * [page.waitForSelector(selector[, options])](https://playwright.dev/docs/api/class-page#page-wait-for-selector) * instead. + * * @param selector A selector to query for. * @param options */ @@ -10430,7 +11054,9 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * This method checks the element by performing the following steps: * 1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already * checked, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-elementhandle#element-handle-check-option-force) option is + * set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. @@ -10438,8 +11064,9 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-check-option-timeout), this method + * throws a [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param options */ check(options?: { @@ -10484,16 +11111,22 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * Read more about [locators](https://playwright.dev/docs/locators). * * This method clicks the element by performing the following steps: - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-elementhandle#element-handle-click-option-force) option is + * set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the - * element, or the specified `position`. - * 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-elementhandle#element-handle-click-option-position). + * 1. Wait for initiated navigations to either succeed or fail, unless + * [`noWaitAfter`](https://playwright.dev/docs/api/class-elementhandle#element-handle-click-option-no-wait-after) + * option is set. * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-click-option-timeout), this method + * throws a [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param options */ click(options?: { @@ -10567,17 +11200,23 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * instead. Read more about [locators](https://playwright.dev/docs/locators). * * This method double clicks the element by performing the following steps: - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-elementhandle#element-handle-dblclick-option-force) option is + * set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to double click in the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-elementhandle#element-handle-dblclick-option-position). * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-dblclick-option-timeout), this + * method throws a [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables + * this. * * **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event. + * * @param options */ dblclick(options?: { @@ -10649,10 +11288,15 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * await elementHandle.dispatchEvent('click'); * ``` * - * Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` + * Under the hood, it creates an instance of an event based on the given + * [`type`](https://playwright.dev/docs/api/class-elementhandle#element-handle-dispatch-event-option-type), + * initializes it with + * [`eventInit`](https://playwright.dev/docs/api/class-elementhandle#element-handle-dispatch-event-option-event-init) * properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. * - * Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties: + * Since + * [`eventInit`](https://playwright.dev/docs/api/class-elementhandle#element-handle-dispatch-event-option-event-init) + * is event-specific, please refer to the events documentation for the lists of initial properties: * - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent) * - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent) * - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent) @@ -10738,15 +11382,19 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * Read more about [locators](https://playwright.dev/docs/locators). * * This method hovers over the element by performing the following steps: - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-elementhandle#element-handle-hover-option-force) option is + * set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to hover over the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-elementhandle#element-handle-hover-option-position). * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-hover-option-timeout), this method + * throws a [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param options */ hover(options?: { @@ -10894,9 +11542,10 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * [keyboard.down(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-down) and * [keyboard.up(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-up). * - * `key` can specify the intended - * [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character - * to generate the text for. A superset of the `key` values can be found + * [`key`](https://playwright.dev/docs/api/class-elementhandle#element-handle-press-option-key) can specify the + * intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single + * character to generate the text for. A superset of the + * [`key`](https://playwright.dev/docs/api/class-elementhandle#element-handle-press-option-key) values can be found * [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are: * * `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, @@ -10906,10 +11555,11 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, * `ControlOrMeta`. * - * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. + * Holding down `Shift` will type the text that corresponds to the + * [`key`](https://playwright.dev/docs/api/class-elementhandle#element-handle-press-option-key) in the upper case. * - * If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective - * texts. + * If [`key`](https://playwright.dev/docs/api/class-elementhandle#element-handle-press-option-key) is a single + * character, it is case-sensitive, so the values `a` and `A` will generate different respective texts. * * Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When * specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed. @@ -10972,7 +11622,9 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { /** * Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink - * box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. + * box `#FF00FF` (customized by + * [`maskColor`](https://playwright.dev/docs/api/class-elementhandle#element-handle-screenshot-option-mask-color)) + * that completely covers its bounding box. */ mask?: Array<Locator>; @@ -10989,9 +11641,10 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { omitBackground?: boolean; /** - * The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a - * relative path, then it is resolved relative to the current working directory. If no path is provided, the image - * won't be saved to the disk. + * The file path to save the image to. The screenshot type will be inferred from file extension. If + * [`path`](https://playwright.dev/docs/api/class-elementhandle#element-handle-screenshot-option-path) is a relative + * path, then it is resolved relative to the current working directory. If no path is provided, the image won't be + * saved to the disk. */ path?: string; @@ -11177,15 +11830,18 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * This method checks or unchecks an element by performing the following steps: * 1. Ensure that element is a checkbox or a radio input. If not, this method throws. * 1. If the element already has the right checked state, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If - * the element is detached during the checks, the whole action is retried. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-elementhandle#element-handle-set-checked-option-force) option + * is set. If the element is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. * 1. Ensure that the element is now checked or unchecked. If not, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-set-checked-option-timeout), this + * method throws a [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables + * this. * @param checked Whether to check or uncheck the checkbox. * @param options */ @@ -11235,7 +11891,7 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * they are resolved relative to the current working directory. For empty array, clears the selected files. For inputs * with a `[webkitdirectory]` attribute, only a single directory path is supported. * - * This method expects {@link ElementHandle} to point to an + * This method expects [ElementHandle](https://playwright.dev/docs/api/class-elementhandle) to point to an * [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside * the `<label>` element that has an associated * [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead. @@ -11293,17 +11949,21 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * more about [locators](https://playwright.dev/docs/locators). * * This method taps the element by performing the following steps: - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-elementhandle#element-handle-tap-option-force) option is set. * 1. Scroll the element into view if needed. * 1. Use [page.touchscreen](https://playwright.dev/docs/api/class-page#page-touchscreen) to tap the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-elementhandle#element-handle-tap-option-position). * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-tap-option-timeout), this method + * throws a [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **NOTE** `elementHandle.tap()` requires that the `hasTouch` option of the browser context be set to true. + * * @param options */ tap(options?: { @@ -11402,7 +12062,9 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * This method checks the element by performing the following steps: * 1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already * unchecked, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-elementhandle#element-handle-uncheck-option-force) option is + * set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. @@ -11410,8 +12072,9 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-uncheck-option-timeout), this method + * throws a [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param options */ uncheck(options?: { @@ -11452,10 +12115,13 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { }): Promise<void>; /** - * Returns when the element satisfies the `state`. + * Returns when the element satisfies the + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-element-state-option-state). * - * Depending on the `state` parameter, this method waits for one of the [actionability](https://playwright.dev/docs/actionability) checks to - * pass. This method throws when the element is detached while waiting, unless waiting for the `"hidden"` state. + * Depending on the + * [`state`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-element-state-option-state) + * parameter, this method waits for one of the [actionability](https://playwright.dev/docs/actionability) checks to pass. This method throws + * when the element is detached while waiting, unless waiting for the `"hidden"` state. * - `"visible"` Wait until the element is [visible](https://playwright.dev/docs/actionability#visible). * - `"hidden"` Wait until the element is [not visible](https://playwright.dev/docs/actionability#visible) or not attached. Note that * waiting for hidden does not throw when the element detaches. @@ -11465,7 +12131,9 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * - `"disabled"` Wait until the element is [not enabled](https://playwright.dev/docs/actionability#enabled). * - `"editable"` Wait until the element is [editable](https://playwright.dev/docs/actionability#editable). * - * If the element does not satisfy the condition for the `timeout` milliseconds, this method will throw. + * If the element does not satisfy the condition for the + * [`timeout`](https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-element-state-option-timeout) + * milliseconds, this method will throw. * @param state A state to wait for, see below for more details. * @param options */ @@ -11493,12 +12161,16 @@ export interface Locator { * * **Details** * - * Returns the return value of `pageFunction`, called with the matching element as a first argument, and `arg` as a - * second argument. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression), called with the + * matching element as a first argument, and + * [`arg`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-arg) as a second argument. * - * If `pageFunction` returns a [Promise], this method will wait for the promise to resolve and return its value. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression) returns a + * [Promise], this method will wait for the promise to resolve and return its value. * - * If `pageFunction` throws or rejects, this method throws. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression) throws or + * rejects, this method throws. * * **Usage** * @@ -11508,7 +12180,8 @@ export interface Locator { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression). * @param options */ evaluate<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, Arg, R>, arg: Arg, options?: { @@ -11519,12 +12192,16 @@ export interface Locator { * * **Details** * - * Returns the return value of `pageFunction`, called with the matching element as a first argument, and `arg` as a - * second argument. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression), called with the + * matching element as a first argument, and + * [`arg`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-arg) as a second argument. * - * If `pageFunction` returns a [Promise], this method will wait for the promise to resolve and return its value. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression) returns a + * [Promise], this method will wait for the promise to resolve and return its value. * - * If `pageFunction` throws or rejects, this method throws. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression) throws or + * rejects, this method throws. * * **Usage** * @@ -11534,20 +12211,23 @@ export interface Locator { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-option-expression). * @param options */ evaluate<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, void, R>, options?: { timeout?: number; }): Promise<R>; /** - * Execute JavaScript code in the page, taking the matching element as an argument, and return a {@link JSHandle} with - * the result. + * Execute JavaScript code in the page, taking the matching element as an argument, and return a + * [JSHandle](https://playwright.dev/docs/api/class-jshandle) with the result. * * **Details** * - * Returns the return value of `pageFunction` as a{@link JSHandle}, called with the matching element as a first - * argument, and `arg` as a second argument. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression) as + * a[JSHandle](https://playwright.dev/docs/api/class-jshandle), called with the matching element as a first argument, + * and [`arg`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-arg) as a second argument. * * The only difference between * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate) @@ -11555,27 +12235,32 @@ export interface Locator { * [locator.evaluateHandle(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle) * is that * [locator.evaluateHandle(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle) - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * - * If `pageFunction` returns a [Promise], this method will wait for the promise to resolve and return its value. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression) + * returns a [Promise], this method will wait for the promise to resolve and return its value. * - * If `pageFunction` throws or rejects, this method throws. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression) throws + * or rejects, this method throws. * * See [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) for * more details. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression). * @param options */ evaluateHandle<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, Arg, R>, arg: Arg): Promise<SmartHandle<R>>; /** - * Execute JavaScript code in the page, taking the matching element as an argument, and return a {@link JSHandle} with - * the result. + * Execute JavaScript code in the page, taking the matching element as an argument, and return a + * [JSHandle](https://playwright.dev/docs/api/class-jshandle) with the result. * * **Details** * - * Returns the return value of `pageFunction` as a{@link JSHandle}, called with the matching element as a first - * argument, and `arg` as a second argument. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression) as + * a[JSHandle](https://playwright.dev/docs/api/class-jshandle), called with the matching element as a first argument, + * and [`arg`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-arg) as a second argument. * * The only difference between * [locator.evaluate(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate) @@ -11583,16 +12268,19 @@ export interface Locator { * [locator.evaluateHandle(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle) * is that * [locator.evaluateHandle(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle) - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * - * If `pageFunction` returns a [Promise], this method will wait for the promise to resolve and return its value. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression) + * returns a [Promise], this method will wait for the promise to resolve and return its value. * - * If `pageFunction` throws or rejects, this method throws. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression) throws + * or rejects, this method throws. * * See [page.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-page#page-evaluate-handle) for * more details. * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-handle-option-expression). * @param options */ evaluateHandle<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E, void, R>): Promise<SmartHandle<R>>; @@ -11601,12 +12289,16 @@ export interface Locator { * * **Details** * - * Returns the return value of `pageFunction`, called with an array of all matching elements as a first argument, and - * `arg` as a second argument. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-expression), called with + * an array of all matching elements as a first argument, and + * [`arg`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-arg) as a second argument. * - * If `pageFunction` returns a [Promise], this method will wait for the promise to resolve and return its value. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-expression) returns a + * [Promise], this method will wait for the promise to resolve and return its value. * - * If `pageFunction` throws or rejects, this method throws. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-expression) throws or + * rejects, this method throws. * * **Usage** * @@ -11616,7 +12308,8 @@ export interface Locator { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-expression). */ evaluateAll<R, Arg, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E[], Arg, R>, arg: Arg): Promise<R>; /** @@ -11624,12 +12317,16 @@ export interface Locator { * * **Details** * - * Returns the return value of `pageFunction`, called with an array of all matching elements as a first argument, and - * `arg` as a second argument. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-expression), called with + * an array of all matching elements as a first argument, and + * [`arg`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-arg) as a second argument. * - * If `pageFunction` returns a [Promise], this method will wait for the promise to resolve and return its value. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-expression) returns a + * [Promise], this method will wait for the promise to resolve and return its value. * - * If `pageFunction` throws or rejects, this method throws. + * If [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-expression) throws or + * rejects, this method throws. * * **Usage** * @@ -11639,12 +12336,13 @@ export interface Locator { * ``` * * @param pageFunction Function to be evaluated in the page context. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-locator#locator-evaluate-all-option-expression). */ evaluateAll<R, E extends SVGElement | HTMLElement = SVGElement | HTMLElement>(pageFunction: PageFunctionOn<E[], void, R>): Promise<R>; /** - * **NOTE** Always prefer using {@link Locator}s and web assertions over {@link ElementHandle}s because latter are inherently - * racy. + * **NOTE** Always prefer using [Locator](https://playwright.dev/docs/api/class-locator)s and web assertions over + * [ElementHandle](https://playwright.dev/docs/api/class-elementhandle)s because latter are inherently racy. * * Resolves given locator to the first matching DOM element. If there are no matching elements, waits for one. If * multiple elements match the locator, throws. @@ -11658,10 +12356,14 @@ export interface Locator { * elements. * * **NOTE** [locator.all()](https://playwright.dev/docs/api/class-locator#locator-all) does not wait for elements to - * match the locator, and instead immediately returns whatever is present in the page. When the list of elements - * changes dynamically, [locator.all()](https://playwright.dev/docs/api/class-locator#locator-all) will produce - * unpredictable and flaky results. When the list of elements is stable, but loaded dynamically, wait for the full - * list to finish loading before calling [locator.all()](https://playwright.dev/docs/api/class-locator#locator-all). + * match the locator, and instead immediately returns whatever is present in the page. + * + * When the list of elements changes dynamically, + * [locator.all()](https://playwright.dev/docs/api/class-locator#locator-all) will produce unpredictable and flaky + * results. + * + * When the list of elements is stable, but loaded dynamically, wait for the full list to finish loading before + * calling [locator.all()](https://playwright.dev/docs/api/class-locator#locator-all). * * **Usage** * @@ -11678,7 +12380,9 @@ export interface Locator { * * **NOTE** If you need to assert text on the page, prefer * [expect(locator).toHaveText(expected[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-text) - * with `useInnerText` option to avoid flakiness. See [assertions guide](https://playwright.dev/docs/test-assertions) for more details. + * with + * [`useInnerText`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-text-option-use-inner-text) + * option to avoid flakiness. See [assertions guide](https://playwright.dev/docs/test-assertions) for more details. * * **Usage** * @@ -11798,7 +12502,8 @@ export interface Locator { * Performs the following steps: * 1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already * checked, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-locator#locator-check-option-force) option is set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. @@ -11806,8 +12511,9 @@ export interface Locator { * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-locator#locator-check-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **Usage** * @@ -11902,16 +12608,21 @@ export interface Locator { * **Details** * * This method clicks the element by performing the following steps: - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-locator#locator-click-option-force) option is set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the - * element, or the specified `position`. - * 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-locator#locator-click-option-position). + * 1. Wait for initiated navigations to either succeed or fail, unless + * [`noWaitAfter`](https://playwright.dev/docs/api/class-locator#locator-click-option-no-wait-after) option is + * set. * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-locator#locator-click-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **Usage** * @@ -11989,16 +12700,19 @@ export interface Locator { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; /** - * Returns a {@link FrameLocator} object pointing to the same `iframe` as this locator. + * Returns a [FrameLocator](https://playwright.dev/docs/api/class-framelocator) object pointing to the same `iframe` + * as this locator. * - * Useful when you have a {@link Locator} object obtained somewhere, and later on would like to interact with the - * content inside the frame. + * Useful when you have a [Locator](https://playwright.dev/docs/api/class-locator) object obtained somewhere, and + * later on would like to interact with the content inside the frame. * * For a reverse operation, use * [frameLocator.owner()](https://playwright.dev/docs/api/class-framelocator#frame-locator-owner). @@ -12037,17 +12751,21 @@ export interface Locator { * **Details** * * This method double clicks the element by performing the following steps: - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-locator#locator-dblclick-option-force) option is set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to double click in the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-locator#locator-dblclick-option-position). * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-locator#locator-dblclick-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **NOTE** `element.dblclick()` dispatches two `click` events and a single `dblclick` event. + * * @param options */ dblclick(options?: { @@ -12099,7 +12817,9 @@ export interface Locator { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -12119,10 +12839,13 @@ export interface Locator { * `click` is dispatched. This is equivalent to calling * [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click). * - * Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` - * properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. + * Under the hood, it creates an instance of an event based on the given + * [`type`](https://playwright.dev/docs/api/class-locator#locator-dispatch-event-option-type), initializes it with + * [`eventInit`](https://playwright.dev/docs/api/class-locator#locator-dispatch-event-option-event-init) properties + * and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. * - * Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties: + * Since [`eventInit`](https://playwright.dev/docs/api/class-locator#locator-dispatch-event-option-event-init) is + * event-specific, please refer to the events documentation for the lists of initial properties: * - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent) * - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent) * - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent) @@ -12134,7 +12857,8 @@ export interface Locator { * - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent) * - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent) * - * You can also specify {@link JSHandle} as the property value if you want live objects to be passed into the event: + * You can also specify [JSHandle](https://playwright.dev/docs/api/class-jshandle) as the property value if you want + * live objects to be passed into the event: * * ```js * // Note you can only create DataTransfer in Chromium and Firefox @@ -12229,8 +12953,8 @@ export interface Locator { }): Promise<void>; /** - * **NOTE** Always prefer using {@link Locator}s and web assertions over {@link ElementHandle}s because latter are inherently - * racy. + * **NOTE** Always prefer using [Locator](https://playwright.dev/docs/api/class-locator)s and web assertions over + * [ElementHandle](https://playwright.dev/docs/api/class-elementhandle)s because latter are inherently racy. * * Resolves given locator to all matching DOM elements. If there are no matching elements, returns an empty list. */ @@ -12308,8 +13032,8 @@ export interface Locator { * `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article * div` will fail, because the inner locator must be relative and should not use any elements outside the `content`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ has?: Locator; @@ -12317,8 +13041,8 @@ export interface Locator { * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the * outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ hasNot?: Locator; @@ -12376,6 +13100,7 @@ export interface Locator { * **NOTE** If you need to assert an element's attribute, prefer * [expect(locator).toHaveAttribute(name, value[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-attribute) * to avoid flakiness. See [assertions guide](https://playwright.dev/docs/test-assertions) for more details. + * * @param name Attribute name to get the value for. * @param options */ @@ -12528,12 +13253,15 @@ export interface Locator { * * **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about * [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled). + * */ disabled?: boolean; /** - * Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a - * regular expression. Note that exact match still trims whitespace. + * Whether [`name`](https://playwright.dev/docs/api/class-locator#locator-get-by-role-option-name) is matched exactly: + * case-sensitive and whole-string. Defaults to false. Ignored when + * [`name`](https://playwright.dev/docs/api/class-locator#locator-get-by-role-option-name) is a regular expression. + * Note that exact match still trims whitespace. */ exact?: boolean; @@ -12562,7 +13290,8 @@ export interface Locator { /** * Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is - * case-insensitive and searches for a substring, use `exact` to control this behavior. + * case-insensitive and searches for a substring, use + * [`exact`](https://playwright.dev/docs/api/class-locator#locator-get-by-role-option-exact) to control this behavior. * * Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). */ @@ -12719,15 +13448,18 @@ export interface Locator { * **Details** * * This method hovers over the element by performing the following steps: - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-locator#locator-hover-option-force) option is set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to hover over the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-locator#locator-hover-option-position). * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-locator#locator-hover-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param options */ hover(options?: { @@ -12769,7 +13501,9 @@ export interface Locator { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -12793,7 +13527,10 @@ export interface Locator { * * **NOTE** If you need to assert text on the page, prefer * [expect(locator).toHaveText(expected[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-text) - * with `useInnerText` option to avoid flakiness. See [assertions guide](https://playwright.dev/docs/test-assertions) for more details. + * with + * [`useInnerText`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-text-option-use-inner-text) + * option to avoid flakiness. See [assertions guide](https://playwright.dev/docs/test-assertions) for more details. + * * @param options */ innerText(options?: { @@ -13016,8 +13753,8 @@ export interface Locator { * `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article * div` will fail, because the inner locator must be relative and should not use any elements outside the `content`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ has?: Locator; @@ -13025,8 +13762,8 @@ export interface Locator { * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the * outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ hasNot?: Locator; @@ -13101,9 +13838,10 @@ export interface Locator { * [keyboard.down(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-down) and * [keyboard.up(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-up). * - * `key` can specify the intended + * [`key`](https://playwright.dev/docs/api/class-locator#locator-press-option-key) can specify the intended * [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character - * to generate the text for. A superset of the `key` values can be found + * to generate the text for. A superset of the + * [`key`](https://playwright.dev/docs/api/class-locator#locator-press-option-key) values can be found * [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are: * * `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, @@ -13113,10 +13851,11 @@ export interface Locator { * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * - * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. + * Holding down `Shift` will type the text that corresponds to the + * [`key`](https://playwright.dev/docs/api/class-locator#locator-press-option-key) in the upper case. * - * If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective - * texts. + * If [`key`](https://playwright.dev/docs/api/class-locator#locator-press-option-key) is a single character, it is + * case-sensitive, so the values `a` and `A` will generate different respective texts. * * Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When * specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed. @@ -13375,15 +14114,17 @@ export interface Locator { * This method checks or unchecks an element by performing the following steps: * 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. * 1. If the element already has the right checked state, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless `force` option is set. If + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the matched element, unless + * [`force`](https://playwright.dev/docs/api/class-locator#locator-set-checked-option-force) option is set. If * the element is detached during the checks, the whole action is retried. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. * 1. Ensure that the element is now checked or unchecked. If not, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-locator#locator-set-checked-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param checked Whether to check or uncheck the checkbox. * @param options */ @@ -13459,7 +14200,7 @@ export interface Locator { * Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then * they are resolved relative to the current working directory. For empty array, clears the selected files. * - * This method expects {@link Locator} to point to an + * This method expects [Locator](https://playwright.dev/docs/api/class-locator) to point to an * [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside * the `<label>` element that has an associated * [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead. @@ -13518,17 +14259,21 @@ export interface Locator { * **Details** * * This method taps the element by performing the following steps: - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-locator#locator-tap-option-force) option is set. * 1. Scroll the element into view if needed. * 1. Use [page.touchscreen](https://playwright.dev/docs/api/class-page#page-touchscreen) to tap the center of the - * element, or the specified `position`. + * element, or the specified + * [`position`](https://playwright.dev/docs/api/class-locator#locator-tap-option-position). * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-locator#locator-tap-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * * **NOTE** `element.tap()` requires that the `hasTouch` option of the browser context be set to true. + * * @param options */ tap(options?: { @@ -13570,7 +14315,9 @@ export interface Locator { /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults - * to `false`. Useful to wait until the element is ready for the action without performing it. + * to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard + * `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys + * are pressed. */ trial?: boolean; }): Promise<void>; @@ -13581,6 +14328,7 @@ export interface Locator { * **NOTE** If you need to assert text on the page, prefer * [expect(locator).toHaveText(expected[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-text) * to avoid flakiness. See [assertions guide](https://playwright.dev/docs/test-assertions) for more details. + * * @param options */ textContent(options?: { @@ -13643,7 +14391,8 @@ export interface Locator { * This method unchecks the element by performing the following steps: * 1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already * unchecked, this method returns immediately. - * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless `force` option is set. + * 1. Wait for [actionability](https://playwright.dev/docs/actionability) checks on the element, unless + * [`force`](https://playwright.dev/docs/api/class-locator#locator-uncheck-option-force) option is set. * 1. Scroll the element into view if needed. * 1. Use [page.mouse](https://playwright.dev/docs/api/class-page#page-mouse) to click in the center of the * element. @@ -13651,8 +14400,9 @@ export interface Locator { * * If the element is detached from the DOM at any moment during the action, this method throws. * - * When all steps combined have not finished during the specified `timeout`, this method throws a {@link - * TimeoutError}. Passing zero timeout disables this. + * When all steps combined have not finished during the specified + * [`timeout`](https://playwright.dev/docs/api/class-locator#locator-uncheck-option-timeout), this method throws a + * [TimeoutError](https://playwright.dev/docs/api/class-timeouterror). Passing zero timeout disables this. * @param options */ uncheck(options?: { @@ -13693,10 +14443,12 @@ export interface Locator { }): Promise<void>; /** - * Returns when element specified by locator satisfies the `state` option. + * Returns when element specified by locator satisfies the + * [`state`](https://playwright.dev/docs/api/class-locator#locator-wait-for-option-state) option. * * If target element already satisfies the condition, the method returns immediately. Otherwise, waits for up to - * `timeout` milliseconds until the condition is met. + * [`timeout`](https://playwright.dev/docs/api/class-locator#locator-wait-for-option-timeout) milliseconds until the + * condition is met. * * **Usage** * @@ -13825,7 +14577,9 @@ export interface BrowserType<Unused = {}> { * * **Usage** * - * You can use `ignoreDefaultArgs` to filter out `--mute-audio` from default arguments: + * You can use + * [`ignoreDefaultArgs`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-ignore-default-args) + * to filter out `--mute-audio` from default arguments: * * ```js * const browser = await chromium.launch({ // Or 'firefox' or 'webkit'. @@ -13835,7 +14589,9 @@ export interface BrowserType<Unused = {}> { * * > **Chromium-only** Playwright can also be used to control the Google Chrome or Microsoft Edge browsers, but it * works best with the version of Chromium it is bundled with. There is no guarantee it will work with any other - * version. Use `executablePath` option with extreme caution. + * version. Use + * [`executablePath`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-executable-path) + * option with extreme caution. * > * > If Google Chrome (rather than Chromium) is preferred, a * [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or @@ -13854,8 +14610,9 @@ export interface BrowserType<Unused = {}> { /** * Returns the persistent browser context instance. * - * Launches browser that uses persistent storage located at `userDataDir` and returns the only context. Closing this - * context will automatically close the browser. + * Launches browser that uses persistent storage located at + * [`userDataDir`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context-option-user-data-dir) + * and returns the only context. Closing this context will automatically close the browser. * @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for * [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction) and * [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile). Note that Chromium's @@ -13924,6 +14681,7 @@ export interface BrowserType<Unused = {}> { * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. + * */ clientCertificates?: Array<{ /** @@ -13982,7 +14740,8 @@ export interface BrowserType<Unused = {}> { /** * **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the - * `headless` option will be set `false`. + * [`headless`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context-option-headless) + * option will be set `false`. * @deprecated Use [debugging tools](https://playwright.dev/docs/debug) instead. */ devtools?: boolean; @@ -14000,9 +14759,10 @@ export interface BrowserType<Unused = {}> { env?: { [key: string]: string|number|boolean; }; /** - * Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is - * resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium, - * Firefox or WebKit, use at your own risk. + * Path to a browser executable to run instead of the bundled one. If + * [`executablePath`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context-option-executable-path) + * is a relative path, then it is resolved relative to the current working directory. Note that Playwright only works + * with the bundled Chromium, Firefox or WebKit, use at your own risk. */ executablePath?: string; @@ -14066,7 +14826,8 @@ export interface BrowserType<Unused = {}> { * Whether to run browser in headless mode. More details for * [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and * [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the - * `devtools` option is `true`. + * [`devtools`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-devtools) option is + * `true`. */ headless?: boolean; @@ -14085,17 +14846,20 @@ export interface BrowserType<Unused = {}> { origin?: string; /** - * This option only applies to the requests sent from corresponding {@link APIRequestContext} and does not affect - * requests sent from the browser. `'always'` - `Authorization` header with basic authentication credentials will be - * sent with the each API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response - * with `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. + * This option only applies to the requests sent from corresponding + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) and does not affect requests sent from + * the browser. `'always'` - `Authorization` header with basic authentication credentials will be sent with the each + * API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response with + * `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. */ send?: "unauthorized"|"always"; }; /** - * If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is - * given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`. + * If `true`, Playwright does not pass its own configurations args and only uses the ones from + * [`args`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context-option-args). If + * an array is given, then filters out the given default arguments. Dangerous option; use with care. Defaults to + * `false`. */ ignoreDefaultArgs?: boolean|Array<string>; @@ -14202,8 +14966,9 @@ export interface BrowserType<Unused = {}> { mode?: "full"|"minimal"; /** - * A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was - * provided and the passed URL is a path, it gets merged via the + * A glob or regex pattern to filter requests that are stored in the HAR. When a + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the context + * options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none. */ urlFilter?: string|RegExp; @@ -14248,7 +15013,8 @@ export interface BrowserType<Unused = {}> { /** * Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the - * `viewport` is set. + * [`viewport`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context-option-viewport) + * is set. */ screen?: { /** @@ -14279,8 +15045,8 @@ export interface BrowserType<Unused = {}> { /** * If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on * selectors that imply single target DOM element will throw when more than one element matches the selector. This - * option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See {@link Locator} to - * learn more about the strict mode. + * option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See + * [Locator](https://playwright.dev/docs/api/class-locator) to learn more about the strict mode. */ strictSelectors?: boolean; @@ -14308,7 +15074,9 @@ export interface BrowserType<Unused = {}> { userAgent?: string; /** - * @deprecated Use `recordVideo` instead. + * @deprecated Use + * [`recordVideo`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context-option-record-video) + * instead. */ videoSize?: { /** @@ -14323,7 +15091,9 @@ export interface BrowserType<Unused = {}> { }; /** - * @deprecated Use `recordVideo` instead. + * @deprecated Use + * [`recordVideo`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context-option-record-video) + * instead. */ videosPath?: string; @@ -14333,6 +15103,7 @@ export interface BrowserType<Unused = {}> { * * **NOTE** The `null` value opts out from the default presets, makes viewport depend on the host window size defined * by the operating system. It makes the execution of the tests non-deterministic. + * */ viewport?: null|{ /** @@ -14395,7 +15166,8 @@ export interface BrowserType<Unused = {}> { /** * **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the - * `headless` option will be set `false`. + * [`headless`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server-option-headless) option + * will be set `false`. * @deprecated Use [debugging tools](https://playwright.dev/docs/debug) instead. */ devtools?: boolean; @@ -14413,9 +15185,10 @@ export interface BrowserType<Unused = {}> { env?: { [key: string]: string|number|boolean; }; /** - * Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is - * resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium, - * Firefox or WebKit, use at your own risk. + * Path to a browser executable to run instead of the bundled one. If + * [`executablePath`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server-option-executable-path) + * is a relative path, then it is resolved relative to the current working directory. Note that Playwright only works + * with the bundled Chromium, Firefox or WebKit, use at your own risk. */ executablePath?: string; @@ -14444,7 +15217,8 @@ export interface BrowserType<Unused = {}> { * Whether to run browser in headless mode. More details for * [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and * [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the - * `devtools` option is `true`. + * [`devtools`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-devtools) option is + * `true`. */ headless?: boolean; @@ -14456,7 +15230,8 @@ export interface BrowserType<Unused = {}> { host?: string; /** - * If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is + * If `true`, Playwright does not pass its own configurations args and only uses the ones from + * [`args`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server-option-args). If an array is * given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`. */ ignoreDefaultArgs?: boolean|Array<string>; @@ -14513,6 +15288,7 @@ export interface BrowserType<Unused = {}> { * * **NOTE** Any process or web page (including those running in Playwright) with knowledge of the `wsPath` can take * control of the OS user. For this reason, you should use an unguessable token when using this option. + * */ wsPath?: string; }): Promise<BrowserServer>; @@ -14567,6 +15343,170 @@ export interface CDPSession { detach(): Promise<void>; } +/** + * Whenever a [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) route is set up with + * [page.routeWebSocket(url, handler)](https://playwright.dev/docs/api/class-page#page-route-web-socket) or + * [browserContext.routeWebSocket(url, handler)](https://playwright.dev/docs/api/class-browsercontext#browser-context-route-web-socket), + * the `WebSocketRoute` object allows to handle the WebSocket, like an actual server would do. + * + * **Mocking** + * + * By default, the routed WebSocket will not connect to the server. This way, you can mock entire communcation over + * the WebSocket. Here is an example that responds to a `"request"` with a `"response"`. + * + * ```js + * await page.routeWebSocket('/ws', ws => { + * ws.onMessage(message => { + * if (message === 'request') + * ws.send('response'); + * }); + * }); + * ``` + * + * Since we do not call + * [webSocketRoute.connectToServer()](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-connect-to-server) + * inside the WebSocket route handler, Playwright assumes that WebSocket will be mocked, and opens the WebSocket + * inside the page automatically. + * + * **Intercepting** + * + * Alternatively, you may want to connect to the actual server, but intercept messages in-between and modify or block + * them. Calling + * [webSocketRoute.connectToServer()](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-connect-to-server) + * returns a server-side `WebSocketRoute` instance that you can send messages to, or handle incoming messages. + * + * Below is an example that modifies some messages sent by the page to the server. Messages sent from the server to + * the page are left intact, relying on the default forwarding. + * + * ```js + * await page.routeWebSocket('/ws', ws => { + * const server = ws.connectToServer(); + * ws.onMessage(message => { + * if (message === 'request') + * server.send('request2'); + * else + * server.send(message); + * }); + * }); + * ``` + * + * After connecting to the server, all **messages are forwarded** between the page and the server by default. + * + * However, if you call + * [webSocketRoute.onMessage(handler)](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-on-message) + * on the original route, messages from the page to the server **will not be forwarded** anymore, but should instead + * be handled by the + * [`handler`](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-on-message-option-handler). + * + * Similarly, calling + * [webSocketRoute.onMessage(handler)](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-on-message) + * on the server-side WebSocket will **stop forwarding messages** from the server to the page, and + * [`handler`](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-on-message-option-handler) should + * take care of them. + * + * The following example blocks some messages in both directions. Since it calls + * [webSocketRoute.onMessage(handler)](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-on-message) + * in both directions, there is no automatic forwarding at all. + * + * ```js + * await page.routeWebSocket('/ws', ws => { + * const server = ws.connectToServer(); + * ws.onMessage(message => { + * if (message !== 'blocked-from-the-page') + * server.send(message); + * }); + * server.onMessage(message => { + * if (message !== 'blocked-from-the-server') + * ws.send(message); + * }); + * }); + * ``` + * + */ +export interface WebSocketRoute { + /** + * This method allows to handle messages that are sent by the WebSocket, either from the page or from the server. + * + * When called on the original WebSocket route, this method handles messages sent from the page. You can handle this + * messages by responding to them with + * [webSocketRoute.send(message)](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-send), + * forwarding them to the server-side connection returned by + * [webSocketRoute.connectToServer()](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-connect-to-server) + * or do something else. + * + * Once this method is called, messages are not automatically forwarded to the server or to the page - you should do + * that manually by calling + * [webSocketRoute.send(message)](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-send). See + * examples at the top for more details. + * + * Calling this method again will override the handler with a new one. + * @param handler Function that will handle messages. + */ + onMessage(handler: (message: string | Buffer) => any): void; + /** + * Allows to handle [`WebSocket.close`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close). + * + * By default, closing one side of the connection, either in the page or on the server, will close the other side. + * However, when + * [webSocketRoute.onClose(handler)](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-on-close) + * handler is set up, the default forwarding of closure is disabled, and handler should take care of it. + * @param handler Function that will handle WebSocket closure. Received an optional + * [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional + * [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason). + */ + onClose(handler: (code: number | undefined, reason: string | undefined) => any): void; + /** + * Closes one side of the WebSocket connection. + * @param options + */ + close(options?: { + /** + * Optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code). + */ + code?: number; + + /** + * Optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason). + */ + reason?: string; + }): Promise<void>; + + /** + * By default, routed WebSocket does not connect to the server, so you can mock entire WebSocket communication. This + * method connects to the actual WebSocket server, and returns the server-side + * [WebSocketRoute](https://playwright.dev/docs/api/class-websocketroute) instance, giving the ability to send and + * receive messages from the server. + * + * Once connected to the server: + * - Messages received from the server will be **automatically forwarded** to the WebSocket in the page, unless + * [webSocketRoute.onMessage(handler)](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-on-message) + * is called on the server-side `WebSocketRoute`. + * - Messages sent by the [`WebSocket.send()`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send) call + * in the page will be **automatically forwarded** to the server, unless + * [webSocketRoute.onMessage(handler)](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-on-message) + * is called on the original `WebSocketRoute`. + * + * See examples at the top for more details. + */ + connectToServer(): WebSocketRoute; + + /** + * Sends a message to the WebSocket. When called on the original WebSocket, sends the message to the page. When called + * on the result of + * [webSocketRoute.connectToServer()](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-connect-to-server), + * sends the message to the server. See examples at the top for more details. + * @param message Message to send. + */ + send(message: string|Buffer): void; + + /** + * URL of the WebSocket created in the page. + */ + url(): string; + + [Symbol.asyncDispose](): Promise<void>; +} + type DeviceDescriptor = { viewport: ViewportSize; userAgent: string; @@ -14631,8 +15571,9 @@ export interface Accessibility { * the page. * * **NOTE** The Chromium accessibility tree contains nodes that go unused on most platforms and by most screen - * readers. Playwright will discard them as well for an easier to process tree, unless `interestingOnly` is set to - * `false`. + * readers. Playwright will discard them as well for an easier to process tree, unless + * [`interestingOnly`](https://playwright.dev/docs/api/class-accessibility#accessibility-snapshot-option-interesting-only) + * is set to `false`. * * **Usage** * @@ -14743,7 +15684,8 @@ type ElectronType = typeof import('electron'); */ export interface ElectronApplication { /** - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-option-expression). * * If the function passed to the * [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate) @@ -14758,11 +15700,13 @@ export interface ElectronApplication { * returns `undefined`. Playwright also supports transferring some additional values that are not serializable by * `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`. * @param pageFunction Function to be evaluated in the main Electron process. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-option-expression). */ evaluate<R, Arg>(pageFunction: PageFunctionOn<ElectronType, Arg, R>, arg: Arg): Promise<R>; /** - * Returns the return value of `pageFunction`. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-option-expression). * * If the function passed to the * [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate) @@ -14777,12 +15721,15 @@ export interface ElectronApplication { * returns `undefined`. Playwright also supports transferring some additional values that are not serializable by * `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`. * @param pageFunction Function to be evaluated in the main Electron process. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-option-expression). */ evaluate<R>(pageFunction: PageFunctionOn<ElectronType, void, R>, arg?: any): Promise<R>; /** - * Returns the return value of `pageFunction` as a {@link JSHandle}. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle-option-expression) + * as a [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * The only difference between * [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate) @@ -14790,7 +15737,7 @@ export interface ElectronApplication { * [electronApplication.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle) * is that * [electronApplication.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle) - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function passed to the * [electronApplication.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle) @@ -14798,11 +15745,14 @@ export interface ElectronApplication { * [electronApplication.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle) * would wait for the promise to resolve and return its value. * @param pageFunction Function to be evaluated in the main Electron process. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle-option-expression). */ evaluateHandle<R, Arg>(pageFunction: PageFunctionOn<ElectronType, Arg, R>, arg: Arg): Promise<SmartHandle<R>>; /** - * Returns the return value of `pageFunction` as a {@link JSHandle}. + * Returns the return value of + * [`pageFunction`](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle-option-expression) + * as a [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * The only difference between * [electronApplication.evaluate(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate) @@ -14810,7 +15760,7 @@ export interface ElectronApplication { * [electronApplication.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle) * is that * [electronApplication.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle) - * returns {@link JSHandle}. + * returns [JSHandle](https://playwright.dev/docs/api/class-jshandle). * * If the function passed to the * [electronApplication.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle) @@ -14818,7 +15768,8 @@ export interface ElectronApplication { * [electronApplication.evaluateHandle(pageFunction[, arg])](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle) * would wait for the promise to resolve and return its value. * @param pageFunction Function to be evaluated in the main Electron process. - * @param arg Optional argument to pass to `pageFunction`. + * @param arg Optional argument to pass to + * [`pageFunction`](https://playwright.dev/docs/api/class-electronapplication#electron-application-evaluate-handle-option-expression). */ evaluateHandle<R>(pageFunction: PageFunctionOn<ElectronType, void, R>, arg?: any): Promise<SmartHandle<R>>; /** @@ -14830,7 +15781,8 @@ export interface ElectronApplication { * Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or * `console.dir`. * - * The arguments passed into `console.log` are available on the {@link ConsoleMessage} event handler argument. + * The arguments passed into `console.log` are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -14848,8 +15800,8 @@ export interface ElectronApplication { on(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** - * This event is issued for every window that is created **and loaded** in Electron. It contains a {@link Page} that - * can be used for Playwright automation. + * This event is issued for every window that is created **and loaded** in Electron. It contains a + * [Page](https://playwright.dev/docs/api/class-page) that can be used for Playwright automation. */ on(event: 'window', listener: (page: Page) => any): this; @@ -14877,7 +15829,8 @@ export interface ElectronApplication { * Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or * `console.dir`. * - * The arguments passed into `console.log` are available on the {@link ConsoleMessage} event handler argument. + * The arguments passed into `console.log` are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -14895,8 +15848,8 @@ export interface ElectronApplication { addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** - * This event is issued for every window that is created **and loaded** in Electron. It contains a {@link Page} that - * can be used for Playwright automation. + * This event is issued for every window that is created **and loaded** in Electron. It contains a + * [Page](https://playwright.dev/docs/api/class-page) that can be used for Playwright automation. */ addListener(event: 'window', listener: (page: Page) => any): this; @@ -14939,7 +15892,8 @@ export interface ElectronApplication { * Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or * `console.dir`. * - * The arguments passed into `console.log` are available on the {@link ConsoleMessage} event handler argument. + * The arguments passed into `console.log` are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -14957,8 +15911,8 @@ export interface ElectronApplication { prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => any): this; /** - * This event is issued for every window that is created **and loaded** in Electron. It contains a {@link Page} that - * can be used for Playwright automation. + * This event is issued for every window that is created **and loaded** in Electron. It contains a + * [Page](https://playwright.dev/docs/api/class-page) that can be used for Playwright automation. */ prependListener(event: 'window', listener: (page: Page) => any): this; @@ -15016,7 +15970,8 @@ export interface ElectronApplication { * Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or * `console.dir`. * - * The arguments passed into `console.log` are available on the {@link ConsoleMessage} event handler argument. + * The arguments passed into `console.log` are available on the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) event handler argument. * * **Usage** * @@ -15034,8 +15989,8 @@ export interface ElectronApplication { waitForEvent(event: 'console', optionsOrPredicate?: { predicate?: (consoleMessage: ConsoleMessage) => boolean | Promise<boolean>, timeout?: number } | ((consoleMessage: ConsoleMessage) => boolean | Promise<boolean>)): Promise<ConsoleMessage>; /** - * This event is issued for every window that is created **and loaded** in Electron. It contains a {@link Page} that - * can be used for Playwright automation. + * This event is issued for every window that is created **and loaded** in Electron. It contains a + * [Page](https://playwright.dev/docs/api/class-page) that can be used for Playwright automation. */ waitForEvent(event: 'window', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise<boolean>, timeout?: number } | ((page: Page) => boolean | Promise<boolean>)): Promise<Page>; @@ -15354,20 +16309,23 @@ export interface Android { * * **NOTE** Any process or web page (including those running in Playwright) with knowledge of the `wsPath` can take * control of the OS user. For this reason, you should use an unguessable token when using this option. + * */ wsPath?: string; }): Promise<BrowserServer>; /** - * This setting will change the default maximum time for all the methods accepting `timeout` option. + * This setting will change the default maximum time for all the methods accepting + * [`timeout`](https://playwright.dev/docs/api/class-android#android-set-default-timeout-option-timeout) option. * @param timeout Maximum time in milliseconds */ setDefaultTimeout(timeout: number): void; } /** - * {@link AndroidDevice} represents a connected device, either real hardware or emulated. Devices can be obtained - * using [android.devices([options])](https://playwright.dev/docs/api/class-android#android-devices). + * [AndroidDevice](https://playwright.dev/docs/api/class-androiddevice) represents a connected device, either real + * hardware or emulated. Devices can be obtained using + * [android.devices([options])](https://playwright.dev/docs/api/class-android#android-devices). */ export interface AndroidDevice { /** @@ -15436,7 +16394,9 @@ export interface AndroidDevice { close(): Promise<void>; /** - * Drags the widget defined by `selector` towards `dest` point. + * Drags the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-drag-option-selector) towards + * [`dest`](https://playwright.dev/docs/api/class-androiddevice#android-device-drag-option-dest) point. * @param selector Selector to drag. * @param dest Point to drag to. * @param options @@ -15461,7 +16421,9 @@ export interface AndroidDevice { }): Promise<void>; /** - * Fills the specific `selector` input box with `text`. + * Fills the specific + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-fill-option-selector) input box + * with [`text`](https://playwright.dev/docs/api/class-androiddevice#android-device-fill-option-text). * @param selector Selector to fill. * @param text Text to be filled in the input box. * @param options @@ -15477,7 +16439,9 @@ export interface AndroidDevice { }): Promise<void>; /** - * Flings the widget defined by `selector` in the specified `direction`. + * Flings the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-fling-option-selector) in the + * specified [`direction`](https://playwright.dev/docs/api/class-androiddevice#android-device-fling-option-direction). * @param selector Selector to fling. * @param direction Fling direction. * @param options @@ -15498,7 +16462,8 @@ export interface AndroidDevice { }): Promise<void>; /** - * Returns information about a widget defined by `selector`. + * Returns information about a widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-info-option-selector). * @param selector Selector to return information about. */ info(selector: AndroidSelector): Promise<AndroidElementInfo>; @@ -15624,10 +16589,11 @@ export interface AndroidDevice { origin?: string; /** - * This option only applies to the requests sent from corresponding {@link APIRequestContext} and does not affect - * requests sent from the browser. `'always'` - `Authorization` header with basic authentication credentials will be - * sent with the each API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response - * with `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. + * This option only applies to the requests sent from corresponding + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) and does not affect requests sent from + * the browser. `'always'` - `Authorization` header with basic authentication credentials will be sent with the each + * API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response with + * `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. */ send?: "unauthorized"|"always"; }; @@ -15735,8 +16701,9 @@ export interface AndroidDevice { mode?: "full"|"minimal"; /** - * A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was - * provided and the passed URL is a path, it gets merged via the + * A glob or regex pattern to filter requests that are stored in the HAR. When a + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the context + * options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none. */ urlFilter?: string|RegExp; @@ -15781,7 +16748,8 @@ export interface AndroidDevice { /** * Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the - * `viewport` is set. + * [`viewport`](https://playwright.dev/docs/api/class-androiddevice#android-device-launch-browser-option-viewport) is + * set. */ screen?: { /** @@ -15806,8 +16774,8 @@ export interface AndroidDevice { /** * If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on * selectors that imply single target DOM element will throw when more than one element matches the selector. This - * option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See {@link Locator} to - * learn more about the strict mode. + * option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See + * [Locator](https://playwright.dev/docs/api/class-locator) to learn more about the strict mode. */ strictSelectors?: boolean; @@ -15824,7 +16792,9 @@ export interface AndroidDevice { userAgent?: string; /** - * @deprecated Use `recordVideo` instead. + * @deprecated Use + * [`recordVideo`](https://playwright.dev/docs/api/class-androiddevice#android-device-launch-browser-option-record-video) + * instead. */ videoSize?: { /** @@ -15839,7 +16809,9 @@ export interface AndroidDevice { }; /** - * @deprecated Use `recordVideo` instead. + * @deprecated Use + * [`recordVideo`](https://playwright.dev/docs/api/class-androiddevice#android-device-launch-browser-option-record-video) + * instead. */ videosPath?: string; @@ -15849,6 +16821,7 @@ export interface AndroidDevice { * * **NOTE** The `null` value opts out from the default presets, makes viewport depend on the host window size defined * by the operating system. It makes the execution of the tests non-deterministic. + * */ viewport?: null|{ /** @@ -15864,7 +16837,8 @@ export interface AndroidDevice { }): Promise<BrowserContext>; /** - * Performs a long tap on the widget defined by `selector`. + * Performs a long tap on the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-long-tap-option-selector). * @param selector Selector to tap on. * @param options */ @@ -15885,12 +16859,14 @@ export interface AndroidDevice { /** * Launches a process in the shell on the device and returns a socket to communicate with the launched process. - * @param command + * @param command Shell command to execute. */ open(command: string): Promise<AndroidSocket>; /** - * Pinches the widget defined by `selector` in the closing direction. + * Pinches the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-pinch-close-option-selector) in the + * closing direction. * @param selector Selector to pinch close. * @param percent The size of the pinch as a percentage of the widget's size. * @param options @@ -15911,7 +16887,9 @@ export interface AndroidDevice { }): Promise<void>; /** - * Pinches the widget defined by `selector` in the open direction. + * Pinches the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-pinch-open-option-selector) in the + * open direction. * @param selector Selector to pinch open. * @param percent The size of the pinch as a percentage of the widget's size. * @param options @@ -15932,7 +16910,9 @@ export interface AndroidDevice { }): Promise<void>; /** - * Presses the specific `key` in the widget defined by `selector`. + * Presses the specific [`key`](https://playwright.dev/docs/api/class-androiddevice#android-device-press-option-key) + * in the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-press-option-selector). * @param selector Selector to press the key in. * @param key The key to press. * @param options @@ -15966,14 +16946,19 @@ export interface AndroidDevice { */ screenshot(options?: { /** - * The file path to save the image to. If `path` is a relative path, then it is resolved relative to the current - * working directory. If no path is provided, the image won't be saved to the disk. + * The file path to save the image to. If + * [`path`](https://playwright.dev/docs/api/class-androiddevice#android-device-screenshot-option-path) is a relative + * path, then it is resolved relative to the current working directory. If no path is provided, the image won't be + * saved to the disk. */ path?: string; }): Promise<Buffer>; /** - * Scrolls the widget defined by `selector` in the specified `direction`. + * Scrolls the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-scroll-option-selector) in the + * specified + * [`direction`](https://playwright.dev/docs/api/class-androiddevice#android-device-scroll-option-direction). * @param selector Selector to scroll. * @param direction Scroll direction. * @param percent Distance to scroll as a percentage of the widget's size. @@ -16000,7 +16985,9 @@ export interface AndroidDevice { serial(): string; /** - * This setting will change the default maximum time for all the methods accepting `timeout` option. + * This setting will change the default maximum time for all the methods accepting + * [`timeout`](https://playwright.dev/docs/api/class-androiddevice#android-device-set-default-timeout-option-timeout) + * option. * @param timeout Maximum time in milliseconds */ setDefaultTimeout(timeout: number): void; @@ -16012,7 +16999,9 @@ export interface AndroidDevice { shell(command: string): Promise<Buffer>; /** - * Swipes the widget defined by `selector` in the specified `direction`. + * Swipes the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-swipe-option-selector) in the + * specified [`direction`](https://playwright.dev/docs/api/class-androiddevice#android-device-swipe-option-direction). * @param selector Selector to swipe. * @param direction Swipe direction. * @param percent Distance to swipe as a percentage of the widget's size. @@ -16034,7 +17023,8 @@ export interface AndroidDevice { }): Promise<void>; /** - * Taps on the widget defined by `selector`. + * Taps on the widget defined by + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-tap-option-selector). * @param selector Selector to tap on. * @param options */ @@ -16054,7 +17044,10 @@ export interface AndroidDevice { }): Promise<void>; /** - * Waits for the specific `selector` to either appear or disappear, depending on the `state`. + * Waits for the specific + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-wait-option-selector) to either + * appear or disappear, depending on the + * [`state`](https://playwright.dev/docs/api/class-androiddevice#android-device-wait-option-state). * @param selector Selector to wait for. * @param options */ @@ -16087,8 +17080,12 @@ export interface AndroidDevice { /** - * This method waits until {@link AndroidWebView} matching the `selector` is opened and returns it. If there is - * already an open {@link AndroidWebView} matching the `selector`, returns immediately. + * This method waits until [AndroidWebView](https://playwright.dev/docs/api/class-androidwebview) matching the + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-web-view-option-selector) is opened + * and returns it. If there is already an open [AndroidWebView](https://playwright.dev/docs/api/class-androidwebview) + * matching the + * [`selector`](https://playwright.dev/docs/api/class-androiddevice#android-device-web-view-option-selector), returns + * immediately. * @param selector * @param options */ @@ -16124,7 +17121,8 @@ export interface AndroidDevice { export interface AndroidInput { /** - * Performs a drag between `from` and `to` points. + * Performs a drag between [`from`](https://playwright.dev/docs/api/class-androidinput#android-input-drag-option-from) + * and [`to`](https://playwright.dev/docs/api/class-androidinput#android-input-drag-option-to) points. * @param from The start point of the drag. * @param to The end point of the drag. * @param steps The number of steps in the drag. Each step takes 5 milliseconds to complete. @@ -16140,15 +17138,17 @@ export interface AndroidInput { }, steps: number): Promise<void>; /** - * Presses the `key`. + * Presses the [`key`](https://playwright.dev/docs/api/class-androidinput#android-input-press-option-key). * @param key Key to press. */ press(key: AndroidKey): Promise<void>; /** - * Swipes following the path defined by `segments`. + * Swipes following the path defined by + * [`segments`](https://playwright.dev/docs/api/class-androidinput#android-input-swipe-option-segments). * @param from The point to start swiping from. - * @param segments Points following the `from` point in the swipe gesture. + * @param segments Points following the [`from`](https://playwright.dev/docs/api/class-androidinput#android-input-swipe-option-from) + * point in the swipe gesture. * @param steps The number of steps for each segment. Each step takes 5 milliseconds to complete, so 100 steps means half a second * per each segment. */ @@ -16163,7 +17163,7 @@ export interface AndroidInput { }>, steps: number): Promise<void>; /** - * Taps at the specified `point`. + * Taps at the specified [`point`](https://playwright.dev/docs/api/class-androidinput#android-input-tap-option-point). * @param point The point to tap at. */ tap(point: { @@ -16173,14 +17173,16 @@ export interface AndroidInput { }): Promise<void>; /** - * Types `text` into currently focused widget. + * Types [`text`](https://playwright.dev/docs/api/class-androidinput#android-input-type-option-text) into currently + * focused widget. * @param text Text to type. */ type(text: string): Promise<void>; } /** - * {@link AndroidSocket} is a way to communicate with a process launched on the {@link AndroidDevice}. Use + * [AndroidSocket](https://playwright.dev/docs/api/class-androidsocket) is a way to communicate with a process + * launched on the [AndroidDevice](https://playwright.dev/docs/api/class-androiddevice). Use * [androidDevice.open(command)](https://playwright.dev/docs/api/class-androiddevice#android-device-open) to open a * socket. */ @@ -16251,7 +17253,8 @@ export interface AndroidSocket { close(): Promise<void>; /** - * Writes some `data` to the socket. + * Writes some [`data`](https://playwright.dev/docs/api/class-androidsocket#android-socket-write-option-data) to the + * socket. * @param data Data to write. */ write(data: Buffer): Promise<void>; @@ -16260,7 +17263,8 @@ export interface AndroidSocket { } /** - * {@link AndroidWebView} represents a WebView open on the {@link AndroidDevice}. WebView is usually obtained using + * [AndroidWebView](https://playwright.dev/docs/api/class-androidwebview) represents a WebView open on the + * [AndroidDevice](https://playwright.dev/docs/api/class-androiddevice). WebView is usually obtained using * [androidDevice.webView(selector[, options])](https://playwright.dev/docs/api/class-androiddevice#android-device-web-view). */ export interface AndroidWebView { @@ -16295,7 +17299,8 @@ export interface AndroidWebView { prependListener(event: 'close', listener: () => any): this; /** - * Connects to the WebView and returns a regular Playwright {@link Page} to interact with. + * Connects to the WebView and returns a regular Playwright [Page](https://playwright.dev/docs/api/class-page) to + * interact with. */ page(): Promise<Page>; @@ -16311,14 +17316,15 @@ export interface AndroidWebView { } /** - * Exposes API that can be used for the Web API testing. This class is used for creating {@link APIRequestContext} - * instance which in turn can be used for sending web requests. An instance of this class can be obtained via + * Exposes API that can be used for the Web API testing. This class is used for creating + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) instance which in turn can be used for + * sending web requests. An instance of this class can be obtained via * [playwright.request](https://playwright.dev/docs/api/class-playwright#playwright-request). For more information see - * {@link APIRequestContext}. + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext). */ export interface APIRequest { /** - * Creates new instances of {@link APIRequestContext}. + * Creates new instances of [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext). * @param options */ newContext(options?: { @@ -16348,6 +17354,7 @@ export interface APIRequest { * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. + * */ clientCertificates?: Array<{ /** @@ -16411,10 +17418,11 @@ export interface APIRequest { origin?: string; /** - * This option only applies to the requests sent from corresponding {@link APIRequestContext} and does not affect - * requests sent from the browser. `'always'` - `Authorization` header with basic authentication credentials will be - * sent with the each API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response - * with `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. + * This option only applies to the requests sent from corresponding + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) and does not affect requests sent from + * the browser. `'always'` - `Authorization` header with basic authentication credentials will be sent with the each + * API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response with + * `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. */ send?: "unauthorized"|"always"; }; @@ -16512,8 +17520,9 @@ export interface APIRequest { * This API is used for the Web API testing. You can use it to trigger API endpoints, configure micro-services, * prepare environment or the service to your e2e test. * - * Each Playwright browser context has associated with it {@link APIRequestContext} instance which shares cookie - * storage with the browser context and can be accessed via + * Each Playwright browser context has associated with it + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) instance which shares cookie storage + * with the browser context and can be accessed via * [browserContext.request](https://playwright.dev/docs/api/class-browsercontext#browser-context-request) or * [page.request](https://playwright.dev/docs/api/class-page#page-request). It is also possible to create a new * APIRequestContext instance manually by calling @@ -16521,16 +17530,17 @@ export interface APIRequest { * * **Cookie management** * - * {@link APIRequestContext} returned by + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) returned by * [browserContext.request](https://playwright.dev/docs/api/class-browsercontext#browser-context-request) and * [page.request](https://playwright.dev/docs/api/class-page#page-request) shares cookie storage with the - * corresponding {@link BrowserContext}. Each API request will have `Cookie` header populated with the values from the - * browser context. If the API response contains `Set-Cookie` header it will automatically update {@link - * BrowserContext} cookies and requests made from the page will pick them up. This means that if you log in using this - * API, your e2e test will be logged in and vice versa. + * corresponding [BrowserContext](https://playwright.dev/docs/api/class-browsercontext). Each API request will have + * `Cookie` header populated with the values from the browser context. If the API response contains `Set-Cookie` + * header it will automatically update [BrowserContext](https://playwright.dev/docs/api/class-browsercontext) cookies + * and requests made from the page will pick them up. This means that if you log in using this API, your e2e test will + * be logged in and vice versa. * - * If you want API requests to not interfere with the browser cookies you should create a new {@link - * APIRequestContext} by calling + * If you want API requests to not interfere with the browser cookies you should create a new + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) by calling * [apiRequest.newContext([options])](https://playwright.dev/docs/api/class-apirequest#api-request-new-context). Such * `APIRequestContext` object will have its own isolated cookie storage. */ @@ -16626,7 +17636,8 @@ export interface APIRequestContext { * [apiRequestContext.get(url[, options])](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-get) * and similar methods are stored in the memory, so that you can later call * [apiResponse.body()](https://playwright.dev/docs/api/class-apiresponse#api-response-body).This method discards all - * its resources, calling any method on disposed {@link APIRequestContext} will throw an exception. + * its resources, calling any method on disposed + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) will throw an exception. * @param options */ dispose(options?: { @@ -16655,7 +17666,7 @@ export interface APIRequestContext { * ``` * * The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` - * encoding. Use [FormData] to construct request body and pass it to the request as `multipart` parameter: + * encoding, by specifiying the `multipart` parameter: * * ```js * const form = new FormData(); @@ -17262,8 +18273,10 @@ export interface APIRequestContext { */ storageState(options?: { /** - * The file path to save the storage state to. If `path` is a relative path, then it is resolved relative to current - * working directory. If no path is provided, storage state is still returned, but won't be saved to the disk. + * The file path to save the storage state to. If + * [`path`](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-storage-state-option-path) is + * a relative path, then it is resolved relative to current working directory. If no path is provided, storage state + * is still returned, but won't be saved to the disk. */ path?: string; }): Promise<{ @@ -17303,7 +18316,7 @@ export interface APIRequestContext { } /** - * {@link APIResponse} class represents responses returned by + * [APIResponse](https://playwright.dev/docs/api/class-apiresponse) class represents responses returned by * [apiRequestContext.get(url[, options])](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-get) * and similar methods. */ @@ -17439,8 +18452,8 @@ export interface BrowserServer { * Accurately simulating time-dependent behavior is essential for verifying the correctness of applications. Learn * more about [clock emulation](https://playwright.dev/docs/clock). * - * Note that clock is installed for the entire {@link BrowserContext}, so the time in all the pages and iframes is - * controlled by the same clock. + * Note that clock is installed for the entire [BrowserContext](https://playwright.dev/docs/api/class-browsercontext), + * so the time in all the pages and iframes is controlled by the same clock. */ export interface Clock { /** @@ -17558,7 +18571,7 @@ export interface Clock { } /** - * {@link ConsoleMessage} objects are dispatched by page via the + * [ConsoleMessage](https://playwright.dev/docs/api/class-consolemessage) objects are dispatched by page via the * [page.on('console')](https://playwright.dev/docs/api/class-page#page-event-console) event. For each console message * logged in the page there will be corresponding event in the Playwright context. * @@ -17671,8 +18684,10 @@ export interface Coverage { * Returns coverage is started * * **NOTE** Anonymous scripts are ones that don't have an associated url. These are scripts that are dynamically - * created on the page using `eval` or `new Function`. If `reportAnonymousScripts` is set to `true`, anonymous scripts - * will have `__playwright_evaluation_script__` as their URL. + * created on the page using `eval` or `new Function`. If + * [`reportAnonymousScripts`](https://playwright.dev/docs/api/class-coverage#coverage-start-js-coverage-option-report-anonymous-scripts) + * is set to `true`, anonymous scripts will have `__playwright_evaluation_script__` as their URL. + * * @param options */ startJSCoverage(options?: { @@ -17691,6 +18706,7 @@ export interface Coverage { * Returns the array of coverage reports for all stylesheets * * **NOTE** CSS Coverage doesn't include dynamically injected style tags without sourceURLs. + * */ stopCSSCoverage(): Promise<Array<{ /** @@ -17724,6 +18740,7 @@ export interface Coverage { * * **NOTE** JavaScript Coverage doesn't include anonymous scripts by default. However, scripts with sourceURLs are * reported. + * */ stopJSCoverage(): Promise<Array<{ /** @@ -17761,7 +18778,7 @@ export interface Coverage { } /** - * {@link Dialog} objects are dispatched by page via the + * [Dialog](https://playwright.dev/docs/api/class-dialog) objects are dispatched by page via the * [page.on('dialog')](https://playwright.dev/docs/api/class-page#page-event-dialog) event. * * An example of using `Dialog` class: @@ -17788,6 +18805,7 @@ export interface Coverage { * [dialog.dismiss()](https://playwright.dev/docs/api/class-dialog#dialog-dismiss) the dialog - otherwise the page * will [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the * dialog, and actions like click will never finish. + * */ export interface Dialog { /** @@ -17823,7 +18841,7 @@ export interface Dialog { } /** - * {@link Download} objects are dispatched by page via the + * [Download](https://playwright.dev/docs/api/class-download) objects are dispatched by page via the * [page.on('download')](https://playwright.dev/docs/api/class-page#page-event-download) event. * * All the downloaded files belonging to the browser context are deleted when the browser context is closed. @@ -17959,7 +18977,8 @@ export interface Download { */ export interface Electron { /** - * Launches electron application specified with the `executablePath`. + * Launches electron application specified with the + * [`executablePath`](https://playwright.dev/docs/api/class-electron#electron-launch-option-executable-path). * @param options */ launch(options?: { @@ -18038,10 +19057,11 @@ export interface Electron { origin?: string; /** - * This option only applies to the requests sent from corresponding {@link APIRequestContext} and does not affect - * requests sent from the browser. `'always'` - `Authorization` header with basic authentication credentials will be - * sent with the each API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response - * with `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. + * This option only applies to the requests sent from corresponding + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) and does not affect requests sent from + * the browser. `'always'` - `Authorization` header with basic authentication credentials will be sent with the each + * API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response with + * `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. */ send?: "unauthorized"|"always"; }; @@ -18098,8 +19118,9 @@ export interface Electron { mode?: "full"|"minimal"; /** - * A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was - * provided and the passed URL is a path, it gets merged via the + * A glob or regex pattern to filter requests that are stored in the HAR. When a + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the context + * options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none. */ urlFilter?: string|RegExp; @@ -18156,7 +19177,7 @@ export interface Electron { } /** - * {@link FileChooser} objects are dispatched by the page in the + * [FileChooser](https://playwright.dev/docs/api/class-filechooser) objects are dispatched by the page in the * [page.on('filechooser')](https://playwright.dev/docs/api/class-page#page-event-file-chooser) event. * * ```js @@ -18240,11 +19261,12 @@ export interface FileChooser { /** * FrameLocator represents a view to the `iframe` on the page. It captures the logic sufficient to retrieve the * `iframe` and locate elements in that iframe. FrameLocator can be created with either + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame), * [page.frameLocator(selector)](https://playwright.dev/docs/api/class-page#page-frame-locator) or * [locator.frameLocator(selector)](https://playwright.dev/docs/api/class-locator#locator-frame-locator) method. * * ```js - * const locator = page.frameLocator('#my-frame').getByText('Submit'); + * const locator = page.locator('#my-frame').contentFrame().getByText('Submit'); * await locator.click(); * ``` * @@ -18255,25 +19277,29 @@ export interface FileChooser { * * ```js * // Throws if there are several frames in DOM: - * await page.frameLocator('.result-frame').getByRole('button').click(); + * await page.locator('.result-frame').contentFrame().getByRole('button').click(); * * // Works because we explicitly tell locator to pick the first frame: - * await page.frameLocator('.result-frame').first().getByRole('button').click(); + * await page.locator('.result-frame').contentFrame().first().getByRole('button').click(); * ``` * * **Converting Locator to FrameLocator** * - * If you have a {@link Locator} object pointing to an `iframe` it can be converted to {@link FrameLocator} using + * If you have a [Locator](https://playwright.dev/docs/api/class-locator) object pointing to an `iframe` it can be + * converted to [FrameLocator](https://playwright.dev/docs/api/class-framelocator) using * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame). * * **Converting FrameLocator to Locator** * - * If you have a {@link FrameLocator} object it can be converted to {@link Locator} pointing to the same `iframe` - * using [frameLocator.owner()](https://playwright.dev/docs/api/class-framelocator#frame-locator-owner). + * If you have a [FrameLocator](https://playwright.dev/docs/api/class-framelocator) object it can be converted to + * [Locator](https://playwright.dev/docs/api/class-locator) pointing to the same `iframe` using + * [frameLocator.owner()](https://playwright.dev/docs/api/class-framelocator#frame-locator-owner). */ export interface FrameLocator { /** * Returns locator to the first matching frame. + * @deprecated Use [locator.first()](https://playwright.dev/docs/api/class-locator#locator-first) followed by + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame) instead. */ first(): FrameLocator; @@ -18423,12 +19449,15 @@ export interface FrameLocator { * * **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about * [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled). + * */ disabled?: boolean; /** - * Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a - * regular expression. Note that exact match still trims whitespace. + * Whether [`name`](https://playwright.dev/docs/api/class-framelocator#frame-locator-get-by-role-option-name) is + * matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when + * [`name`](https://playwright.dev/docs/api/class-framelocator#frame-locator-get-by-role-option-name) is a regular + * expression. Note that exact match still trims whitespace. */ exact?: boolean; @@ -18457,7 +19486,9 @@ export interface FrameLocator { /** * Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is - * case-insensitive and searches for a substring, use `exact` to control this behavior. + * case-insensitive and searches for a substring, use + * [`exact`](https://playwright.dev/docs/api/class-framelocator#frame-locator-get-by-role-option-exact) to control + * this behavior. * * Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). */ @@ -18598,6 +19629,8 @@ export interface FrameLocator { /** * Returns locator to the last matching frame. + * @deprecated Use [locator.last()](https://playwright.dev/docs/api/class-locator#locator-last) followed by + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame) instead. */ last(): FrameLocator; @@ -18620,8 +19653,8 @@ export interface FrameLocator { * `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article * div` will fail, because the inner locator must be relative and should not use any elements outside the `content`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ has?: Locator; @@ -18629,8 +19662,8 @@ export interface FrameLocator { * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the * outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`. * - * Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@link - * FrameLocator}s. + * Note that outer and inner locators must belong to the same frame. Inner locator must not contain + * [FrameLocator](https://playwright.dev/docs/api/class-framelocator)s. */ hasNot?: Locator; @@ -18650,15 +19683,18 @@ export interface FrameLocator { /** * Returns locator to the n-th matching frame. It's zero based, `nth(0)` selects the first frame. + * @deprecated Use [locator.nth(index)](https://playwright.dev/docs/api/class-locator#locator-nth) followed by + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame) instead. * @param index */ nth(index: number): FrameLocator; /** - * Returns a {@link Locator} object pointing to the same `iframe` as this frame locator. + * Returns a [Locator](https://playwright.dev/docs/api/class-locator) object pointing to the same `iframe` as this + * frame locator. * - * Useful when you have a {@link FrameLocator} object obtained somewhere, and later on would like to interact with the - * `iframe` element. + * Useful when you have a [FrameLocator](https://playwright.dev/docs/api/class-framelocator) object obtained + * somewhere, and later on would like to interact with the `iframe` element. * * For a reverse operation, use * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame). @@ -18666,7 +19702,7 @@ export interface FrameLocator { * **Usage** * * ```js - * const frameLocator = page.frameLocator('iframe[name="embedded"]'); + * const frameLocator = page.locator('iframe[name="embedded"]').contentFrame(); * // ... * const locator = frameLocator.owner(); * await expect(locator).toBeVisible(); @@ -18723,9 +19759,10 @@ export interface Keyboard { /** * Dispatches a `keydown` event. * - * `key` can specify the intended + * [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-down-option-key) can specify the intended * [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character - * to generate the text for. A superset of the `key` values can be found + * to generate the text for. A superset of the + * [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-down-option-key) values can be found * [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are: * * `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, @@ -18735,14 +19772,15 @@ export interface Keyboard { * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * - * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. + * Holding down `Shift` will type the text that corresponds to the + * [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-down-option-key) in the upper case. * - * If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective - * texts. + * If [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-down-option-key) is a single character, it is + * case-sensitive, so the values `a` and `A` will generate different respective texts. * - * If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key presses will be sent with that - * modifier active. To release the modifier key, use - * [keyboard.up(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-up). + * If [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-down-option-key) is a modifier key, `Shift`, + * `Meta`, `Control`, or `Alt`, subsequent key presses will be sent with that modifier active. To release the modifier + * key, use [keyboard.up(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-up). * * After the key is pressed once, subsequent calls to * [keyboard.down(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-down) will have @@ -18750,6 +19788,7 @@ export interface Keyboard { * use [keyboard.up(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-up). * * **NOTE** Modifier keys DO influence `keyboard.down`. Holding down `Shift` will type the text in upper case. + * * @param key Name of the key to press or a character to generate, such as `ArrowLeft` or `a`. */ down(key: string): Promise<void>; @@ -18765,6 +19804,7 @@ export interface Keyboard { * * **NOTE** Modifier keys DO NOT effect `keyboard.insertText`. Holding down `Shift` will not type the text in upper * case. + * * @param text Sets input to the specified text value. */ insertText(text: string): Promise<void>; @@ -18773,9 +19813,10 @@ export interface Keyboard { * **NOTE** In most cases, you should use * [locator.press(key[, options])](https://playwright.dev/docs/api/class-locator#locator-press) instead. * - * `key` can specify the intended + * [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-press-option-key) can specify the intended * [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character - * to generate the text for. A superset of the `key` values can be found + * to generate the text for. A superset of the + * [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-press-option-key) values can be found * [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are: * * `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, @@ -18785,10 +19826,11 @@ export interface Keyboard { * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * - * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. + * Holding down `Shift` will type the text that corresponds to the + * [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-press-option-key) in the upper case. * - * If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective - * texts. + * If [`key`](https://playwright.dev/docs/api/class-keyboard#keyboard-press-option-key) is a single character, it is + * case-sensitive, so the values `a` and `A` will generate different respective texts. * * Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When * specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed. @@ -18840,6 +19882,7 @@ export interface Keyboard { * **NOTE** Modifier keys DO NOT effect `keyboard.type`. Holding down `Shift` will not type the text in upper case. * * **NOTE** For characters that are not on a US keyboard, only an `input` event will be sent. + * * @param text A text to type into a focused element. * @param options */ @@ -19015,6 +20058,7 @@ export interface Mouse { * * **NOTE** Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling * to finish before returning. + * * @param deltaX Pixels to scroll horizontally. * @param deltaY Pixels to scroll vertically. */ @@ -19022,12 +20066,14 @@ export interface Mouse { } /** - * This object can be used to launch or connect to Chromium, returning instances of {@link Browser}. + * This object can be used to launch or connect to Chromium, returning instances of + * [Browser](https://playwright.dev/docs/api/class-browser). */ export const chromium: BrowserType; /** - * This object can be used to launch or connect to Firefox, returning instances of {@link Browser}. + * This object can be used to launch or connect to Firefox, returning instances of + * [Browser](https://playwright.dev/docs/api/class-browser). */ export const firefox: BrowserType; @@ -19043,13 +20089,14 @@ export const request: APIRequest; export const selectors: Selectors; /** - * This object can be used to launch or connect to WebKit, returning instances of {@link Browser}. + * This object can be used to launch or connect to WebKit, returning instances of + * [Browser](https://playwright.dev/docs/api/class-browser). */ export const webkit: BrowserType; /** - * Whenever the page sends a request for a network resource the following sequence of events are emitted by {@link - * Page}: + * Whenever the page sends a request for a network resource the following sequence of events are emitted by + * [Page](https://playwright.dev/docs/api/class-page): * - [page.on('request')](https://playwright.dev/docs/api/class-page#page-event-request) emitted when the request is * issued by the page. * - [page.on('response')](https://playwright.dev/docs/api/class-page#page-event-response) emitted when/if the @@ -19095,7 +20142,7 @@ export interface Request { }; /** - * Returns the {@link Frame} that initiated this request. + * Returns the [Frame](https://playwright.dev/docs/api/class-frame) that initiated this request. * * **Usage** * @@ -19190,9 +20237,10 @@ export interface Request { /** * Request that was redirected by the server to this one, if any. * - * When the server responds with a redirect, Playwright creates a new {@link Request} object. The two requests are - * connected by `redirectedFrom()` and `redirectedTo()` methods. When multiple server redirects has happened, it is - * possible to construct the whole redirect chain by repeatedly calling `redirectedFrom()`. + * When the server responds with a redirect, Playwright creates a new + * [Request](https://playwright.dev/docs/api/class-request) object. The two requests are connected by + * `redirectedFrom()` and `redirectedTo()` methods. When multiple server redirects has happened, it is possible to + * construct the whole redirect chain by repeatedly calling `redirectedFrom()`. * * **Usage** * @@ -19236,12 +20284,13 @@ export interface Request { resourceType(): string; /** - * Returns the matching {@link Response} object, or `null` if the response was not received due to error. + * Returns the matching [Response](https://playwright.dev/docs/api/class-response) object, or `null` if the response + * was not received due to error. */ response(): Promise<null|Response>; /** - * The Service {@link Worker} that is performing the request. + * The Service [Worker](https://playwright.dev/docs/api/class-worker) that is performing the request. * * **Details** * @@ -19357,7 +20406,7 @@ export interface Request { } /** - * {@link Response} class represents responses which are received by page. + * [Response](https://playwright.dev/docs/api/class-response) class represents responses which are received by page. */ export interface Response { /** @@ -19376,7 +20425,7 @@ export interface Response { finished(): Promise<null|Error>; /** - * Returns the {@link Frame} that initiated this response. + * Returns the [Frame](https://playwright.dev/docs/api/class-frame) that initiated this response. */ frame(): Frame; @@ -19438,7 +20487,7 @@ export interface Response { ok(): boolean; /** - * Returns the matching {@link Request} object. + * Returns the matching [Request](https://playwright.dev/docs/api/class-request) object. */ request(): Request; @@ -19557,9 +20606,10 @@ export interface Route { * * **Details** * - * Note that any overrides such as `url` or `headers` only apply to the request being routed. If this request results - * in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header - * through redirects, use the combination of + * Note that any overrides such as [`url`](https://playwright.dev/docs/api/class-route#route-continue-option-url) or + * [`headers`](https://playwright.dev/docs/api/class-route#route-continue-option-headers) only apply to the request + * being routed. If this request results in a redirect, overrides will not be applied to the new redirected request. + * If you want to propagate a header through redirects, use the combination of * [route.fetch([options])](https://playwright.dev/docs/api/class-route#route-fetch) and * [route.fulfill([options])](https://playwright.dev/docs/api/class-route#route-fulfill) instead. * @@ -19702,9 +20752,11 @@ export interface Route { * * **Details** * - * Note that `headers` option will apply to the fetched request as well as any redirects initiated by it. If you want - * to only apply `headers` to the original request, but not to redirects, look into - * [route.continue([options])](https://playwright.dev/docs/api/class-route#route-continue) instead. + * Note that [`headers`](https://playwright.dev/docs/api/class-route#route-fetch-option-headers) option will apply to + * the fetched request as well as any redirects initiated by it. If you want to only apply + * [`headers`](https://playwright.dev/docs/api/class-route#route-fetch-option-headers) to the original request, but + * not to redirects, look into [route.continue([options])](https://playwright.dev/docs/api/class-route#route-continue) + * instead. * @param options */ fetch(options?: { @@ -19801,8 +20853,8 @@ export interface Route { path?: string; /** - * {@link APIResponse} to fulfill route's request with. Individual fields of the response (such as headers) can be - * overridden using fulfill options. + * [APIResponse](https://playwright.dev/docs/api/class-apiresponse) to fulfill route's request with. Individual fields + * of the response (such as headers) can be overridden using fulfill options. */ response?: APIResponse; @@ -19905,10 +20957,13 @@ export interface Selectors { */ export interface Touchscreen { /** - * Dispatches a `touchstart` and `touchend` event with a single touch at the position (`x`,`y`). + * Dispatches a `touchstart` and `touchend` event with a single touch at the position + * ([`x`](https://playwright.dev/docs/api/class-touchscreen#touchscreen-tap-option-x),[`y`](https://playwright.dev/docs/api/class-touchscreen#touchscreen-tap-option-y)). * * **NOTE** [page.tap(selector[, options])](https://playwright.dev/docs/api/class-page#page-tap) the method will throw - * if `hasTouch` option of the browser context is false. + * if [`hasTouch`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-has-touch) option of the + * browser context is false. + * * @param x X coordinate relative to the main frame's viewport in CSS pixels. * @param y Y coordinate relative to the main frame's viewport in CSS pixels. */ @@ -19949,7 +21004,8 @@ export interface Tracing { start(options?: { /** * If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the - * `tracesDir` folder specified in + * [`tracesDir`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-traces-dir) directory + * specified in * [browserType.launch([options])](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). To specify * the final trace zip file name, you need to pass `path` option to * [tracing.stop([options])](https://playwright.dev/docs/api/class-tracing#tracing-stop) instead. @@ -19980,7 +21036,8 @@ export interface Tracing { }): Promise<void>; /** - * Start a new trace chunk. If you'd like to record multiple traces on the same {@link BrowserContext}, use + * Start a new trace chunk. If you'd like to record multiple traces on the same + * [BrowserContext](https://playwright.dev/docs/api/class-browsercontext), use * [tracing.start([options])](https://playwright.dev/docs/api/class-tracing#tracing-start) once, and then create * multiple trace chunks with * [tracing.startChunk([options])](https://playwright.dev/docs/api/class-tracing#tracing-start-chunk) and @@ -20009,7 +21066,8 @@ export interface Tracing { startChunk(options?: { /** * If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the - * `tracesDir` folder specified in + * [`tracesDir`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-traces-dir) directory + * specified in * [browserType.launch([options])](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). To specify * the final trace zip file name, you need to pass `path` option to * [tracing.stopChunk([options])](https://playwright.dev/docs/api/class-tracing#tracing-stop-chunk) instead. @@ -20078,7 +21136,8 @@ export interface Video { } /** - * {@link WebError} class represents an unhandled exception thrown in the page. It is dispatched via the + * [WebError](https://playwright.dev/docs/api/class-weberror) class represents an unhandled exception thrown in the + * page. It is dispatched via the * [browserContext.on('weberror')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-web-error) * event. * @@ -20106,7 +21165,8 @@ export interface WebError { } /** - * The {@link WebSocket} class represents websocket connections in the page. + * The [WebSocket](https://playwright.dev/docs/api/class-websocket) class represents websocket connections in the + * page. */ export interface WebSocket { /** @@ -20386,7 +21446,8 @@ export interface LaunchOptions { /** * **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the - * `headless` option will be set `false`. + * [`headless`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-headless) option will be + * set `false`. * @deprecated Use [debugging tools](https://playwright.dev/docs/debug) instead. */ devtools?: boolean; @@ -20404,9 +21465,10 @@ export interface LaunchOptions { env?: { [key: string]: string|number|boolean; }; /** - * Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is - * resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium, - * Firefox or WebKit, use at your own risk. + * Path to a browser executable to run instead of the bundled one. If + * [`executablePath`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-executable-path) is + * a relative path, then it is resolved relative to the current working directory. Note that Playwright only works + * with the bundled Chromium, Firefox or WebKit, use at your own risk. */ executablePath?: string; @@ -20435,13 +21497,15 @@ export interface LaunchOptions { * Whether to run browser in headless mode. More details for * [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and * [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the - * `devtools` option is `true`. + * [`devtools`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-devtools) option is + * `true`. */ headless?: boolean; /** - * If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is - * given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`. + * If `true`, Playwright does not pass its own configurations args and only uses the ones from + * [`args`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-args). If an array is given, + * then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`. */ ignoreDefaultArgs?: boolean|Array<string>; @@ -20582,7 +21646,9 @@ export interface LocatorScreenshotOptions { /** * Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink - * box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. + * box `#FF00FF` (customized by + * [`maskColor`](https://playwright.dev/docs/api/class-locator#locator-screenshot-option-mask-color)) that completely + * covers its bounding box. */ mask?: Array<Locator>; @@ -20599,9 +21665,10 @@ export interface LocatorScreenshotOptions { omitBackground?: boolean; /** - * The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a - * relative path, then it is resolved relative to the current working directory. If no path is provided, the image - * won't be saved to the disk. + * The file path to save the image to. The screenshot type will be inferred from file extension. If + * [`path`](https://playwright.dev/docs/api/class-locator#locator-screenshot-option-path) is a relative path, then it + * is resolved relative to the current working directory. If no path is provided, the image won't be saved to the + * disk. */ path?: string; @@ -20708,6 +21775,7 @@ export interface BrowserContextOptions { * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. + * */ clientCertificates?: Array<{ /** @@ -20893,8 +21961,9 @@ export interface BrowserContextOptions { mode?: "full"|"minimal"; /** - * A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was - * provided and the passed URL is a path, it gets merged via the + * A glob or regex pattern to filter requests that are stored in the HAR. When a + * [`baseURL`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-base-url) via the context + * options was provided and the passed URL is a path, it gets merged via the * [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none. */ urlFilter?: string|RegExp; @@ -20939,7 +22008,7 @@ export interface BrowserContextOptions { /** * Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the - * `viewport` is set. + * [`viewport`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-viewport) is set. */ screen?: { /** @@ -21020,8 +22089,8 @@ export interface BrowserContextOptions { /** * If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on * selectors that imply single target DOM element will throw when more than one element matches the selector. This - * option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See {@link Locator} to - * learn more about the strict mode. + * option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See + * [Locator](https://playwright.dev/docs/api/class-locator) to learn more about the strict mode. */ strictSelectors?: boolean; @@ -21038,7 +22107,7 @@ export interface BrowserContextOptions { userAgent?: string; /** - * @deprecated Use `recordVideo` instead. + * @deprecated Use [`recordVideo`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-video) instead. */ videoSize?: { /** @@ -21053,7 +22122,7 @@ export interface BrowserContextOptions { }; /** - * @deprecated Use `recordVideo` instead. + * @deprecated Use [`recordVideo`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-video) instead. */ videosPath?: string; @@ -21063,6 +22132,7 @@ export interface BrowserContextOptions { * * **NOTE** The `null` value opts out from the default presets, makes viewport depend on the host window size defined * by the operating system. It makes the execution of the tests non-deterministic. + * */ viewport?: null|ViewportSize; } @@ -21090,10 +22160,11 @@ export interface HTTPCredentials { origin?: string; /** - * This option only applies to the requests sent from corresponding {@link APIRequestContext} and does not affect - * requests sent from the browser. `'always'` - `Authorization` header with basic authentication credentials will be - * sent with the each API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response - * with `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. + * This option only applies to the requests sent from corresponding + * [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) and does not affect requests sent from + * the browser. `'always'` - `Authorization` header with basic authentication credentials will be sent with the each + * API request. `'unauthorized` - the credentials are only sent when 401 (Unauthorized) response with + * `WWW-Authenticate` header is received. Defaults to `'unauthorized'`. */ send?: "unauthorized"|"always"; } @@ -21165,9 +22236,11 @@ interface PageWaitForSelectorOptions { interface PageWaitForFunctionOptions { /** - * If `polling` is `'raf'`, then `pageFunction` is constantly executed in `requestAnimationFrame` callback. If - * `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. - * Defaults to `raf`. + * If [`polling`](https://playwright.dev/docs/api/class-page#page-wait-for-function-option-polling) is `'raf'`, then + * [`pageFunction`](https://playwright.dev/docs/api/class-page#page-wait-for-function-option-expression) is constantly + * executed in `requestAnimationFrame` callback. If + * [`polling`](https://playwright.dev/docs/api/class-page#page-wait-for-function-option-polling) is a number, then it + * is treated as an interval in milliseconds at which the function would be executed. Defaults to `raf`. */ polling?: number|"raf"; @@ -21230,7 +22303,9 @@ export interface PageScreenshotOptions { /** * Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink - * box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. + * box `#FF00FF` (customized by + * [`maskColor`](https://playwright.dev/docs/api/class-page#page-screenshot-option-mask-color)) that completely covers + * its bounding box. */ mask?: Array<Locator>; @@ -21247,9 +22322,9 @@ export interface PageScreenshotOptions { omitBackground?: boolean; /** - * The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a - * relative path, then it is resolved relative to the current working directory. If no path is provided, the image - * won't be saved to the disk. + * The file path to save the image to. The screenshot type will be inferred from file extension. If + * [`path`](https://playwright.dev/docs/api/class-page#page-screenshot-option-path) is a relative path, then it is + * resolved relative to the current working directory. If no path is provided, the image won't be saved to the disk. */ path?: string; diff --git a/packages/playwright-ct-core/package.json b/packages/playwright-ct-core/package.json index ef959ebeb5..93b45c19e0 100644 --- a/packages/playwright-ct-core/package.json +++ b/packages/playwright-ct-core/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-core", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright Component Testing Helpers", "repository": { "type": "git", @@ -26,8 +26,8 @@ } }, "dependencies": { - "playwright-core": "1.48.0-next", + "playwright-core": "1.49.0-next", "vite": "^5.2.8", - "playwright": "1.48.0-next" + "playwright": "1.49.0-next" } } diff --git a/packages/playwright-ct-react/package.json b/packages/playwright-ct-react/package.json index b024d75855..02ce2fc3c3 100644 --- a/packages/playwright-ct-react/package.json +++ b/packages/playwright-ct-react/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-react/registerSource.mjs b/packages/playwright-ct-react/registerSource.mjs index e5a14c3db6..4b233a8dec 100644 --- a/packages/playwright-ct-react/registerSource.mjs +++ b/packages/playwright-ct-react/registerSource.mjs @@ -33,12 +33,24 @@ function isJsxComponent(component) { } /** + * @param {any} type + * @returns {boolean} type is Playwright's mock JSX.Fragment + */ +function isJsxFragment(type) { + return typeof type === 'object' && type?.__pw_jsx_fragment; +} + +/** + * Turns the Playwright representation of JSX (see jsx-runtime.js) into React.createElement calls. * @param {any} value */ function __pwRender(value) { return window.__pwTransformObject(value, v => { if (isJsxComponent(v)) { const component = v; + let type = component.type; + if (isJsxFragment(type)) + type = __pwReact.Fragment; const props = component.props ? __pwRender(component.props) : {}; const key = component.key ? __pwRender(component.key) : undefined; const { children, ...propsWithoutChildren } = props; @@ -47,7 +59,7 @@ function __pwRender(value) { const createElementArguments = [propsWithoutChildren]; if (children) createElementArguments.push(children); - return { result: __pwReact.createElement(component.type, ...createElementArguments) }; + return { result: __pwReact.createElement(type, ...createElementArguments) }; } }); } diff --git a/packages/playwright-ct-react17/package.json b/packages/playwright-ct-react17/package.json index 46e1c80fff..92bf481f6a 100644 --- a/packages/playwright-ct-react17/package.json +++ b/packages/playwright-ct-react17/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react17", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-solid/package.json b/packages/playwright-ct-solid/package.json index 5fa614a43b..518e070e17 100644 --- a/packages/playwright-ct-solid/package.json +++ b/packages/playwright-ct-solid/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-solid", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright Component Testing for Solid", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "vite-plugin-solid": "^2.7.0" }, "devDependencies": { diff --git a/packages/playwright-ct-svelte/package.json b/packages/playwright-ct-svelte/package.json index 1b3cb47789..57129d4651 100644 --- a/packages/playwright-ct-svelte/package.json +++ b/packages/playwright-ct-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-svelte", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright Component Testing for Svelte", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@sveltejs/vite-plugin-svelte": "^3.0.1" }, "devDependencies": { diff --git a/packages/playwright-ct-vue/package.json b/packages/playwright-ct-vue/package.json index f648cf2aca..82a5968568 100644 --- a/packages/playwright-ct-vue/package.json +++ b/packages/playwright-ct-vue/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright Component Testing for Vue", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@vitejs/plugin-vue": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-vue/registerSource.mjs b/packages/playwright-ct-vue/registerSource.mjs index 07ce5298f4..33bc74e731 100644 --- a/packages/playwright-ct-vue/registerSource.mjs +++ b/packages/playwright-ct-vue/registerSource.mjs @@ -188,7 +188,7 @@ function __pwWrapFunctions(slots) { for (const [key, value] of Object.entries(slots || {})) slotsWithRenderFunctions[key] = () => [value]; } else if (slots?.length) { - slots['default'] = () => slots; + slotsWithRenderFunctions['default'] = () => slots; } return slotsWithRenderFunctions; } diff --git a/packages/playwright-ct-vue2/package.json b/packages/playwright-ct-vue2/package.json index c104e44ca6..9ec30191ad 100644 --- a/packages/playwright-ct-vue2/package.json +++ b/packages/playwright-ct-vue2/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue2", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "Playwright Component Testing for Vue2", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.48.0-next", + "@playwright/experimental-ct-core": "1.49.0-next", "@vitejs/plugin-vue2": "^2.2.0" }, "devDependencies": { diff --git a/packages/playwright-firefox/package.json b/packages/playwright-firefox/package.json index 7fe7b929f1..8949b1eccc 100644 --- a/packages/playwright-firefox/package.json +++ b/packages/playwright-firefox/package.json @@ -1,6 +1,6 @@ { "name": "playwright-firefox", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "A high-level API to automate Firefox", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" } } diff --git a/packages/playwright-test/package.json b/packages/playwright-test/package.json index d70f9f0a18..c3067c5f7e 100644 --- a/packages/playwright-test/package.json +++ b/packages/playwright-test/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/test", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -30,6 +30,6 @@ }, "scripts": {}, "dependencies": { - "playwright": "1.48.0-next" + "playwright": "1.49.0-next" } } diff --git a/packages/playwright-webkit/package.json b/packages/playwright-webkit/package.json index ff6c8e97b0..9de034f93b 100644 --- a/packages/playwright-webkit/package.json +++ b/packages/playwright-webkit/package.json @@ -1,6 +1,6 @@ { "name": "playwright-webkit", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "A high-level API to automate WebKit", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" } } diff --git a/packages/playwright/bundles/babel/package-lock.json b/packages/playwright/bundles/babel/package-lock.json index 0902f0601e..d3531ca0d7 100644 --- a/packages/playwright/bundles/babel/package-lock.json +++ b/packages/playwright/bundles/babel/package-lock.json @@ -11,7 +11,6 @@ "@babel/code-frame": "^7.24.2", "@babel/core": "^7.24.4", "@babel/helper-plugin-utils": "^7.24.0", - "@babel/parser": "^7.24.4", "@babel/plugin-proposal-decorators": "^7.24.1", "@babel/plugin-proposal-explicit-resource-management": "^7.24.1", "@babel/plugin-syntax-async-generators": "^7.8.4", diff --git a/packages/playwright/bundles/babel/package.json b/packages/playwright/bundles/babel/package.json index 27853cf80e..90a0aa85de 100644 --- a/packages/playwright/bundles/babel/package.json +++ b/packages/playwright/bundles/babel/package.json @@ -12,7 +12,6 @@ "@babel/code-frame": "^7.24.2", "@babel/core": "^7.24.4", "@babel/helper-plugin-utils": "^7.24.0", - "@babel/parser": "^7.24.4", "@babel/plugin-proposal-decorators": "^7.24.1", "@babel/plugin-proposal-explicit-resource-management": "^7.24.1", "@babel/plugin-syntax-async-generators": "^7.8.4", diff --git a/packages/playwright/bundles/babel/src/babelBundleImpl.ts b/packages/playwright/bundles/babel/src/babelBundleImpl.ts index f4e44fcea3..81e617ef27 100644 --- a/packages/playwright/bundles/babel/src/babelBundleImpl.ts +++ b/packages/playwright/bundles/babel/src/babelBundleImpl.ts @@ -23,7 +23,6 @@ import * as babel from '@babel/core'; export { codeFrameColumns } from '@babel/code-frame'; export { declare } from '@babel/helper-plugin-utils'; export { types } from '@babel/core'; -export { parse } from '@babel/parser'; import traverseFunction from '@babel/traverse'; export const traverse = traverseFunction; @@ -46,6 +45,7 @@ function babelTransformOptions(isTypeScript: boolean, isModule: boolean, plugins [require('@babel/plugin-syntax-async-generators')], [require('@babel/plugin-syntax-object-rest-spread')], [require('@babel/plugin-transform-export-namespace-from')], + [require('@babel/plugin-syntax-import-attributes'), { deprecatedAssertSyntax: true }], [ // From https://github.com/G-Rath/babel-plugin-replace-ts-export-assignment/blob/8dfdca32c8aa428574b0cae341444fc5822f2dc6/src/index.ts ( @@ -87,8 +87,6 @@ function babelTransformOptions(isTypeScript: boolean, isModule: boolean, plugins } }) ]); - } else { - plugins.push([require('@babel/plugin-syntax-import-attributes'), { deprecatedAssertSyntax: true }]); } return { diff --git a/packages/playwright/jsx-runtime.js b/packages/playwright/jsx-runtime.js index d35e5e6e8d..ff3d79e34e 100644 --- a/packages/playwright/jsx-runtime.js +++ b/packages/playwright/jsx-runtime.js @@ -32,7 +32,8 @@ function jsxs(type, props, key) { }; } -const Fragment = {}; +// this is used in <></> notation +const Fragment = { __pw_jsx_fragment: true }; module.exports = { Fragment, diff --git a/packages/playwright/package.json b/packages/playwright/package.json index 16c3bd24c7..1d196e253b 100644 --- a/packages/playwright/package.json +++ b/packages/playwright/package.json @@ -1,6 +1,6 @@ { "name": "playwright", - "version": "1.48.0-next", + "version": "1.49.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -56,7 +56,7 @@ }, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.48.0-next" + "playwright-core": "1.49.0-next" }, "optionalDependencies": { "fsevents": "2.3.2" diff --git a/packages/playwright/src/common/testType.ts b/packages/playwright/src/common/testType.ts index 5c7850a3df..f0882735dc 100644 --- a/packages/playwright/src/common/testType.ts +++ b/packages/playwright/src/common/testType.ts @@ -259,11 +259,11 @@ export class TestTypeImpl { suite._use.push({ fixtures, location }); } - async _step<T>(title: string, body: () => Promise<T>, options: { box?: boolean } = {}): Promise<T> { + async _step<T>(title: string, body: () => Promise<T>, options: {box?: boolean, location?: Location } = {}): Promise<T> { const testInfo = currentTestInfo(); if (!testInfo) throw new Error(`test.step() can only be called from a test`); - const step = testInfo._addStep({ category: 'test.step', title, box: options.box }); + const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box }); return await zones.run('stepZone', step, async () => { try { const result = await body(); diff --git a/packages/playwright/src/isomorphic/testServerInterface.ts b/packages/playwright/src/isomorphic/testServerInterface.ts index 22cb9e35ef..257d04c681 100644 --- a/packages/playwright/src/isomorphic/testServerInterface.ts +++ b/packages/playwright/src/isomorphic/testServerInterface.ts @@ -45,7 +45,7 @@ export interface TestServerInterface { installBrowsers(params: {}): Promise<void>; - runGlobalSetup(params: { outputDir?: string }): Promise<{ + runGlobalSetup(params: {}): Promise<{ report: ReportEntry[], status: reporterTypes.FullResult['status'] }>; @@ -82,7 +82,6 @@ export interface TestServerInterface { locations?: string[]; grep?: string; grepInvert?: string; - outputDir?: string; }): Promise<{ report: ReportEntry[], status: reporterTypes.FullResult['status'] @@ -95,8 +94,6 @@ export interface TestServerInterface { testIds?: string[]; headed?: boolean; workers?: number | string; - timeout?: number, - outputDir?: string; updateSnapshots?: 'all' | 'none' | 'missing'; reporters?: string[], trace?: 'on' | 'off'; diff --git a/packages/playwright/src/matchers/expect.ts b/packages/playwright/src/matchers/expect.ts index 3a254ae7bf..16300607d9 100644 --- a/packages/playwright/src/matchers/expect.ts +++ b/packages/playwright/src/matchers/expect.ts @@ -140,8 +140,7 @@ function createExpect(info: ExpectMetaInfo, prefix: string[], customMatchers: Re const wrappedMatchers: any = {}; const extendedMatchers: any = { ...customMatchers }; for (const [name, matcher] of Object.entries(matchers)) { - const key = qualifiedMatcherName(qualifier, name); - wrappedMatchers[key] = function(...args: any[]) { + wrappedMatchers[name] = function(...args: any[]) { const { isNot, promise, utils } = this; const newThis: ExpectMatcherState = { isNot, @@ -152,6 +151,8 @@ function createExpect(info: ExpectMetaInfo, prefix: string[], customMatchers: Re (newThis as any).equals = throwUnsupportedExpectMatcherError; return (matcher as any).call(newThis, ...args); }; + const key = qualifiedMatcherName(qualifier, name); + wrappedMatchers[key] = wrappedMatchers[name]; Object.defineProperty(wrappedMatchers[key], 'name', { value: name }); extendedMatchers[name] = wrappedMatchers[key]; } diff --git a/packages/playwright/src/program.ts b/packages/playwright/src/program.ts index 775ef27065..b4a12c0ea7 100644 --- a/packages/playwright/src/program.ts +++ b/packages/playwright/src/program.ts @@ -161,19 +161,14 @@ async function runTests(args: string[], opts: { [key: string]: any }) { if (opts.onlyChanged) throw new Error(`--only-changed is not supported in UI mode. If you'd like that to change, see https://github.com/microsoft/playwright/issues/15075 for more details.`); - const status = await testServer.runUIMode(opts.config, { + const status = await testServer.runUIMode(opts.config, cliOverrides, { host: opts.uiHost, port: opts.uiPort ? +opts.uiPort : undefined, args, grep: opts.grep as string | undefined, grepInvert: opts.grepInvert as string | undefined, project: opts.project || undefined, - headed: opts.headed, reporter: Array.isArray(opts.reporter) ? opts.reporter : opts.reporter ? [opts.reporter] : undefined, - workers: cliOverrides.workers, - timeout: cliOverrides.timeout, - outputDir: cliOverrides.outputDir, - updateSnapshots: cliOverrides.updateSnapshots, }); await stopProfiling('runner'); if (status === 'restarted') @@ -227,7 +222,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) { async function runTestServer(opts: { [key: string]: any }) { const host = opts.host || 'localhost'; const port = opts.port ? +opts.port : 0; - const status = await testServer.runTestServer(opts.config, { host, port }); + const status = await testServer.runTestServer(opts.config, { }, { host, port }); if (status === 'restarted') return; const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1); diff --git a/packages/playwright/src/reporters/base.ts b/packages/playwright/src/reporters/base.ts index e0aa7fa04e..6776ce606f 100644 --- a/packages/playwright/src/reporters/base.ts +++ b/packages/playwright/src/reporters/base.ts @@ -567,28 +567,24 @@ export function resolveOutputFile(reporterName: string, options: { } }): { outputFile: string, outputDir?: string } |undefined { const name = reporterName.toUpperCase(); - let outputFile; - if (options.outputFile) + let outputFile = resolveFromEnv(`PLAYWRIGHT_${name}_OUTPUT_FILE`); + if (!outputFile && options.outputFile) outputFile = path.resolve(options.configDir, options.outputFile); - if (!outputFile) - outputFile = resolveFromEnv(`PLAYWRIGHT_${name}_OUTPUT_FILE`); - // Return early to avoid deleting outputDir. if (outputFile) return { outputFile }; - let outputDir; - if (options.outputDir) + let outputDir = resolveFromEnv(`PLAYWRIGHT_${name}_OUTPUT_DIR`); + if (!outputDir && options.outputDir) outputDir = path.resolve(options.configDir, options.outputDir); - if (!outputDir) - outputDir = resolveFromEnv(`PLAYWRIGHT_${name}_OUTPUT_DIR`); if (!outputDir && options.default) outputDir = resolveReporterOutputPath(options.default.outputDir, options.configDir, undefined); + if (!outputDir) + outputDir = options.configDir; + + const reportName = process.env[`PLAYWRIGHT_${name}_OUTPUT_NAME`] ?? options.fileName ?? options.default?.fileName; + if (!reportName) + return undefined; + outputFile = path.resolve(outputDir, reportName); - if (!outputFile) { - const reportName = options.fileName ?? process.env[`PLAYWRIGHT_${name}_OUTPUT_NAME`] ?? options.default?.fileName; - if (!reportName) - return undefined; - outputFile = path.resolve(outputDir ?? process.cwd(), reportName); - } return { outputFile, outputDir }; } diff --git a/packages/playwright/src/reporters/html.ts b/packages/playwright/src/reporters/html.ts index fd1fb0cbdf..5aada7e495 100644 --- a/packages/playwright/src/reporters/html.ts +++ b/packages/playwright/src/reporters/html.ts @@ -143,7 +143,7 @@ class HtmlReporter implements ReporterV2 { const shouldOpen = !this._options._isTestServer && (this._open === 'always' || (!ok && this._open === 'on-failure')); if (shouldOpen) { await showHTMLReport(this._outputFolder, this._host, this._port, singleTestId); - } else if (this._options._mode === 'test') { + } else if (this._options._mode === 'test' && !this._options._isTestServer) { const packageManagerCommand = getPackageManagerExecCommand(); const relativeReportPath = this._outputFolder === standaloneDefaultFolder() ? '' : ' ' + path.relative(process.cwd(), this._outputFolder); const hostArg = this._host ? ` --host ${this._host}` : ''; diff --git a/packages/playwright/src/runner/testServer.ts b/packages/playwright/src/runner/testServer.ts index 5d67385dc5..50a8de366e 100644 --- a/packages/playwright/src/runner/testServer.ts +++ b/packages/playwright/src/runner/testServer.ts @@ -44,14 +44,16 @@ const originalStderrWrite = process.stderr.write; class TestServer { private _configLocation: ConfigLocation; + private _configCLIOverrides: ConfigCLIOverrides; private _dispatcher: TestServerDispatcher | undefined; - constructor(configLocation: ConfigLocation) { + constructor(configLocation: ConfigLocation, configCLIOverrides: ConfigCLIOverrides) { this._configLocation = configLocation; + this._configCLIOverrides = configCLIOverrides; } async start(options: { host?: string, port?: number }): Promise<HttpServer> { - this._dispatcher = new TestServerDispatcher(this._configLocation); + this._dispatcher = new TestServerDispatcher(this._configLocation, this._configCLIOverrides); return await startTraceViewerServer({ ...options, transport: this._dispatcher.transport }); } @@ -63,6 +65,7 @@ class TestServer { export class TestServerDispatcher implements TestServerInterface { private _configLocation: ConfigLocation; + private _configCLIOverrides: ConfigCLIOverrides; private _watcher: Watcher; private _watchedProjectDirs = new Set<string>(); @@ -81,9 +84,11 @@ export class TestServerDispatcher implements TestServerInterface { private _closeOnDisconnect = false; private _populateDependenciesOnList = false; - constructor(configLocation: ConfigLocation) { + constructor(configLocation: ConfigLocation, configCLIOverrides: ConfigCLIOverrides) { this._configLocation = configLocation; + this._configCLIOverrides = configCLIOverrides; this.transport = { + onconnect: () => {}, dispatch: (method, params) => (this as any)[method](params), onclose: () => { if (this._closeOnDisconnect) @@ -144,11 +149,8 @@ export class TestServerDispatcher implements TestServerInterface { async runGlobalSetup(params: Parameters<TestServerInterface['runGlobalSetup']>[0]): ReturnType<TestServerInterface['runGlobalSetup']> { await this.runGlobalTeardown(); - const overrides: ConfigCLIOverrides = { - outputDir: params.outputDir, - }; const { reporter, report } = await this._collectingInternalReporter(new ListReporter()); - const config = await this._loadConfigOrReportError(reporter, overrides); + const config = await this._loadConfigOrReportError(reporter, this._configCLIOverrides); if (!config) return { status: 'failed', report }; @@ -238,9 +240,9 @@ export class TestServerDispatcher implements TestServerInterface { config?: FullConfigInternal, }> { const overrides: ConfigCLIOverrides = { + ...this._configCLIOverrides, repeatEach: 1, retries: 0, - outputDir: params.outputDir, }; const { reporter, report } = await this._collectingInternalReporter(); const config = await this._loadConfigOrReportError(reporter, overrides); @@ -294,21 +296,21 @@ export class TestServerDispatcher implements TestServerInterface { private async _innerRunTests(params: Parameters<TestServerInterface['runTests']>[0]): ReturnType<TestServerInterface['runTests']> { await this.stopTests(); const overrides: ConfigCLIOverrides = { + ...this._configCLIOverrides, repeatEach: 1, retries: 0, preserveOutputDir: true, - timeout: params.timeout, reporter: params.reporters ? params.reporters.map(r => [r]) : undefined, use: { + ...(this._configCLIOverrides.use || {}), trace: params.trace === 'on' ? { mode: 'on', sources: false, _live: true } : (params.trace === 'off' ? 'off' : undefined), video: params.video === 'on' ? 'on' : (params.video === 'off' ? 'off' : undefined), headless: params.headed ? false : undefined, _optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined, _optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : undefined, }, - outputDir: params.outputDir, - updateSnapshots: params.updateSnapshots, - workers: params.workers, + ...(params.updateSnapshots ? { updateSnapshots: params.updateSnapshots } : {}), + ...(params.workers ? { workers: params.workers } : {}), }; if (params.trace === 'on') process.env.PW_LIVE_TRACE_STACKS = '1'; @@ -423,9 +425,9 @@ export class TestServerDispatcher implements TestServerInterface { } } -export async function runUIMode(configFile: string | undefined, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise<reporterTypes.FullResult['status'] | 'restarted'> { +export async function runUIMode(configFile: string | undefined, configCLIOverrides: ConfigCLIOverrides, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise<reporterTypes.FullResult['status'] | 'restarted'> { const configLocation = resolveConfigLocation(configFile); - return await innerRunTestServer(configLocation, options, async (server: HttpServer, cancelPromise: ManualPromise<void>) => { + return await innerRunTestServer(configLocation, configCLIOverrides, options, async (server: HttpServer, cancelPromise: ManualPromise<void>) => { await installRootRedirect(server, [], { ...options, webApp: 'uiMode.html' }); if (options.host !== undefined || options.port !== undefined) { await openTraceInBrowser(server.urlPrefix('human-readable')); @@ -441,18 +443,18 @@ export async function runUIMode(configFile: string | undefined, options: TraceVi }); } -export async function runTestServer(configFile: string | undefined, options: { host?: string, port?: number }): Promise<reporterTypes.FullResult['status'] | 'restarted'> { +export async function runTestServer(configFile: string | undefined, configCLIOverrides: ConfigCLIOverrides, options: { host?: string, port?: number }): Promise<reporterTypes.FullResult['status'] | 'restarted'> { const configLocation = resolveConfigLocation(configFile); - return await innerRunTestServer(configLocation, options, async server => { + return await innerRunTestServer(configLocation, configCLIOverrides, options, async server => { // eslint-disable-next-line no-console console.log('Listening on ' + server.urlPrefix('precise').replace('http:', 'ws:') + '/' + server.wsGuid()); }); } -async function innerRunTestServer(configLocation: ConfigLocation, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>, configLocation: ConfigLocation) => Promise<void>): Promise<reporterTypes.FullResult['status'] | 'restarted'> { +async function innerRunTestServer(configLocation: ConfigLocation, configCLIOverrides: ConfigCLIOverrides, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>, configLocation: ConfigLocation) => Promise<void>): Promise<reporterTypes.FullResult['status'] | 'restarted'> { if (restartWithExperimentalTsEsm(undefined, true)) return 'restarted'; - const testServer = new TestServer(configLocation); + const testServer = new TestServer(configLocation, configCLIOverrides); const cancelPromise = new ManualPromise<void>(); const sigintWatcher = new SigIntWatcher(); process.stdin.on('close', () => gracefullyProcessExitDoNotHang(0)); diff --git a/packages/playwright/src/runner/watchMode.ts b/packages/playwright/src/runner/watchMode.ts index e086ac21e2..9c164e7ff3 100644 --- a/packages/playwright/src/runner/watchMode.ts +++ b/packages/playwright/src/runner/watchMode.ts @@ -75,7 +75,7 @@ export async function runWatchModeLoop(configLocation: ConfigLocation, initialOp const options: WatchModeOptions = { ...initialOptions }; let bufferMode = false; - const testServerDispatcher = new TestServerDispatcher(configLocation); + const testServerDispatcher = new TestServerDispatcher(configLocation, {}); const transport = new InMemoryTransport( async data => { const { id, method, params } = JSON.parse(data); @@ -144,11 +144,13 @@ export async function runWatchModeLoop(configLocation: ConfigLocation, initialOp else printPrompt(); + const waitForCommand = readCommand(); const command = await Promise.race([ onDirtyTests, - readCommand(), + waitForCommand.result, ]); - + if (command === 'changed') + waitForCommand.cancel(); if (bufferMode && command === 'changed') continue; @@ -264,27 +266,32 @@ export async function runWatchModeLoop(configLocation: ConfigLocation, initialOp return result === 'passed' ? teardown.status : result; } -function readKeyPress<T extends string>(handler: (text: string, key: any) => T | undefined): Promise<T> { - return new Promise(resolve => { - const rl = readline.createInterface({ input: process.stdin, escapeCodeTimeout: 50 }); - readline.emitKeypressEvents(process.stdin, rl); +function readKeyPress<T extends string>(handler: (text: string, key: any) => T | undefined): { cancel(): void; result: Promise<T> } { + const promise = new ManualPromise<T>(); + + const rl = readline.createInterface({ input: process.stdin, escapeCodeTimeout: 50 }); + readline.emitKeypressEvents(process.stdin, rl); + if (process.stdin.isTTY) + process.stdin.setRawMode(true); + + const cancel = () => { + process.stdin.off('keypress', onKeypress); + rl.close(); if (process.stdin.isTTY) - process.stdin.setRawMode(true); + process.stdin.setRawMode(false); + }; - function onKeypress(text: string, key: any) { - const result = handler(text, key); - if (result) { - process.stdin.off('keypress', onKeypress); - rl.close(); - if (process.stdin.isTTY) - process.stdin.setRawMode(false); - - resolve(result); - } + function onKeypress(text: string, key: any) { + const result = handler(text, key); + if (result) { + cancel(); + promise.resolve(result); } + } - process.stdin.on('keypress', onKeypress); - }); + process.stdin.on('keypress', onKeypress); + + return { result: promise, cancel }; } const isInterrupt = (text: string, key: any) => text === '\x03' || text === '\x1B' || (key && key.name === 'escape') || (key && key.ctrl && key.name === 'c'); @@ -314,7 +321,7 @@ async function runTests(watchOptions: WatchModeOptions, testServerConnection: Te }); } -function readCommand(): Promise<Command> { +function readCommand() { return readKeyPress<Command>((text: string, key: any) => { if (isInterrupt(text, key)) return 'interrupted'; diff --git a/packages/playwright/src/runner/workerHost.ts b/packages/playwright/src/runner/workerHost.ts index c9029aaff0..834dd1facf 100644 --- a/packages/playwright/src/runner/workerHost.ts +++ b/packages/playwright/src/runner/workerHost.ts @@ -37,7 +37,7 @@ export class WorkerHost extends ProcessHost { super(require.resolve('../worker/workerMain.js'), `worker-${workerIndex}`, { ...extraEnv, FORCE_COLOR: '1', - DEBUG_COLORS: '1', + DEBUG_COLORS: process.env.DEBUG_COLORS === undefined ? '1' : process.env.DEBUG_COLORS, }); this.workerIndex = workerIndex; this.parallelIndex = parallelIndex; diff --git a/packages/playwright/src/transform/babelBundle.ts b/packages/playwright/src/transform/babelBundle.ts index 2806a05aec..faf06b7158 100644 --- a/packages/playwright/src/transform/babelBundle.ts +++ b/packages/playwright/src/transform/babelBundle.ts @@ -18,7 +18,6 @@ import type { BabelFileResult } from '../../bundles/babel/node_modules/@types/ba export const codeFrameColumns: typeof import('../../bundles/babel/node_modules/@types/babel__code-frame').codeFrameColumns = require('./babelBundleImpl').codeFrameColumns; export const declare: typeof import('../../bundles/babel/node_modules/@types/babel__helper-plugin-utils').declare = require('./babelBundleImpl').declare; export const types: typeof import('../../bundles/babel/node_modules/@types/babel__core').types = require('./babelBundleImpl').types; -export const parse: typeof import('../../bundles/babel/node_modules/@babel/parser/typings/babel-parser').parse = require('./babelBundleImpl').parse; export const traverse: typeof import('../../bundles/babel/node_modules/@types/babel__traverse').default = require('./babelBundleImpl').traverse; export type BabelPlugin = [string, any?]; export type BabelTransformFunction = (code: string, filename: string, isTypeScript: boolean, isModule: boolean, pluginsPrefix: BabelPlugin[], pluginsSuffix: BabelPlugin[]) => BabelFileResult; diff --git a/packages/playwright/src/worker/timeoutManager.ts b/packages/playwright/src/worker/timeoutManager.ts index 71b853c463..ef7665a650 100644 --- a/packages/playwright/src/worker/timeoutManager.ts +++ b/packages/playwright/src/worker/timeoutManager.ts @@ -60,6 +60,8 @@ export class TimeoutManager { setIgnoreTimeouts() { this._ignoreTimeouts = true; + if (this._running) + this._updateTimeout(this._running); } interrupt() { diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index e17a43843c..400d7cbcf3 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -36,13 +36,14 @@ type UseOptions<TestArgs, WorkerArgs> = Partial<WorkerArgs> & Partial<TestArgs>; /** * Playwright Test supports running multiple test projects at the same time. This is useful for running tests in * multiple configurations. For example, consider running tests against multiple browsers. This type describes format - * of a project in the configuration file, to access resolved configuration parameters at run time use {@link - * FullProject}. + * of a project in the configuration file, to access resolved configuration parameters at run time use + * [FullProject](https://playwright.dev/docs/api/class-fullproject). * * `TestProject` encapsulates configuration specific to a single project. Projects are configured in * [testConfig.projects](https://playwright.dev/docs/api/class-testconfig#test-config-projects) specified in the - * [configuration file](https://playwright.dev/docs/test-configuration). Note that all properties of {@link TestProject} are available in - * the top-level {@link TestConfig}, in which case they are shared between all projects. + * [configuration file](https://playwright.dev/docs/test-configuration). Note that all properties of + * [TestProject](https://playwright.dev/docs/api/class-testproject) are available in the top-level + * [TestConfig](https://playwright.dev/docs/api/class-testconfig), in which case they are shared between all projects. * * Here is an example configuration that runs every test in Chromium, Firefox and WebKit, both Desktop and Mobile * versions. @@ -89,7 +90,8 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> { /** * Options for all tests in this project, for example * [testOptions.browserName](https://playwright.dev/docs/api/class-testoptions#test-options-browser-name). Learn more - * about [configuration](https://playwright.dev/docs/test-configuration) and see [available options]{@link TestOptions}. + * about [configuration](https://playwright.dev/docs/test-configuration) and see + * [available options][TestOptions](https://playwright.dev/docs/api/class-testoptions). * * ```js * // playwright.config.ts @@ -190,25 +192,26 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> { maxDiffPixelRatio?: number; /** - * See `animations` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). - * Defaults to `"disabled"`. + * See [`animations`](https://playwright.dev/docs/api/class-page#page-screenshot-option-animations) in + * [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to `"disabled"`. */ animations?: "allow"|"disabled"; /** - * See `caret` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults - * to `"hide"`. + * See [`caret`](https://playwright.dev/docs/api/class-page#page-screenshot-option-caret) in + * [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to `"hide"`. */ caret?: "hide"|"initial"; /** - * See `scale` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults - * to `"css"`. + * See [`scale`](https://playwright.dev/docs/api/class-page#page-screenshot-option-scale) in + * [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to `"css"`. */ scale?: "css"|"device"; /** - * See `style` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). + * See [`style`](https://playwright.dev/docs/api/class-page#page-screenshot-option-style) in + * [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). */ stylePath?: string|Array<string>; }; @@ -641,8 +644,8 @@ export interface Project<TestArgs = {}, WorkerArgs = {}> extends TestProject<Tes * Runtime representation of the test project configuration. It is accessible in the tests via * [testInfo.project](https://playwright.dev/docs/api/class-testinfo#test-info-project) and * [workerInfo.project](https://playwright.dev/docs/api/class-workerinfo#worker-info-project) and is passed to the - * test reporters. To see the format of the project in the Playwright configuration file please see {@link - * TestProject} instead. + * test reporters. To see the format of the project in the Playwright configuration file please see + * [TestProject](https://playwright.dev/docs/api/class-testproject) instead. */ export interface FullProject<TestArgs = {}, WorkerArgs = {}> { /** @@ -724,13 +727,15 @@ type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never }); /** * Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` - * or `testDir`. These options are described in the {@link TestConfig} object in the - * [configuration file](https://playwright.dev/docs/test-configuration). This type describes format of the configuration file, to access - * resolved configuration parameters at run time use {@link FullConfig}. + * or `testDir`. These options are described in the [TestConfig](https://playwright.dev/docs/api/class-testconfig) + * object in the [configuration file](https://playwright.dev/docs/test-configuration). This type describes format of the configuration file, + * to access resolved configuration parameters at run time use + * [FullConfig](https://playwright.dev/docs/api/class-fullconfig). * * Playwright Test supports running multiple test projects at the same time. Project-specific options should be put to - * [testConfig.projects](https://playwright.dev/docs/api/class-testconfig#test-config-projects), but top-level {@link - * TestConfig} can also define base options shared between all projects. + * [testConfig.projects](https://playwright.dev/docs/api/class-testconfig#test-config-projects), but top-level + * [TestConfig](https://playwright.dev/docs/api/class-testconfig) can also define base options shared between all + * projects. * * ```js * // playwright.config.ts @@ -747,8 +752,8 @@ type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never }); */ interface TestConfig<TestArgs = {}, WorkerArgs = {}> { /** - * Playwright Test supports running multiple test projects at the same time. See {@link TestProject} for more - * information. + * Playwright Test supports running multiple test projects at the same time. See + * [TestProject](https://playwright.dev/docs/api/class-testproject) for more information. * * **Usage** * @@ -791,7 +796,8 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> { /** * Global options for all tests, for example * [testOptions.browserName](https://playwright.dev/docs/api/class-testoptions#test-options-browser-name). Learn more - * about [configuration](https://playwright.dev/docs/test-configuration) and see [available options]{@link TestOptions}. + * about [configuration](https://playwright.dev/docs/test-configuration) and see + * [available options][TestOptions](https://playwright.dev/docs/api/class-testoptions). * * **Usage** * @@ -945,14 +951,14 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> { */ toHaveScreenshot?: { /** - * See `animations` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). - * Defaults to `"disabled"`. + * See [`animations`](https://playwright.dev/docs/api/class-page#page-screenshot-option-animations) in + * [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to `"disabled"`. */ animations?: "allow"|"disabled"; /** - * See `caret` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults - * to `"hide"`. + * See [`caret`](https://playwright.dev/docs/api/class-page#page-screenshot-option-caret) in + * [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to `"hide"`. */ caret?: "hide"|"initial"; @@ -968,13 +974,14 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> { maxDiffPixelRatio?: number; /** - * See `scale` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults - * to `"css"`. + * See [`scale`](https://playwright.dev/docs/api/class-page#page-screenshot-option-scale) in + * [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). Defaults to `"css"`. */ scale?: "css"|"device"; /** - * See `style` in [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). + * See [`style`](https://playwright.dev/docs/api/class-page#page-screenshot-option-style) in + * [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot). */ stylePath?: string|Array<string>; @@ -1070,7 +1077,7 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> { /** * Path to the global setup file. This file will be required and run before all the tests. It must export a single - * function that takes a {@link FullConfig} argument. + * function that takes a [FullConfig](https://playwright.dev/docs/api/class-fullconfig) argument. * * Learn more about [global setup and teardown](https://playwright.dev/docs/test-global-setup-teardown). * @@ -1692,7 +1699,8 @@ export type Metadata = { [key: string]: any }; /** * Resolved configuration which is accessible via * [testInfo.config](https://playwright.dev/docs/api/class-testinfo#test-info-config) and is passed to the test - * reporters. To see the format of Playwright configuration file, please see {@link TestConfig} instead. + * reporters. To see the format of Playwright configuration file, please see + * [TestConfig](https://playwright.dev/docs/api/class-testconfig) instead. */ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> { /** @@ -2192,7 +2200,8 @@ interface TestFunction<TestArgs> { * Learn more about [test annotations](https://playwright.dev/docs/test-annotations). * @param title Test title. * @param details Additional test details. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ (title: string, body: (args: TestArgs, testInfo: TestInfo) => Promise<void> | void): void; /** @@ -2268,7 +2277,8 @@ interface TestFunction<TestArgs> { * Learn more about [test annotations](https://playwright.dev/docs/test-annotations). * @param title Test title. * @param details Additional test details. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ (title: string, details: TestDetails, body: (args: TestArgs, testInfo: TestInfo) => Promise<void> | void): void; } @@ -2304,7 +2314,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ only: TestFunction<TestArgs & WorkerArgs>; /** @@ -2515,6 +2526,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * * **NOTE** Using serial is not recommended. It is usually better to make your tests isolated, so they can be run * independently. + * * - `test.describe.serial(title, callback)` * - `test.describe.serial(title)` * - `test.describe.serial(title, details, callback)` @@ -2554,6 +2566,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * * **NOTE** Using serial is not recommended. It is usually better to make your tests isolated, so they can be run * independently. + * * - `test.describe.serial.only(title, callback)` * - `test.describe.serial.only(title)` * - `test.describe.serial.only(title, details, callback)` @@ -2795,7 +2808,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -2875,7 +2889,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -2955,7 +2970,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3035,7 +3051,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3115,7 +3132,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3192,7 +3210,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3269,7 +3288,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3346,7 +3366,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3423,7 +3444,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3500,7 +3522,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3576,7 +3599,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3652,7 +3676,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3728,7 +3753,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3804,7 +3830,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -3880,7 +3907,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param title Test title. * @param details See [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) for test details * description. - * @param body Test body that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param body Test body that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). * @param condition Test is marked as "should fail" when the condition is `true`. * @param callback A function that returns whether to mark as "should fail", based on test fixtures. Test or tests are marked as * "should fail" when the return value is `true`. @@ -4121,8 +4149,9 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group, runs * before each test in the group. * - * You can access all the same {@link Fixtures} as the test body itself, and also the {@link TestInfo} object that - * gives a lot of useful information. For example, you can navigate the page before starting the test. + * You can access all the same [Fixtures](https://playwright.dev/docs/api/class-fixtures) as the test body itself, and + * also the [TestInfo](https://playwright.dev/docs/api/class-testinfo) object that gives a lot of useful information. + * For example, you can navigate the page before starting the test. * * You can use [test.afterEach([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-each) to * teardown any resources set up in `beforeEach`. @@ -4162,7 +4191,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * @param title Hook title. - * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** @@ -4172,8 +4202,9 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group, runs * before each test in the group. * - * You can access all the same {@link Fixtures} as the test body itself, and also the {@link TestInfo} object that - * gives a lot of useful information. For example, you can navigate the page before starting the test. + * You can access all the same [Fixtures](https://playwright.dev/docs/api/class-fixtures) as the test body itself, and + * also the [TestInfo](https://playwright.dev/docs/api/class-testinfo) object that gives a lot of useful information. + * For example, you can navigate the page before starting the test. * * You can use [test.afterEach([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-each) to * teardown any resources set up in `beforeEach`. @@ -4213,7 +4244,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * @param title Hook title. - * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ beforeEach(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** @@ -4223,8 +4255,9 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group, runs * after each test in the group. * - * You can access all the same {@link Fixtures} as the test body itself, and also the {@link TestInfo} object that - * gives a lot of useful information. For example, you can check whether the test succeeded or failed. + * You can access all the same [Fixtures](https://playwright.dev/docs/api/class-fixtures) as the test body itself, and + * also the [TestInfo](https://playwright.dev/docs/api/class-testinfo) object that gives a lot of useful information. + * For example, you can check whether the test succeeded or failed. * - `test.afterEach(hookFunction)` * - `test.afterEach(title, hookFunction)` * @@ -4263,7 +4296,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * @param title Hook title. - * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** @@ -4273,8 +4307,9 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group, runs * after each test in the group. * - * You can access all the same {@link Fixtures} as the test body itself, and also the {@link TestInfo} object that - * gives a lot of useful information. For example, you can check whether the test succeeded or failed. + * You can access all the same [Fixtures](https://playwright.dev/docs/api/class-fixtures) as the test body itself, and + * also the [TestInfo](https://playwright.dev/docs/api/class-testinfo) object that gives a lot of useful information. + * For example, you can check whether the test succeeded or failed. * - `test.afterEach(hookFunction)` * - `test.afterEach(title, hookFunction)` * @@ -4313,7 +4348,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * @param title Hook title. - * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ afterEach(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** @@ -4366,7 +4402,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * @param title Hook title. - * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. + * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ beforeAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** @@ -4419,7 +4456,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * @param title Hook title. - * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. + * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ beforeAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** @@ -4459,7 +4497,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * @param title Hook title. - * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. + * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** @@ -4499,7 +4538,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * @param title Hook title. - * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. + * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional + * [TestInfo](https://playwright.dev/docs/api/class-testinfo). */ afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** @@ -4703,7 +4743,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * @param body Step body. * @param options */ - step<T>(title: string, body: () => T | Promise<T>, options?: { box?: boolean }): Promise<T>; + step<T>(title: string, body: () => T | Promise<T>, options?: { box?: boolean, location?: Location }): Promise<T>; /** * `expect` function can be used to create test assertions. Read more about [test assertions](https://playwright.dev/docs/test-assertions). * @@ -4862,8 +4902,9 @@ type ConnectOptions = { }; /** - * Playwright Test provides many options to configure test environment, {@link Browser}, {@link BrowserContext} and - * more. + * Playwright Test provides many options to configure test environment, + * [Browser](https://playwright.dev/docs/api/class-browser), + * [BrowserContext](https://playwright.dev/docs/api/class-browsercontext) and more. * * These options are usually provided in the [configuration file](https://playwright.dev/docs/test-configuration) through * [testConfig.use](https://playwright.dev/docs/api/class-testconfig#test-config-use) and @@ -4901,7 +4942,7 @@ type ConnectOptions = { export interface PlaywrightWorkerOptions { /** * Name of the browser that runs tests. Defaults to `'chromium'`. Most of the time you should set `browserName` in - * your {@link TestConfig}: + * your [TestConfig](https://playwright.dev/docs/api/class-testconfig): * * **Usage** * @@ -4923,7 +4964,8 @@ export interface PlaywrightWorkerOptions { * Whether to run browser in headless mode. More details for * [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and * [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the - * `devtools` option is `true`. + * [`devtools`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-devtools) option is + * `true`. * * **Usage** * @@ -5108,8 +5150,9 @@ export type TraceMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'; /** - * Playwright Test provides many options to configure test environment, {@link Browser}, {@link BrowserContext} and - * more. + * Playwright Test provides many options to configure test environment, + * [Browser](https://playwright.dev/docs/api/class-browser), + * [BrowserContext](https://playwright.dev/docs/api/class-browsercontext) and more. * * These options are usually provided in the [configuration file](https://playwright.dev/docs/test-configuration) through * [testConfig.use](https://playwright.dev/docs/api/class-testconfig#test-config-use) and @@ -5726,10 +5769,11 @@ export interface PlaywrightTestOptions { * ``` * * Given the test above, Playwright Test will set up the `page` fixture before running the test, and tear it down - * after the test has finished. `page` fixture provides a {@link Page} object that is available to the test. + * after the test has finished. `page` fixture provides a [Page](https://playwright.dev/docs/api/class-page) object + * that is available to the test. * * Playwright Test comes with builtin fixtures listed below, and you can add your own fixtures as well. Playwright - * Test also [provides options]{@link TestOptions} to configure + * Test also [provides options][TestOptions](https://playwright.dev/docs/api/class-testoptions) to configure * [fixtures.browser](https://playwright.dev/docs/api/class-fixtures#fixtures-browser), * [fixtures.context](https://playwright.dev/docs/api/class-fixtures#fixtures-context) and * [fixtures.page](https://playwright.dev/docs/api/class-fixtures#fixtures-page). @@ -5737,10 +5781,12 @@ export interface PlaywrightTestOptions { export interface PlaywrightWorkerArgs { playwright: typeof import('playwright-core'); /** - * {@link Browser} instance is shared between all tests in the [same worker](https://playwright.dev/docs/test-parallel) - this makes testing - * efficient. However, each test runs in an isolated {@link BrowserContext} and gets a fresh environment. + * [Browser](https://playwright.dev/docs/api/class-browser) instance is shared between all tests in the + * [same worker](https://playwright.dev/docs/test-parallel) - this makes testing efficient. However, each test runs in an isolated + * [BrowserContext](https://playwright.dev/docs/api/class-browsercontext) and gets a fresh environment. * - * Learn how to [configure browser](https://playwright.dev/docs/test-configuration) and see [available options]{@link TestOptions}. + * Learn how to [configure browser](https://playwright.dev/docs/test-configuration) and see + * [available options][TestOptions](https://playwright.dev/docs/api/class-testoptions). * * **Usage** * @@ -5772,21 +5818,23 @@ export interface PlaywrightWorkerArgs { * ``` * * Given the test above, Playwright Test will set up the `page` fixture before running the test, and tear it down - * after the test has finished. `page` fixture provides a {@link Page} object that is available to the test. + * after the test has finished. `page` fixture provides a [Page](https://playwright.dev/docs/api/class-page) object + * that is available to the test. * * Playwright Test comes with builtin fixtures listed below, and you can add your own fixtures as well. Playwright - * Test also [provides options]{@link TestOptions} to configure + * Test also [provides options][TestOptions](https://playwright.dev/docs/api/class-testoptions) to configure * [fixtures.browser](https://playwright.dev/docs/api/class-fixtures#fixtures-browser), * [fixtures.context](https://playwright.dev/docs/api/class-fixtures#fixtures-context) and * [fixtures.page](https://playwright.dev/docs/api/class-fixtures#fixtures-page). */ export interface PlaywrightTestArgs { /** - * Isolated {@link BrowserContext} instance, created for each test. Since contexts are isolated between each other, - * every test gets a fresh environment, even when multiple tests run in a single {@link Browser} for maximum - * efficiency. + * Isolated [BrowserContext](https://playwright.dev/docs/api/class-browsercontext) instance, created for each test. + * Since contexts are isolated between each other, every test gets a fresh environment, even when multiple tests run + * in a single [Browser](https://playwright.dev/docs/api/class-browser) for maximum efficiency. * - * Learn how to [configure context](https://playwright.dev/docs/test-configuration) and see [available options]{@link TestOptions}. + * Learn how to [configure context](https://playwright.dev/docs/test-configuration) and see + * [available options][TestOptions](https://playwright.dev/docs/api/class-testoptions). * * Default [fixtures.page](https://playwright.dev/docs/api/class-fixtures#fixtures-page) belongs to this context. * @@ -5802,8 +5850,8 @@ export interface PlaywrightTestArgs { */ context: BrowserContext; /** - * Isolated {@link Page} instance, created for each test. Pages are isolated between tests due to - * [fixtures.context](https://playwright.dev/docs/api/class-fixtures#fixtures-context) isolation. + * Isolated [Page](https://playwright.dev/docs/api/class-page) instance, created for each test. Pages are isolated + * between tests due to [fixtures.context](https://playwright.dev/docs/api/class-fixtures#fixtures-context) isolation. * * This is the most common fixture used in a test. * @@ -5824,7 +5872,7 @@ export interface PlaywrightTestArgs { */ page: Page; /** - * Isolated {@link APIRequestContext} instance for each test. + * Isolated [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext) instance for each test. * * **Usage** * @@ -5858,8 +5906,9 @@ type AsymmetricMatcher = Record<string, any>; interface AsymmetricMatchers { /** - * `expect.any()` matches any object instance created from the `constructor` or a corresponding primitive type. Use it - * inside + * `expect.any()` matches any object instance created from the + * [`constructor`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-any-option-constructor) + * or a corresponding primitive type. Use it inside * [expect(value).toEqual(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal) * to perform pattern matching. * @@ -6002,8 +6051,9 @@ interface AsymmetricMatchers { } /** - * The {@link GenericAssertions} class provides assertion methods that can be used to make assertions about any values - * in the tests. A new instance of {@link GenericAssertions} is created by calling + * The [GenericAssertions](https://playwright.dev/docs/api/class-genericassertions) class provides assertion methods + * that can be used to make assertions about any values in the tests. A new instance of + * [GenericAssertions](https://playwright.dev/docs/api/class-genericassertions) is created by calling * [expect(value)](https://playwright.dev/docs/api/class-playwrightassertions#playwright-assertions-expect-generic): * * ```js @@ -6028,8 +6078,10 @@ interface GenericAssertions<R> { */ not: GenericAssertions<R>; /** - * Compares value with `expected` by calling `Object.is`. This method compares objects by reference instead of their - * contents, similarly to the strict equality operator `===`. + * Compares value with + * [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-be-option-expected) by + * calling `Object.is`. This method compares objects by reference instead of their contents, similarly to the strict + * equality operator `===`. * * **Usage** * @@ -6254,7 +6306,9 @@ interface GenericAssertions<R> { */ toContainEqual(expected: unknown): R; /** - * Compares contents of the value with contents of `expected`, performing "deep equality" check. + * Compares contents of the value with contents of + * [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal-option-expected), + * performing "deep equality" check. * * For objects, this method recursively checks equality of all fields, rather than comparing objects by reference as * performed by @@ -6313,7 +6367,9 @@ interface GenericAssertions<R> { */ toEqual(expected: unknown): R; /** - * Ensures that value has a `.length` property equal to `expected`. Useful for arrays and strings. + * Ensures that value has a `.length` property equal to + * [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-have-length-option-expected). + * Useful for arrays and strings. * * **Usage** * @@ -6327,7 +6383,9 @@ interface GenericAssertions<R> { toHaveLength(expected: number): R; /** * Ensures that property at provided `keyPath` exists on the object and optionally checks that property is equal to - * the `expected`. Equality is checked recursively, similarly to + * the + * [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-have-property-option-expected). + * Equality is checked recursively, similarly to * [expect(value).toEqual(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal). * * **Usage** @@ -6365,8 +6423,9 @@ interface GenericAssertions<R> { */ toMatch(expected: RegExp | string): R; /** - * Compares contents of the value with contents of `expected`, performing "deep equality" check. Allows extra - * properties to be present in the value, unlike + * Compares contents of the value with contents of + * [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-match-object-option-expected), + * performing "deep equality" check. Allows extra properties to be present in the value, unlike * [expect(value).toEqual(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal), * so you can check just a subset of object properties. * @@ -6390,7 +6449,9 @@ interface GenericAssertions<R> { */ toMatchObject(expected: Record<string, unknown> | Array<unknown>): R; /** - * Compares contents of the value with contents of `expected` **and** their types. + * Compares contents of the value with contents of + * [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-strict-equal-option-expected) + * **and** their types. * * Differences from * [expect(value).toEqual(expected)](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-equal): @@ -6412,7 +6473,9 @@ interface GenericAssertions<R> { /** * Calls the function and ensures it throws an error. * - * Optionally compares the error with `expected`. Allowed expected values: + * Optionally compares the error with + * [`expected`](https://playwright.dev/docs/api/class-genericassertions#generic-assertions-to-throw-option-expected). + * Allowed expected values: * - Regular expression - error message should **match** the pattern. * - String - error message should **include** the substring. * - Error object - error message should be **equal to** the message property of the object. @@ -6637,8 +6700,9 @@ export { }; /** - * The {@link APIResponseAssertions} class provides assertion methods that can be used to make assertions about the - * {@link APIResponse} in the tests. + * The [APIResponseAssertions](https://playwright.dev/docs/api/class-apiresponseassertions) class provides assertion + * methods that can be used to make assertions about the + * [APIResponse](https://playwright.dev/docs/api/class-apiresponse) in the tests. * * ```js * import { test, expect } from '@playwright/test'; @@ -6677,8 +6741,9 @@ interface APIResponseAssertions { } /** - * The {@link LocatorAssertions} class provides assertion methods that can be used to make assertions about the {@link - * Locator} state in the tests. + * The [LocatorAssertions](https://playwright.dev/docs/api/class-locatorassertions) class provides assertion methods + * that can be used to make assertions about the [Locator](https://playwright.dev/docs/api/class-locator) state in the + * tests. * * ```js * import { test, expect } from '@playwright/test'; @@ -6693,7 +6758,7 @@ interface APIResponseAssertions { */ interface LocatorAssertions { /** - * Ensures that {@link Locator} points to an element that is + * Ensures that [Locator](https://playwright.dev/docs/api/class-locator) points to an element that is * [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot. * * **Usage** @@ -6714,7 +6779,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to a checked input. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to a checked input. * * **Usage** * @@ -6735,8 +6800,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to a disabled element. Element is disabled if it has "disabled" attribute or is - * disabled via + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to a disabled element. Element is + * disabled if it has "disabled" attribute or is disabled via * ['aria-disabled'](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled). Note * that only native control elements such as HTML `button`, `input`, `select`, `textarea`, `option`, `optgroup` can be * disabled by setting "disabled" attribute. "disabled" attribute on other elements is ignored by the browser. @@ -6758,7 +6823,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an editable element. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an editable element. * * **Usage** * @@ -6779,7 +6844,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an empty editable element or to a DOM node that has no text. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an empty editable element or to a + * DOM node that has no text. * * **Usage** * @@ -6798,7 +6864,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an enabled element. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an enabled element. * * **Usage** * @@ -6819,7 +6885,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to a focused DOM node. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to a focused DOM node. * * **Usage** * @@ -6838,8 +6904,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures that {@link Locator} either does not resolve to any DOM node, or resolves to a - * [non-visible](https://playwright.dev/docs/actionability#visible) one. + * Ensures that [Locator](https://playwright.dev/docs/api/class-locator) either does not resolve to any DOM node, or + * resolves to a [non-visible](https://playwright.dev/docs/actionability#visible) one. * * **Usage** * @@ -6858,7 +6924,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element that intersects viewport, according to the + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element that intersects viewport, + * according to the * [intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). * * **Usage** @@ -6889,7 +6956,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures that {@link Locator} points to an attached and [visible](https://playwright.dev/docs/actionability#visible) DOM node. + * Ensures that [Locator](https://playwright.dev/docs/api/class-locator) points to an attached and + * [visible](https://playwright.dev/docs/actionability#visible) DOM node. * * To check that at least one element from the list is visible, use * [locator.first()](https://playwright.dev/docs/api/class-locator#locator-first). @@ -6923,8 +6991,9 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element that contains the given text. All nested elements will be - * considered when computing the text content of the element. You can use regular expressions for the value as well. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element that contains the given + * text. All nested elements will be considered when computing the text content of the element. You can use regular + * expressions for the value as well. * * **Details** * @@ -6976,8 +7045,9 @@ interface LocatorAssertions { */ toContainText(expected: string|RegExp|ReadonlyArray<string|RegExp>, options?: { /** - * Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular - * expression flag if specified. + * Whether to perform case-insensitive match. + * [`ignoreCase`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-contain-text-option-ignore-case) + * option takes precedence over the corresponding regular expression flag if specified. */ ignoreCase?: boolean; @@ -6993,7 +7063,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with a given + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with a given * [accessible description](https://w3c.github.io/accname/#dfn-accessible-description). * * **Usage** @@ -7008,8 +7078,9 @@ interface LocatorAssertions { */ toHaveAccessibleDescription(description: string|RegExp, options?: { /** - * Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular - * expression flag if specified. + * Whether to perform case-insensitive match. + * [`ignoreCase`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-accessible-description-option-ignore-case) + * option takes precedence over the corresponding regular expression flag if specified. */ ignoreCase?: boolean; @@ -7020,7 +7091,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with a given + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with a given * [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). * * **Usage** @@ -7035,8 +7106,9 @@ interface LocatorAssertions { */ toHaveAccessibleName(name: string|RegExp, options?: { /** - * Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular - * expression flag if specified. + * Whether to perform case-insensitive match. + * [`ignoreCase`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-accessible-name-option-ignore-case) + * option takes precedence over the corresponding regular expression flag if specified. */ ignoreCase?: boolean; @@ -7047,7 +7119,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with given attribute. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with given attribute. * * **Usage** * @@ -7062,8 +7134,9 @@ interface LocatorAssertions { */ toHaveAttribute(name: string, value: string|RegExp, options?: { /** - * Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular - * expression flag if specified. + * Whether to perform case-insensitive match. + * [`ignoreCase`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-attribute-option-ignore-case) + * option takes precedence over the corresponding regular expression flag if specified. */ ignoreCase?: boolean; @@ -7074,7 +7147,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with given attribute. The method will assert attribute presence. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with given attribute. The + * method will assert attribute presence. * * ```js * const locator = page.locator('input'); @@ -7094,8 +7168,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with given CSS classes. This needs to be a full match or using a - * relaxed regular expression. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with given CSS classes. + * This needs to be a full match or using a relaxed regular expression. * * **Usage** * @@ -7127,7 +7201,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} resolves to an exact number of DOM nodes. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) resolves to an exact number of DOM nodes. * * **Usage** * @@ -7147,7 +7221,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} resolves to an element with the given computed CSS style. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) resolves to an element with the given computed + * CSS style. * * **Usage** * @@ -7168,7 +7243,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with the given DOM Node ID. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with the given DOM Node + * ID. * * **Usage** * @@ -7188,8 +7264,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with given JavaScript property. Note that this property can be of - * a primitive type as well as a plain serializable JavaScript object. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with given JavaScript + * property. Note that this property can be of a primitive type as well as a plain serializable JavaScript object. * * **Usage** * @@ -7210,7 +7286,7 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with a given + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with a given * [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles). * * Note that role is matched as a string, disregarding the ARIA role hierarchy. For example, asserting a superclass @@ -7267,7 +7343,9 @@ interface LocatorAssertions { /** * Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink - * box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. + * box `#FF00FF` (customized by + * [`maskColor`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-screenshot-1-option-mask-color)) + * that completely covers its bounding box. */ mask?: Array<Locator>; @@ -7357,7 +7435,9 @@ interface LocatorAssertions { /** * Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink - * box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. + * box `#FF00FF` (customized by + * [`maskColor`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-screenshot-2-option-mask-color)) + * that completely covers its bounding box. */ mask?: Array<Locator>; @@ -7415,8 +7495,9 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with the given text. All nested elements will be considered when - * computing the text content of the element. You can use regular expressions for the value as well. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with the given text. All + * nested elements will be considered when computing the text content of the element. You can use regular expressions + * for the value as well. * * **Details** * @@ -7467,8 +7548,9 @@ interface LocatorAssertions { */ toHaveText(expected: string|RegExp|ReadonlyArray<string|RegExp>, options?: { /** - * Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular - * expression flag if specified. + * Whether to perform case-insensitive match. + * [`ignoreCase`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-text-option-ignore-case) + * option takes precedence over the corresponding regular expression flag if specified. */ ignoreCase?: boolean; @@ -7484,8 +7566,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to an element with the given input value. You can use regular expressions for - * the value as well. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with the given input + * value. You can use regular expressions for the value as well. * * **Usage** * @@ -7505,8 +7587,8 @@ interface LocatorAssertions { }): Promise<void>; /** - * Ensures the {@link Locator} points to multi-select/combobox (i.e. a `select` with the `multiple` attribute) and the - * specified values are selected. + * Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to multi-select/combobox (i.e. a + * `select` with the `multiple` attribute) and the specified values are selected. * * **Usage** * @@ -7549,8 +7631,8 @@ interface LocatorAssertions { } /** - * The {@link PageAssertions} class provides assertion methods that can be used to make assertions about the {@link - * Page} state in the tests. + * The [PageAssertions](https://playwright.dev/docs/api/class-pageassertions) class provides assertion methods that + * can be used to make assertions about the [Page](https://playwright.dev/docs/api/class-page) state in the tests. * * ```js * import { test, expect } from '@playwright/test'; @@ -7628,8 +7710,9 @@ interface PageAssertions { */ toHaveURL(urlOrRegExp: string|RegExp, options?: { /** - * Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular - * expression flag if specified. + * Whether to perform case-insensitive match. + * [`ignoreCase`](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-url-option-ignore-case) + * option takes precedence over the corresponding regular expression flag if specified. */ ignoreCase?: boolean; @@ -7772,6 +7855,26 @@ interface SnapshotAssertions { }): void; } +/** + * Represents a location in the source code where [TestCase] or [Suite] is defined. + */ +export interface Location { + /** + * Column number in the source file. + */ + column: number; + + /** + * Path to the source file. + */ + file: string; + + /** + * Line number in the source file. + */ + line: number; +} + /** * `TestInfo` contains information about currently running test. It is available to test functions, * [test.beforeEach([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-before-each), @@ -7793,8 +7896,10 @@ interface SnapshotAssertions { */ export interface TestInfo { /** - * Attach a value or a file from disk to the current test. Some reporters show test attachments. Either `path` or - * `body` must be specified, but not both. + * Attach a value or a file from disk to the current test. Some reporters show test attachments. Either + * [`path`](https://playwright.dev/docs/api/class-testinfo#test-info-attach-option-path) or + * [`body`](https://playwright.dev/docs/api/class-testinfo#test-info-attach-option-body) must be specified, but not + * both. * * For example, you can attach a screenshot to the test: * @@ -7823,24 +7928,28 @@ export interface TestInfo { * **NOTE** [testInfo.attach(name[, options])](https://playwright.dev/docs/api/class-testinfo#test-info-attach) * automatically takes care of copying attached files to a location that is accessible to reporters. You can safely * remove the attachment after awaiting the attach call. + * * @param name Attachment name. The name will also be sanitized and used as the prefix of file name when saving to disk. * @param options */ attach(name: string, options?: { /** - * Attachment body. Mutually exclusive with `path`. + * Attachment body. Mutually exclusive with + * [`path`](https://playwright.dev/docs/api/class-testinfo#test-info-attach-option-path). */ body?: string|Buffer; /** * Content type of this attachment to properly present in the report, for example `'application/json'` or - * `'image/png'`. If omitted, content type is inferred based on the `path`, or defaults to `text/plain` for [string] - * attachments and `application/octet-stream` for [Buffer] attachments. + * `'image/png'`. If omitted, content type is inferred based on the + * [`path`](https://playwright.dev/docs/api/class-testinfo#test-info-attach-option-path), or defaults to `text/plain` + * for [string] attachments and `application/octet-stream` for [Buffer] attachments. */ contentType?: string; /** - * Path on the filesystem to the attached file. Mutually exclusive with `body`. + * Path on the filesystem to the attached file. Mutually exclusive with + * [`body`](https://playwright.dev/docs/api/class-testinfo#test-info-attach-option-body). */ path?: string; }): Promise<void>; @@ -8173,7 +8282,8 @@ export interface TestInfo { /** * Tags that apply to the test. Learn more about [tags](https://playwright.dev/docs/test-annotations#tag-tests). * - * Note that any changes made to this list while the test is running will not be visible to test reporters. + * **NOTE** Any changes made to this list while the test is running will not be visible to test reporters. + * */ tags: Array<string>; @@ -8242,7 +8352,8 @@ export interface TestInfoError { /** * `WorkerInfo` contains information about the worker that is running tests and is available to worker-scoped - * fixtures. `WorkerInfo` is a subset of {@link TestInfo} that is available in many other places. + * fixtures. `WorkerInfo` is a subset of [TestInfo](https://playwright.dev/docs/api/class-testinfo) that is available + * in many other places. */ export interface WorkerInfo { /** @@ -8325,7 +8436,9 @@ export interface PageAssertionsToHaveScreenshotOptions { /** * Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink - * box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. + * box `#FF00FF` (customized by + * [`maskColor`](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-1-option-mask-color)) + * that completely covers its bounding box. */ mask?: Array<Locator>; diff --git a/packages/playwright/types/testReporter.d.ts b/packages/playwright/types/testReporter.d.ts index 52073066f0..a9d1f020ae 100644 --- a/packages/playwright/types/testReporter.d.ts +++ b/packages/playwright/types/testReporter.d.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import type { TestStatus, Metadata, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription, FullConfig, FullProject } from './test'; -export type { FullConfig, FullProject, TestStatus } from './test'; +import type { TestStatus, Metadata, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription, FullConfig, FullProject, Location } from './test'; +export type { FullConfig, FullProject, TestStatus, Location } from './test'; /** * Result of the full test run. @@ -94,18 +94,20 @@ export interface FullResult { * * Here is a typical order of reporter calls: * - [reporter.onBegin(config, suite)](https://playwright.dev/docs/api/class-reporter#reporter-on-begin) is called - * once with a root suite that contains all other suites and tests. Learn more about [suites hierarchy]{@link - * Suite}. + * once with a root suite that contains all other suites and tests. Learn more about + * [suites hierarchy][Suite](https://playwright.dev/docs/api/class-suite). * - [reporter.onTestBegin(test, result)](https://playwright.dev/docs/api/class-reporter#reporter-on-test-begin) is - * called for each test run. It is given a {@link TestCase} that is executed, and a {@link TestResult} that is - * almost empty. Test result will be populated while the test runs (for example, with steps and stdio) and will - * get final `status` once the test finishes. + * called for each test run. It is given a [TestCase](https://playwright.dev/docs/api/class-testcase) that is + * executed, and a [TestResult](https://playwright.dev/docs/api/class-testresult) that is almost empty. Test + * result will be populated while the test runs (for example, with steps and stdio) and will get final `status` + * once the test finishes. * - [reporter.onStepBegin(test, result, step)](https://playwright.dev/docs/api/class-reporter#reporter-on-step-begin) * and * [reporter.onStepEnd(test, result, step)](https://playwright.dev/docs/api/class-reporter#reporter-on-step-end) * are called for each executed step inside the test. When steps are executed, test run has not finished yet. * - [reporter.onTestEnd(test, result)](https://playwright.dev/docs/api/class-reporter#reporter-on-test-end) is - * called when test run has finished. By this time, {@link TestResult} is complete and you can use + * called when test run has finished. By this time, [TestResult](https://playwright.dev/docs/api/class-testresult) + * is complete and you can use * [testResult.status](https://playwright.dev/docs/api/class-testresult#test-result-status), * [testResult.error](https://playwright.dev/docs/api/class-testresult#test-result-error) and more. * - [reporter.onEnd(result)](https://playwright.dev/docs/api/class-reporter#reporter-on-end) is called once after @@ -128,12 +130,13 @@ export interface FullResult { * **Merged report API notes** * * When merging multiple [`blob`](https://playwright.dev/docs/test-reporters#blob-reporter) reports via - * [`merge-reports`](https://playwright.dev/docs/test-sharding#merge-reports-cli) CLI command, the same {@link Reporter} API is called to - * produce final reports and all existing reporters should work without any changes. There some subtle differences - * though which might affect some custom reporters. - * - Projects from different shards are always kept as separate {@link TestProject} objects. E.g. if project - * 'Desktop Chrome' was sharded across 5 machines then there will be 5 instances of projects with the same name in - * the config passed to + * [`merge-reports`](https://playwright.dev/docs/test-sharding#merge-reports-cli) CLI command, the same + * [Reporter](https://playwright.dev/docs/api/class-reporter) API is called to produce final reports and all existing + * reporters should work without any changes. There some subtle differences though which might affect some custom + * reporters. + * - Projects from different shards are always kept as separate + * [TestProject](https://playwright.dev/docs/api/class-testproject) objects. E.g. if project 'Desktop Chrome' was + * sharded across 5 machines then there will be 5 instances of projects with the same name in the config passed to * [reporter.onBegin(config, suite)](https://playwright.dev/docs/api/class-reporter#reporter-on-begin). */ export interface Reporter { @@ -151,8 +154,8 @@ export interface Reporter { */ onEnd?(result: FullResult): Promise<{ status?: FullResult['status'] } | undefined | void> | void; /** - * Called once before running tests. All tests have been already discovered and put into a hierarchy of {@link - * Suite}s. + * Called once before running tests. All tests have been already discovered and put into a hierarchy of + * [Suite](https://playwright.dev/docs/api/class-suite)s. * @param config Resolved configuration. * @param suite The root suite that contains all projects, files and test cases. */ @@ -319,38 +322,18 @@ export type JSONReportSTDIOEntry = { text: string } | { buffer: string }; export {}; -/** - * Represents a location in the source code where {@link TestCase} or {@link Suite} is defined. - */ -export interface Location { - /** - * Column number in the source file. - */ - column: number; - - /** - * Path to the source file. - */ - file: string; - - /** - * Line number in the source file. - */ - line: number; -} - /** * `Suite` is a group of tests. All tests in Playwright Test form the following hierarchy: - * - Root suite has a child suite for each {@link FullProject}. + * - Root suite has a child suite for each [FullProject](https://playwright.dev/docs/api/class-fullproject). * - Project suite #1. Has a child suite for each test file in the project. * - File suite #1 - * - {@link TestCase} #1 - * - {@link TestCase} #2 + * - [TestCase](https://playwright.dev/docs/api/class-testcase) #1 + * - [TestCase](https://playwright.dev/docs/api/class-testcase) #2 * - Suite corresponding to a * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) * group - * - {@link TestCase} #1 in a group - * - {@link TestCase} #2 in a group + * - [TestCase](https://playwright.dev/docs/api/class-testcase) #1 in a group + * - [TestCase](https://playwright.dev/docs/api/class-testcase) #2 in a group * - < more test cases ... > * - File suite #2 * - < more file suites ... > @@ -396,7 +379,7 @@ export interface Suite { parent?: Suite; /** - * Child suites. See {@link Suite} for the hierarchy of suites. + * Child suites. See [Suite](https://playwright.dev/docs/api/class-suite) for the hierarchy of suites. */ suites: Array<Suite>; @@ -598,7 +581,7 @@ export interface TestError { } /** - * A result of a single {@link TestCase} run. + * A result of a single [TestCase](https://playwright.dev/docs/api/class-testcase) run. */ export interface TestResult { /** diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index 689f0275b1..a0d6a30b9f 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -40,6 +40,7 @@ export type InitializerTraits<T> = T extends BindingCallChannel ? BindingCallInitializer : T extends WebSocketChannel ? WebSocketInitializer : T extends ResponseChannel ? ResponseInitializer : + T extends WebSocketRouteChannel ? WebSocketRouteInitializer : T extends RouteChannel ? RouteInitializer : T extends RequestChannel ? RequestInitializer : T extends ElementHandleChannel ? ElementHandleInitializer : @@ -77,6 +78,7 @@ export type EventsTraits<T> = T extends BindingCallChannel ? BindingCallEvents : T extends WebSocketChannel ? WebSocketEvents : T extends ResponseChannel ? ResponseEvents : + T extends WebSocketRouteChannel ? WebSocketRouteEvents : T extends RouteChannel ? RouteEvents : T extends RequestChannel ? RequestEvents : T extends ElementHandleChannel ? ElementHandleEvents : @@ -114,6 +116,7 @@ export type EventTargetTraits<T> = T extends BindingCallChannel ? BindingCallEventTarget : T extends WebSocketChannel ? WebSocketEventTarget : T extends ResponseChannel ? ResponseEventTarget : + T extends WebSocketRouteChannel ? WebSocketRouteEventTarget : T extends RouteChannel ? RouteEventTarget : T extends RequestChannel ? RequestEventTarget : T extends ElementHandleChannel ? ElementHandleEventTarget : @@ -1493,6 +1496,7 @@ export interface BrowserContextEventTarget { on(event: 'page', callback: (params: BrowserContextPageEvent) => void): this; on(event: 'pageError', callback: (params: BrowserContextPageErrorEvent) => void): this; on(event: 'route', callback: (params: BrowserContextRouteEvent) => void): this; + on(event: 'webSocketRoute', callback: (params: BrowserContextWebSocketRouteEvent) => void): this; on(event: 'video', callback: (params: BrowserContextVideoEvent) => void): this; on(event: 'backgroundPage', callback: (params: BrowserContextBackgroundPageEvent) => void): this; on(event: 'serviceWorker', callback: (params: BrowserContextServiceWorkerEvent) => void): this; @@ -1518,10 +1522,11 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT setGeolocation(params: BrowserContextSetGeolocationParams, metadata?: CallMetadata): Promise<BrowserContextSetGeolocationResult>; setHTTPCredentials(params: BrowserContextSetHTTPCredentialsParams, metadata?: CallMetadata): Promise<BrowserContextSetHTTPCredentialsResult>; setNetworkInterceptionPatterns(params: BrowserContextSetNetworkInterceptionPatternsParams, metadata?: CallMetadata): Promise<BrowserContextSetNetworkInterceptionPatternsResult>; + setWebSocketInterceptionPatterns(params: BrowserContextSetWebSocketInterceptionPatternsParams, metadata?: CallMetadata): Promise<BrowserContextSetWebSocketInterceptionPatternsResult>; setOffline(params: BrowserContextSetOfflineParams, metadata?: CallMetadata): Promise<BrowserContextSetOfflineResult>; storageState(params?: BrowserContextStorageStateParams, metadata?: CallMetadata): Promise<BrowserContextStorageStateResult>; pause(params?: BrowserContextPauseParams, metadata?: CallMetadata): Promise<BrowserContextPauseResult>; - recorderSupplementEnable(params: BrowserContextRecorderSupplementEnableParams, metadata?: CallMetadata): Promise<BrowserContextRecorderSupplementEnableResult>; + enableRecorder(params: BrowserContextEnableRecorderParams, metadata?: CallMetadata): Promise<BrowserContextEnableRecorderResult>; newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: CallMetadata): Promise<BrowserContextNewCDPSessionResult>; harStart(params: BrowserContextHarStartParams, metadata?: CallMetadata): Promise<BrowserContextHarStartResult>; harExport(params: BrowserContextHarExportParams, metadata?: CallMetadata): Promise<BrowserContextHarExportResult>; @@ -1563,6 +1568,9 @@ export type BrowserContextPageErrorEvent = { export type BrowserContextRouteEvent = { route: RouteChannel, }; +export type BrowserContextWebSocketRouteEvent = { + webSocketRoute: WebSocketRouteChannel, +}; export type BrowserContextVideoEvent = { artifact: ArtifactChannel, }; @@ -1731,6 +1739,17 @@ export type BrowserContextSetNetworkInterceptionPatternsOptions = { }; export type BrowserContextSetNetworkInterceptionPatternsResult = void; +export type BrowserContextSetWebSocketInterceptionPatternsParams = { + patterns: { + glob?: string, + regexSource?: string, + regexFlags?: string, + }[], +}; +export type BrowserContextSetWebSocketInterceptionPatternsOptions = { + +}; +export type BrowserContextSetWebSocketInterceptionPatternsResult = void; export type BrowserContextSetOfflineParams = { offline: boolean, }; @@ -1747,9 +1766,10 @@ export type BrowserContextStorageStateResult = { export type BrowserContextPauseParams = {}; export type BrowserContextPauseOptions = {}; export type BrowserContextPauseResult = void; -export type BrowserContextRecorderSupplementEnableParams = { +export type BrowserContextEnableRecorderParams = { language?: string, mode?: 'inspecting' | 'recording', + codegenMode?: 'actions' | 'trace-events', pauseOnNextStatement?: boolean, testIdAttributeName?: string, launchOptions?: any, @@ -1759,9 +1779,10 @@ export type BrowserContextRecorderSupplementEnableParams = { outputFile?: string, omitCallTracking?: boolean, }; -export type BrowserContextRecorderSupplementEnableOptions = { +export type BrowserContextEnableRecorderOptions = { language?: string, mode?: 'inspecting' | 'recording', + codegenMode?: 'actions' | 'trace-events', pauseOnNextStatement?: boolean, testIdAttributeName?: string, launchOptions?: any, @@ -1771,7 +1792,7 @@ export type BrowserContextRecorderSupplementEnableOptions = { outputFile?: string, omitCallTracking?: boolean, }; -export type BrowserContextRecorderSupplementEnableResult = void; +export type BrowserContextEnableRecorderResult = void; export type BrowserContextNewCDPSessionParams = { page?: PageChannel, frame?: FrameChannel, @@ -1890,6 +1911,7 @@ export interface BrowserContextEvents { 'page': BrowserContextPageEvent; 'pageError': BrowserContextPageErrorEvent; 'route': BrowserContextRouteEvent; + 'webSocketRoute': BrowserContextWebSocketRouteEvent; 'video': BrowserContextVideoEvent; 'backgroundPage': BrowserContextBackgroundPageEvent; 'serviceWorker': BrowserContextServiceWorkerEvent; @@ -1919,6 +1941,7 @@ export interface PageEventTarget { on(event: 'frameDetached', callback: (params: PageFrameDetachedEvent) => void): this; on(event: 'locatorHandlerTriggered', callback: (params: PageLocatorHandlerTriggeredEvent) => void): this; on(event: 'route', callback: (params: PageRouteEvent) => void): this; + on(event: 'webSocketRoute', callback: (params: PageWebSocketRouteEvent) => void): this; on(event: 'video', callback: (params: PageVideoEvent) => void): this; on(event: 'webSocket', callback: (params: PageWebSocketEvent) => void): this; on(event: 'worker', callback: (params: PageWorkerEvent) => void): this; @@ -1933,7 +1956,7 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel { exposeBinding(params: PageExposeBindingParams, metadata?: CallMetadata): Promise<PageExposeBindingResult>; goBack(params: PageGoBackParams, metadata?: CallMetadata): Promise<PageGoBackResult>; goForward(params: PageGoForwardParams, metadata?: CallMetadata): Promise<PageGoForwardResult>; - forceGarbageCollection(params?: PageForceGarbageCollectionParams, metadata?: CallMetadata): Promise<PageForceGarbageCollectionResult>; + requestGC(params?: PageRequestGCParams, metadata?: CallMetadata): Promise<PageRequestGCResult>; registerLocatorHandler(params: PageRegisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageRegisterLocatorHandlerResult>; resolveLocatorHandlerNoReply(params: PageResolveLocatorHandlerNoReplyParams, metadata?: CallMetadata): Promise<PageResolveLocatorHandlerNoReplyResult>; unregisterLocatorHandler(params: PageUnregisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageUnregisterLocatorHandlerResult>; @@ -1942,6 +1965,7 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel { screenshot(params: PageScreenshotParams, metadata?: CallMetadata): Promise<PageScreenshotResult>; setExtraHTTPHeaders(params: PageSetExtraHTTPHeadersParams, metadata?: CallMetadata): Promise<PageSetExtraHTTPHeadersResult>; setNetworkInterceptionPatterns(params: PageSetNetworkInterceptionPatternsParams, metadata?: CallMetadata): Promise<PageSetNetworkInterceptionPatternsResult>; + setWebSocketInterceptionPatterns(params: PageSetWebSocketInterceptionPatternsParams, metadata?: CallMetadata): Promise<PageSetWebSocketInterceptionPatternsResult>; setViewportSize(params: PageSetViewportSizeParams, metadata?: CallMetadata): Promise<PageSetViewportSizeResult>; keyboardDown(params: PageKeyboardDownParams, metadata?: CallMetadata): Promise<PageKeyboardDownResult>; keyboardUp(params: PageKeyboardUpParams, metadata?: CallMetadata): Promise<PageKeyboardUpResult>; @@ -1989,6 +2013,9 @@ export type PageLocatorHandlerTriggeredEvent = { export type PageRouteEvent = { route: RouteChannel, }; +export type PageWebSocketRouteEvent = { + webSocketRoute: WebSocketRouteChannel, +}; export type PageVideoEvent = { artifact: ArtifactChannel, }; @@ -2071,9 +2098,9 @@ export type PageGoForwardOptions = { export type PageGoForwardResult = { response?: ResponseChannel, }; -export type PageForceGarbageCollectionParams = {}; -export type PageForceGarbageCollectionOptions = {}; -export type PageForceGarbageCollectionResult = void; +export type PageRequestGCParams = {}; +export type PageRequestGCOptions = {}; +export type PageRequestGCResult = void; export type PageRegisterLocatorHandlerParams = { selector: string, noWaitAfter?: boolean, @@ -2221,6 +2248,17 @@ export type PageSetNetworkInterceptionPatternsOptions = { }; export type PageSetNetworkInterceptionPatternsResult = void; +export type PageSetWebSocketInterceptionPatternsParams = { + patterns: { + glob?: string, + regexSource?: string, + regexFlags?: string, + }[], +}; +export type PageSetWebSocketInterceptionPatternsOptions = { + +}; +export type PageSetWebSocketInterceptionPatternsResult = void; export type PageSetViewportSizeParams = { viewportSize: { width: number, @@ -2448,6 +2486,7 @@ export interface PageEvents { 'frameDetached': PageFrameDetachedEvent; 'locatorHandlerTriggered': PageLocatorHandlerTriggeredEvent; 'route': PageRouteEvent; + 'webSocketRoute': PageWebSocketRouteEvent; 'video': PageVideoEvent; 'webSocket': PageWebSocketEvent; 'worker': PageWorkerEvent; @@ -3187,7 +3226,6 @@ export interface JSHandleChannel extends JSHandleEventTarget, Channel { getPropertyList(params?: JSHandleGetPropertyListParams, metadata?: CallMetadata): Promise<JSHandleGetPropertyListResult>; getProperty(params: JSHandleGetPropertyParams, metadata?: CallMetadata): Promise<JSHandleGetPropertyResult>; jsonValue(params?: JSHandleJsonValueParams, metadata?: CallMetadata): Promise<JSHandleJsonValueResult>; - objectCount(params?: JSHandleObjectCountParams, metadata?: CallMetadata): Promise<JSHandleObjectCountResult>; } export type JSHandlePreviewUpdatedEvent = { preview: string, @@ -3239,11 +3277,6 @@ export type JSHandleJsonValueOptions = {}; export type JSHandleJsonValueResult = { value: SerializedValue, }; -export type JSHandleObjectCountParams = {}; -export type JSHandleObjectCountOptions = {}; -export type JSHandleObjectCountResult = { - count: number, -}; export interface JSHandleEvents { 'previewUpdated': JSHandlePreviewUpdatedEvent; @@ -3732,7 +3765,6 @@ export type RouteRedirectNavigationRequestOptions = { export type RouteRedirectNavigationRequestResult = void; export type RouteAbortParams = { errorCode?: string, - requestUrl: string, }; export type RouteAbortOptions = { errorCode?: string, @@ -3743,7 +3775,6 @@ export type RouteContinueParams = { method?: string, headers?: NameValue[], postData?: Binary, - requestUrl: string, isFallback: boolean, }; export type RouteContinueOptions = { @@ -3759,7 +3790,6 @@ export type RouteFulfillParams = { body?: string, isBase64?: boolean, fetchResponseUid?: string, - requestUrl: string, }; export type RouteFulfillOptions = { status?: number, @@ -3773,6 +3803,93 @@ export type RouteFulfillResult = void; export interface RouteEvents { } +// ----------- WebSocketRoute ----------- +export type WebSocketRouteInitializer = { + url: string, +}; +export interface WebSocketRouteEventTarget { + on(event: 'messageFromPage', callback: (params: WebSocketRouteMessageFromPageEvent) => void): this; + on(event: 'messageFromServer', callback: (params: WebSocketRouteMessageFromServerEvent) => void): this; + on(event: 'closePage', callback: (params: WebSocketRouteClosePageEvent) => void): this; + on(event: 'closeServer', callback: (params: WebSocketRouteCloseServerEvent) => void): this; +} +export interface WebSocketRouteChannel extends WebSocketRouteEventTarget, Channel { + _type_WebSocketRoute: boolean; + connect(params?: WebSocketRouteConnectParams, metadata?: CallMetadata): Promise<WebSocketRouteConnectResult>; + ensureOpened(params?: WebSocketRouteEnsureOpenedParams, metadata?: CallMetadata): Promise<WebSocketRouteEnsureOpenedResult>; + sendToPage(params: WebSocketRouteSendToPageParams, metadata?: CallMetadata): Promise<WebSocketRouteSendToPageResult>; + sendToServer(params: WebSocketRouteSendToServerParams, metadata?: CallMetadata): Promise<WebSocketRouteSendToServerResult>; + closePage(params: WebSocketRouteClosePageParams, metadata?: CallMetadata): Promise<WebSocketRouteClosePageResult>; + closeServer(params: WebSocketRouteCloseServerParams, metadata?: CallMetadata): Promise<WebSocketRouteCloseServerResult>; +} +export type WebSocketRouteMessageFromPageEvent = { + message: string, + isBase64: boolean, +}; +export type WebSocketRouteMessageFromServerEvent = { + message: string, + isBase64: boolean, +}; +export type WebSocketRouteClosePageEvent = { + code?: number, + reason?: string, + wasClean: boolean, +}; +export type WebSocketRouteCloseServerEvent = { + code?: number, + reason?: string, + wasClean: boolean, +}; +export type WebSocketRouteConnectParams = {}; +export type WebSocketRouteConnectOptions = {}; +export type WebSocketRouteConnectResult = void; +export type WebSocketRouteEnsureOpenedParams = {}; +export type WebSocketRouteEnsureOpenedOptions = {}; +export type WebSocketRouteEnsureOpenedResult = void; +export type WebSocketRouteSendToPageParams = { + message: string, + isBase64: boolean, +}; +export type WebSocketRouteSendToPageOptions = { + +}; +export type WebSocketRouteSendToPageResult = void; +export type WebSocketRouteSendToServerParams = { + message: string, + isBase64: boolean, +}; +export type WebSocketRouteSendToServerOptions = { + +}; +export type WebSocketRouteSendToServerResult = void; +export type WebSocketRouteClosePageParams = { + code?: number, + reason?: string, + wasClean: boolean, +}; +export type WebSocketRouteClosePageOptions = { + code?: number, + reason?: string, +}; +export type WebSocketRouteClosePageResult = void; +export type WebSocketRouteCloseServerParams = { + code?: number, + reason?: string, + wasClean: boolean, +}; +export type WebSocketRouteCloseServerOptions = { + code?: number, + reason?: string, +}; +export type WebSocketRouteCloseServerResult = void; + +export interface WebSocketRouteEvents { + 'messageFromPage': WebSocketRouteMessageFromPageEvent; + 'messageFromServer': WebSocketRouteMessageFromServerEvent; + 'closePage': WebSocketRouteClosePageEvent; + 'closeServer': WebSocketRouteCloseServerEvent; +} + export type ResourceTiming = { startTime: number, domainLookupStart: number, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index ce206ab569..a32ebde3d4 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1160,6 +1160,17 @@ BrowserContext: regexSource: string? regexFlags: string? + setWebSocketInterceptionPatterns: + parameters: + patterns: + type: array + items: + type: object + properties: + glob: string? + regexSource: string? + regexFlags: string? + setOffline: parameters: offline: boolean @@ -1176,7 +1187,7 @@ BrowserContext: pause: experimental: True - recorderSupplementEnable: + enableRecorder: experimental: True parameters: language: string? @@ -1185,6 +1196,11 @@ BrowserContext: literals: - inspecting - recording + codegenMode: + type: enum? + literals: + - actions + - trace-events pauseOnNextStatement: boolean? testIdAttributeName: string? launchOptions: json? @@ -1305,6 +1321,10 @@ BrowserContext: parameters: route: Route + webSocketRoute: + parameters: + webSocketRoute: WebSocketRoute + video: parameters: artifact: Artifact @@ -1430,7 +1450,7 @@ Page: slowMo: true snapshot: true - forceGarbageCollection: + requestGC: registerLocatorHandler: parameters: @@ -1520,6 +1540,17 @@ Page: regexSource: string? regexFlags: string? + setWebSocketInterceptionPatterns: + parameters: + patterns: + type: array + items: + type: object + properties: + glob: string? + regexSource: string? + regexFlags: string? + setViewportSize: parameters: viewportSize: @@ -1771,6 +1802,10 @@ Page: parameters: route: Route + webSocketRoute: + parameters: + webSocketRoute: WebSocketRoute + video: parameters: artifact: Artifact @@ -2453,10 +2488,6 @@ JSHandle: returns: value: SerializedValue - objectCount: - returns: - count: number - events: previewUpdated: @@ -2911,7 +2942,6 @@ Route: abort: parameters: errorCode: string? - requestUrl: string continue: parameters: @@ -2921,7 +2951,6 @@ Route: type: array? items: NameValue postData: binary? - requestUrl: string isFallback: boolean fulfill: @@ -2934,7 +2963,66 @@ Route: body: string? isBase64: boolean? fetchResponseUid: string? - requestUrl: string + + +WebSocketRoute: + type: interface + + initializer: + url: string + + commands: + + connect: + + ensureOpened: + + sendToPage: + parameters: + message: string + isBase64: boolean + + sendToServer: + parameters: + message: string + isBase64: boolean + + closePage: + parameters: + code: number? + reason: string? + wasClean: boolean + + closeServer: + parameters: + code: number? + reason: string? + wasClean: boolean + + events: + + messageFromPage: + parameters: + message: string + isBase64: boolean + + messageFromServer: + parameters: + message: string + isBase64: boolean + + closePage: + parameters: + code: number? + reason: string? + wasClean: boolean + + closeServer: + parameters: + code: number? + reason: string? + wasClean: boolean + ResourceTiming: type: object diff --git a/packages/playwright-core/src/server/recorder/recorderActions.ts b/packages/recorder/src/actions.ts similarity index 92% rename from packages/playwright-core/src/server/recorder/recorderActions.ts rename to packages/recorder/src/actions.ts index 9447f32457..a17e0c172b 100644 --- a/packages/playwright-core/src/server/recorder/recorderActions.ts +++ b/packages/recorder/src/actions.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Point } from '../../common/types'; +type Point = { x: number, y: number }; export type ActionName = 'check' | @@ -143,3 +143,16 @@ export type DialogSignal = BaseSignal & { }; export type Signal = NavigationSignal | PopupSignal | DownloadSignal | DialogSignal; + +export type FrameDescription = { + pageAlias: string; + framePath: string[]; +}; + +export type ActionInContext = { + frame: FrameDescription; + description?: string; + action: Action; + startTime: number; + endTime?: number; +}; diff --git a/packages/recorder/src/main.tsx b/packages/recorder/src/main.tsx index 2f7ea3ac4c..61ac9da67f 100644 --- a/packages/recorder/src/main.tsx +++ b/packages/recorder/src/main.tsx @@ -27,7 +27,10 @@ export const Main: React.FC = ({ const [mode, setMode] = React.useState<Mode>('none'); window.playwrightSetMode = setMode; - window.playwrightSetSources = setSources; + window.playwrightSetSources = React.useCallback((sources: Source[]) => { + setSources(sources); + window.playwrightSourcesEchoForTest = sources; + }, []); window.playwrightSetPaused = setPaused; window.playwrightUpdateLogs = callLogs => { setLog(log => { @@ -40,6 +43,5 @@ export const Main: React.FC = ({ }); }; - window.playwrightSourcesEchoForTest = sources; return <Recorder sources={sources} paused={paused} log={log} mode={mode} />; }; diff --git a/packages/recorder/src/recorder.tsx b/packages/recorder/src/recorder.tsx index 31bad2b70b..9d5c0feeba 100644 --- a/packages/recorder/src/recorder.tsx +++ b/packages/recorder/src/recorder.tsx @@ -19,6 +19,7 @@ import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper'; import { SplitView } from '@web/components/splitView'; import { TabbedPane } from '@web/components/tabbedPane'; import { Toolbar } from '@web/components/toolbar'; +import { emptySource, SourceChooser } from '@web/components/sourceChooser'; import { ToolbarButton, ToolbarSeparator } from '@web/components/toolbarButton'; import * as React from 'react'; import { CallLogView } from './callLog'; @@ -54,15 +55,7 @@ export const Recorder: React.FC<RecorderProps> = ({ if (source) return source; } - const source: Source = { - id: 'default', - isRecorded: false, - text: '', - language: 'javascript', - label: '', - highlight: [] - }; - return source; + return emptySource(); }, [sources, fileId]); const [locator, setLocator] = React.useState(''); @@ -152,10 +145,10 @@ export const Recorder: React.FC<RecorderProps> = ({ }}></ToolbarButton> <div style={{ flex: 'auto' }}></div> <div>Target:</div> - <select className='recorder-chooser' hidden={!sources.length} value={fileId} onChange={event => { - setFileId(event.target.selectedOptions[0].value); - window.dispatch({ event: 'fileChanged', params: { file: event.target.selectedOptions[0].value } }); - }}>{renderSourceOptions(sources)}</select> + <SourceChooser fileId={fileId} sources={sources} setFileId={fileId => { + setFileId(fileId); + window.dispatch({ event: 'fileChanged', params: { file: fileId } }); + }} /> <ToolbarButton icon='clear-all' title='Clear' disabled={!source || !source.text} onClick={() => { window.dispatch({ event: 'clear' }); }}></ToolbarButton> @@ -184,22 +177,3 @@ export const Recorder: React.FC<RecorderProps> = ({ /> </div>; }; - -function renderSourceOptions(sources: Source[]): React.ReactNode { - const transformTitle = (title: string): string => title.replace(/.*[/\\]([^/\\]+)/, '$1'); - const renderOption = (source: Source): React.ReactNode => ( - <option key={source.id} value={source.id}>{transformTitle(source.label)}</option> - ); - - const hasGroup = sources.some(s => s.group); - if (hasGroup) { - const groups = new Set(sources.map(s => s.group)); - return [...groups].filter(Boolean).map(group => ( - <optgroup label={group} key={group}> - {sources.filter(s => s.group === group).map(source => renderOption(source))} - </optgroup> - )); - } - - return sources.map(source => renderOption(source)); -} diff --git a/packages/trace-viewer/src/DEPS.list b/packages/trace-viewer/src/DEPS.list index f2614cff83..3d486b5452 100644 --- a/packages/trace-viewer/src/DEPS.list +++ b/packages/trace-viewer/src/DEPS.list @@ -3,3 +3,10 @@ @trace/** @web/** ui/ + +[sw-main.ts] +sw/** + + +[recorder.tsx] +ui/recorder/** diff --git a/packages/trace-viewer/src/multimap.ts b/packages/trace-viewer/src/multimap.ts deleted file mode 100644 index 18777d29e5..0000000000 --- a/packages/trace-viewer/src/multimap.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * 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. - */ - -export class MultiMap<K, V> { - private _map: Map<K, V[]>; - - constructor() { - this._map = new Map<K, V[]>(); - } - - set(key: K, value: V) { - let values = this._map.get(key); - if (!values) { - values = []; - this._map.set(key, values); - } - values.push(value); - } - - get(key: K): V[] { - return this._map.get(key) || []; - } - - has(key: K): boolean { - return this._map.has(key); - } - - delete(key: K, value: V) { - const values = this._map.get(key); - if (!values) - return; - if (values.includes(value)) - this._map.set(key, values.filter(v => value !== v)); - } - - deleteAll(key: K) { - this._map.delete(key); - } - - hasValue(key: K, value: V): boolean { - const values = this._map.get(key); - if (!values) - return false; - return values.includes(value); - } - - get size(): number { - return this._map.size; - } - - [Symbol.iterator](): Iterator<[K, V[]]> { - return this._map[Symbol.iterator](); - } - - keys(): IterableIterator<K> { - return this._map.keys(); - } - - values(): Iterable<V> { - const result: V[] = []; - for (const key of this.keys()) - result.push(...this.get(key)); - return result; - } - - clear() { - this._map.clear(); - } -} diff --git a/packages/trace-viewer/src/recorder.tsx b/packages/trace-viewer/src/recorder.tsx index 4de705d4fc..5e6b9764e3 100644 --- a/packages/trace-viewer/src/recorder.tsx +++ b/packages/trace-viewer/src/recorder.tsx @@ -18,7 +18,7 @@ import '@web/common.css'; import { applyTheme } from '@web/theme'; import '@web/third_party/vscode/codicon.css'; import * as ReactDOM from 'react-dom/client'; -import { RecorderView } from './ui/recorderView'; +import { RecorderView } from './ui/recorder/recorderView'; (async () => { applyTheme(); diff --git a/tests/page/page-object-count.spec.ts b/packages/trace-viewer/src/sw-main.ts similarity index 55% rename from tests/page/page-object-count.spec.ts rename to packages/trace-viewer/src/sw-main.ts index 57377b76e7..5d11452851 100644 --- a/tests/page/page-object-count.spec.ts +++ b/packages/trace-viewer/src/sw-main.ts @@ -5,7 +5,7 @@ * 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 + * 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, @@ -14,12 +14,4 @@ * limitations under the License. */ -import { test, expect } from './pageTest'; - -test('should count objects', async ({ page, browserName }) => { - test.skip(browserName !== 'chromium'); - await page.setContent('<button>Submit</button>'); - await page.evaluate(() => document.querySelectorAll('button')); - const proto = await page.evaluateHandle(() => HTMLButtonElement.prototype); - expect(await (proto as any)._objectCount()).toBe(1); -}); +import './sw/main'; diff --git a/packages/trace-viewer/src/sw/DEPS.list b/packages/trace-viewer/src/sw/DEPS.list new file mode 100644 index 0000000000..047840f509 --- /dev/null +++ b/packages/trace-viewer/src/sw/DEPS.list @@ -0,0 +1,2 @@ +[*] +@isomorphic/** diff --git a/packages/trace-viewer/src/sw.ts b/packages/trace-viewer/src/sw/main.ts similarity index 100% rename from packages/trace-viewer/src/sw.ts rename to packages/trace-viewer/src/sw/main.ts diff --git a/packages/trace-viewer/src/progress.ts b/packages/trace-viewer/src/sw/progress.ts similarity index 100% rename from packages/trace-viewer/src/progress.ts rename to packages/trace-viewer/src/sw/progress.ts diff --git a/packages/trace-viewer/src/snapshotRenderer.ts b/packages/trace-viewer/src/sw/snapshotRenderer.ts similarity index 100% rename from packages/trace-viewer/src/snapshotRenderer.ts rename to packages/trace-viewer/src/sw/snapshotRenderer.ts diff --git a/packages/trace-viewer/src/snapshotServer.ts b/packages/trace-viewer/src/sw/snapshotServer.ts similarity index 100% rename from packages/trace-viewer/src/snapshotServer.ts rename to packages/trace-viewer/src/sw/snapshotServer.ts diff --git a/packages/trace-viewer/src/snapshotStorage.ts b/packages/trace-viewer/src/sw/snapshotStorage.ts similarity index 100% rename from packages/trace-viewer/src/snapshotStorage.ts rename to packages/trace-viewer/src/sw/snapshotStorage.ts diff --git a/packages/trace-viewer/src/traceModel.ts b/packages/trace-viewer/src/sw/traceModel.ts similarity index 89% rename from packages/trace-viewer/src/traceModel.ts rename to packages/trace-viewer/src/sw/traceModel.ts index 1248dde967..602ff4e075 100644 --- a/packages/trace-viewer/src/traceModel.ts +++ b/packages/trace-viewer/src/sw/traceModel.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { parseClientSideCallMetadata } from '../../../packages/playwright-core/src/utils/isomorphic/traceUtils'; -import type { ContextEntry } from './entries'; -import { createEmptyContext } from './entries'; +import { parseClientSideCallMetadata } from '@isomorphic/traceUtils'; +import type { ContextEntry } from '../types/entries'; import { SnapshotStorage } from './snapshotStorage'; import { TraceModernizer } from './traceModernizer'; @@ -73,6 +72,7 @@ export class TraceModel { unzipProgress(++done, total); contextEntry.actions = modernizer.actions().sort((a1, a2) => a1.startTime - a2.startTime); + if (!backend.isLive()) { // Terminate actions w/o after event gracefully. // This would close after hooks event that has not been closed because @@ -132,3 +132,26 @@ function stripEncodingFromContentType(contentType: string) { return charset[1]; return contentType; } + +function createEmptyContext(): ContextEntry { + return { + origin: 'testRunner', + traceUrl: '', + startTime: Number.MAX_SAFE_INTEGER, + wallTime: Number.MAX_SAFE_INTEGER, + endTime: 0, + browserName: '', + options: { + deviceScaleFactor: 1, + isMobile: false, + viewport: { width: 1280, height: 800 }, + }, + pages: [], + resources: [], + actions: [], + events: [], + errors: [], + stdio: [], + hasSource: false, + }; +} diff --git a/packages/trace-viewer/src/traceModelBackends.ts b/packages/trace-viewer/src/sw/traceModelBackends.ts similarity index 100% rename from packages/trace-viewer/src/traceModelBackends.ts rename to packages/trace-viewer/src/sw/traceModelBackends.ts diff --git a/packages/trace-viewer/src/traceModernizer.ts b/packages/trace-viewer/src/sw/traceModernizer.ts similarity index 99% rename from packages/trace-viewer/src/traceModernizer.ts rename to packages/trace-viewer/src/sw/traceModernizer.ts index 7c6ff19ce6..69d7f965dc 100644 --- a/packages/trace-viewer/src/traceModernizer.ts +++ b/packages/trace-viewer/src/sw/traceModernizer.ts @@ -19,7 +19,7 @@ import type * as traceV3 from './versions/traceV3'; import type * as traceV4 from './versions/traceV4'; import type * as traceV5 from './versions/traceV5'; import type * as traceV6 from './versions/traceV6'; -import type { ActionEntry, ContextEntry, PageEntry } from './entries'; +import type { ActionEntry, ContextEntry, PageEntry } from '../types/entries'; import type { SnapshotStorage } from './snapshotStorage'; export class TraceVersionError extends Error { diff --git a/packages/trace-viewer/src/versions/traceV3.ts b/packages/trace-viewer/src/sw/versions/traceV3.ts similarity index 97% rename from packages/trace-viewer/src/versions/traceV3.ts rename to packages/trace-viewer/src/sw/versions/traceV3.ts index fea10264a7..a9cb7d7704 100644 --- a/packages/trace-viewer/src/versions/traceV3.ts +++ b/packages/trace-viewer/src/sw/versions/traceV3.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Entry as ResourceSnapshot } from '../../../trace/src/har'; +import type { Entry as ResourceSnapshot } from '@trace/har'; type SerializedValue = { n?: number, diff --git a/packages/trace-viewer/src/versions/traceV4.ts b/packages/trace-viewer/src/sw/versions/traceV4.ts similarity index 98% rename from packages/trace-viewer/src/versions/traceV4.ts rename to packages/trace-viewer/src/sw/versions/traceV4.ts index 982bea1492..722604f0ad 100644 --- a/packages/trace-viewer/src/versions/traceV4.ts +++ b/packages/trace-viewer/src/sw/versions/traceV4.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Entry as ResourceSnapshot } from '../../../trace/src/har'; +import type { Entry as ResourceSnapshot } from '@trace/har'; type Language = 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl'; type Point = { x: number, y: number }; diff --git a/packages/trace-viewer/src/versions/traceV5.ts b/packages/trace-viewer/src/sw/versions/traceV5.ts similarity index 98% rename from packages/trace-viewer/src/versions/traceV5.ts rename to packages/trace-viewer/src/sw/versions/traceV5.ts index 8024330ebd..54d54459ce 100644 --- a/packages/trace-viewer/src/versions/traceV5.ts +++ b/packages/trace-viewer/src/sw/versions/traceV5.ts @@ -15,7 +15,7 @@ */ -import type { Entry as ResourceSnapshot } from '../../../trace/src/har'; +import type { Entry as ResourceSnapshot } from '@trace/har'; type Language = 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl'; type Point = { x: number, y: number }; diff --git a/packages/trace-viewer/src/versions/traceV6.ts b/packages/trace-viewer/src/sw/versions/traceV6.ts similarity index 98% rename from packages/trace-viewer/src/versions/traceV6.ts rename to packages/trace-viewer/src/sw/versions/traceV6.ts index 2bbbd447b3..1b069a20b2 100644 --- a/packages/trace-viewer/src/versions/traceV6.ts +++ b/packages/trace-viewer/src/sw/versions/traceV6.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Entry as ResourceSnapshot } from '../../../trace/src/har'; +import type { Entry as ResourceSnapshot } from '@trace/har'; type Language = 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl'; type Point = { x: number, y: number }; diff --git a/packages/trace-viewer/src/third_party/devtools.ts b/packages/trace-viewer/src/third_party/devtools.ts new file mode 100644 index 0000000000..27c520cbce --- /dev/null +++ b/packages/trace-viewer/src/third_party/devtools.ts @@ -0,0 +1,285 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Modifications copyright (c) Microsoft Corporation. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org> + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import type { Entry } from '@trace/har'; + +// The following function is derived from Chromium's source code +// https://github.com/ChromeDevTools/devtools-frontend/blob/83cbe41b4107e188a1f66fdf6ea3a9cca42587c6/front_end/panels/network/NetworkLogView.ts#L2363 +export async function generateCurlCommand(resource: Entry): Promise<string> { + const platform = navigator.platform.includes('Win') ? 'win' : 'unix'; + let command: string[] = []; + // Most of these headers are derived from the URL and are automatically added by cURL. + // The |Accept-Encoding| header is ignored to prevent decompression errors. crbug.com/1015321 + const ignoredHeaders = + new Set<string>(['accept-encoding', 'host', 'method', 'path', 'scheme', 'version', 'authority', 'protocol']); + + function escapeStringWin(str: string): string { + /* Always escape the " characters so that we can use caret escaping. + + Because cmd.exe parser and MS Crt arguments parsers use some of the + same escape characters, they can interact with each other in + horrible ways, the order of operations is critical. + + Replace \ with \\ first because it is an escape character for certain + conditions in both parsers. + + Replace all " with \" to ensure the first parser does not remove it. + + Then escape all characters we are not sure about with ^ to ensure it + gets to MS Crt parser safely. + + The % character is special because MS Crt parser will try and look for + ENV variables and fill them in its place. We cannot escape them with % + and cannot escape them with ^ (because it's cmd.exe's escape not MS Crt + parser); So we can get cmd.exe parser to escape the character after it, + if it is followed by a valid beginning character of an ENV variable. + This ensures we do not try and double escape another ^ if it was placed + by the previous replace. + + Lastly we replace new lines with ^ and TWO new lines because the first + new line is there to enact the escape command the second is the character + to escape (in this case new line). + */ + const encapsChars = '^"'; + return encapsChars + + str.replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/[^a-zA-Z0-9\s_\-:=+~'\/.',?;()*`]/g, '^$&') + .replace(/%(?=[a-zA-Z0-9_])/g, '%^') + .replace(/\r?\n/g, '^\n\n') + + encapsChars; + } + + function escapeStringPosix(str: string): string { + function escapeCharacter(x: string): string { + const code = x.charCodeAt(0); + let hexString = code.toString(16); + // Zero pad to four digits to comply with ANSI-C Quoting: + // http://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html + while (hexString.length < 4) + hexString = '0' + hexString; + + + return '\\u' + hexString; + } + + if (/[\0-\x1F\x7F-\x9F!]|\'/.test(str)) { + // Use ANSI-C quoting syntax. + return '$\'' + + str.replace(/\\/g, '\\\\') + .replace(/\'/g, '\\\'') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/[\0-\x1F\x7F-\x9F!]/g, escapeCharacter) + + '\''; + } + // Use single quote syntax. + return '\'' + str + '\''; + } + + // cURL command expected to run on the same platform that DevTools run + // (it may be different from the inspected page platform). + const escapeString = platform === 'win' ? escapeStringWin : escapeStringPosix; + + command.push(escapeString(resource.request.url).replace(/[[{}\]]/g, '\\$&')); + + let inferredMethod = 'GET'; + const data = []; + const formData = await fetchRequestPostData(resource); + if (formData) { + // Note that formData is not necessarily urlencoded because it might for example + // come from a fetch request made with an explicitly unencoded body. + data.push('--data-raw ' + escapeString(formData)); + ignoredHeaders.add('content-length'); + inferredMethod = 'POST'; + } + + if (resource.request.method !== inferredMethod) + command.push('-X ' + escapeString(resource.request.method)); + + + const requestHeaders = resource.request.headers; + for (let i = 0; i < requestHeaders.length; i++) { + const header = requestHeaders[i]; + const name = header.name.replace(/^:/, ''); // Translate SPDY v3 headers to HTTP headers. + if (ignoredHeaders.has(name.toLowerCase())) + continue; + + if (header.value.trim()) { + command.push('-H ' + escapeString(name + ': ' + header.value)); + } else { + // A header passed with -H with no value or only whitespace as its + // value tells curl to not set the header at all. To post an empty + // header, you have to terminate it with a semicolon. + command.push('-H ' + escapeString(name + ';')); + } + } + command = command.concat(data); + + return 'curl ' + command.join(command.length >= 3 ? (platform === 'win' ? ' ^\n ' : ' \\\n ') : ' '); +} + +const enum FetchStyle { + BROWSER = 0, + NODE_JS = 1, +} + +export async function generateFetchCall(resource: Entry, style: FetchStyle = FetchStyle.BROWSER): Promise<string> { + const ignoredHeaders = new Set<string>([ + // Internal headers + 'method', + 'path', + 'scheme', + 'version', + + // Unsafe headers + // Keep this list synchronized with src/net/http/http_util.cc + 'accept-charset', + 'accept-encoding', + 'access-control-request-headers', + 'access-control-request-method', + 'connection', + 'content-length', + 'cookie', + 'cookie2', + 'date', + 'dnt', + 'expect', + 'host', + 'keep-alive', + 'origin', + 'referer', + 'te', + 'trailer', + 'transfer-encoding', + 'upgrade', + 'via', + // TODO(phistuck) - remove this once crbug.com/571722 is fixed. + 'user-agent', + ]); + + const credentialHeaders = new Set<string>(['cookie', 'authorization']); + + const url = JSON.stringify(resource.request.url); + + const requestHeaders = resource.request.headers; + const headerData: Headers = requestHeaders.reduce((result, header) => { + const name = header.name; + + if (!ignoredHeaders.has(name.toLowerCase()) && !name.includes(':')) + result.append(name, header.value); + + + return result; + }, new Headers()); + + const headers: HeadersInit = {}; + for (const headerArray of headerData) + headers[headerArray[0]] = headerArray[1]; + + + const credentials = resource.request.cookies.length || + requestHeaders.some(({ name }) => credentialHeaders.has(name.toLowerCase())) ? + 'include' : + 'omit'; + + const referrerHeader = requestHeaders.find(({ name }) => name.toLowerCase() === 'referer'); + + const referrer = referrerHeader ? referrerHeader.value : void 0; + + const requestBody = await fetchRequestPostData(resource); + + const fetchOptions: RequestInit = { + headers: Object.keys(headers).length ? headers : void 0, + referrer, + body: requestBody, + method: resource.request.method, + mode: 'cors', + }; + + if (style === FetchStyle.NODE_JS) { + const cookieHeader = requestHeaders.find(header => header.name.toLowerCase() === 'cookie'); + const extraHeaders: HeadersInit = {}; + // According to https://www.npmjs.com/package/node-fetch#class-request the + // following properties are not implemented in Node.js. + delete fetchOptions.mode; + if (cookieHeader) + extraHeaders['cookie'] = cookieHeader.value; + + if (referrer) { + delete fetchOptions.referrer; + extraHeaders['Referer'] = referrer; + } + if (Object.keys(extraHeaders).length) { + fetchOptions.headers = { + ...headers, + ...extraHeaders, + }; + } + } else { + fetchOptions.credentials = credentials; + } + + const options = JSON.stringify(fetchOptions, null, 2); + return `fetch(${url}, ${options});`; +} + +async function fetchRequestPostData(resource: Entry) { + return resource.request.postData?._sha1 ? await fetch(`sha1/${resource.request.postData._sha1}`).then(r => r.text()) : resource.request.postData?.text; +} \ No newline at end of file diff --git a/packages/trace-viewer/src/entries.ts b/packages/trace-viewer/src/types/entries.ts similarity index 73% rename from packages/trace-viewer/src/entries.ts rename to packages/trace-viewer/src/types/entries.ts index dbfec63be8..e7b7a0b977 100644 --- a/packages/trace-viewer/src/entries.ts +++ b/packages/trace-viewer/src/types/entries.ts @@ -14,10 +14,12 @@ * limitations under the License. */ -import type { Language } from '../../playwright-core/src/utils/isomorphic/locatorGenerators'; +import type { Language } from 'playwright-core/src/utils/isomorphic/locatorGenerators'; import type { ResourceSnapshot } from '@trace/snapshot'; import type * as trace from '@trace/trace'; +// *Entry structures are used to pass the trace between the sw and the page. + export type ContextEntry = { origin: 'testRunner'|'library'; traceUrl: string; @@ -54,26 +56,3 @@ export type PageEntry = { export type ActionEntry = trace.ActionTraceEvent & { log: { time: number, message: string }[]; }; - -export function createEmptyContext(): ContextEntry { - return { - origin: 'testRunner', - traceUrl: '', - startTime: Number.MAX_SAFE_INTEGER, - wallTime: Number.MAX_SAFE_INTEGER, - endTime: 0, - browserName: '', - options: { - deviceScaleFactor: 1, - isMobile: false, - viewport: { width: 1280, height: 800 }, - }, - pages: [], - resources: [], - actions: [], - events: [], - errors: [], - stdio: [], - hasSource: false, - }; -} diff --git a/packages/trace-viewer/src/ui/DEPS.list b/packages/trace-viewer/src/ui/DEPS.list index 3fab0da95b..0056375c05 100644 --- a/packages/trace-viewer/src/ui/DEPS.list +++ b/packages/trace-viewer/src/ui/DEPS.list @@ -6,3 +6,4 @@ ../entries.ts ../geometry.ts ../../../playwright/src/isomorphic/** +../third_party/devtools.ts diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 3935447e7d..f375ab0baa 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -24,7 +24,7 @@ import type { Language } from '@isomorphic/locatorGenerators'; import type { TreeState } from '@web/components/treeView'; import { TreeView } from '@web/components/treeView'; import type { ActionTraceEventInContext, ActionTreeItem } from './modelUtil'; -import type { Boundaries } from '../geometry'; +import type { Boundaries } from './geometry'; export interface ActionListProps { actions: ActionTraceEventInContext[], @@ -32,9 +32,9 @@ export interface ActionListProps { selectedTime: Boundaries | undefined, setSelectedTime: (time: Boundaries | undefined) => void, sdkLanguage: Language | undefined; - onSelected: (action: ActionTraceEventInContext) => void, - onHighlighted: (action: ActionTraceEventInContext | undefined) => void, - revealConsole: () => void, + onSelected?: (action: ActionTraceEventInContext) => void, + onHighlighted?: (action: ActionTraceEventInContext | undefined) => void, + revealConsole?: () => void, isLive?: boolean, } @@ -67,8 +67,8 @@ export const ActionList: React.FC<ActionListProps> = ({ treeState={treeState} setTreeState={setTreeState} selectedItem={selectedItem} - onSelected={item => onSelected(item.action!)} - onHighlighted={item => onHighlighted(item?.action)} + onSelected={item => onSelected?.(item.action!)} + onHighlighted={item => onHighlighted?.(item?.action)} onAccepted={item => setSelectedTime({ minimum: item.action!.startTime, maximum: item.action!.endTime })} isError={item => !!item.action?.error?.message} isVisible={item => !selectedTime || (item.action!.startTime <= selectedTime.maximum && item.action!.endTime >= selectedTime.minimum)} diff --git a/packages/trace-viewer/src/ui/callTab.css b/packages/trace-viewer/src/ui/callTab.css index 56928088b5..f57f3f1529 100644 --- a/packages/trace-viewer/src/ui/callTab.css +++ b/packages/trace-viewer/src/ui/callTab.css @@ -36,6 +36,8 @@ .call-section { padding-left: 6px; + padding-top: 2px; + margin-top: 2px; font-weight: bold; text-transform: uppercase; font-size: 10px; @@ -53,9 +55,8 @@ align-items: center; text-overflow: ellipsis; overflow: hidden; - line-height: 18px; + line-height: 20px; white-space: nowrap; - max-height: 18px; } .call-line:not(:hover) .toolbar-button.copy { @@ -64,7 +65,8 @@ .call-line .toolbar-button.copy { margin-left: 5px; - transform: scale(0.8); + margin-top: -2px; + margin-bottom: -2px; } .call-value { diff --git a/packages/trace-viewer/src/ui/consoleTab.tsx b/packages/trace-viewer/src/ui/consoleTab.tsx index b2947f5011..cdc3cb7e93 100644 --- a/packages/trace-viewer/src/ui/consoleTab.tsx +++ b/packages/trace-viewer/src/ui/consoleTab.tsx @@ -19,7 +19,7 @@ import * as React from 'react'; import './consoleTab.css'; import type * as modelUtil from './modelUtil'; import { ListView } from '@web/components/listView'; -import type { Boundaries } from '../geometry'; +import type { Boundaries } from './geometry'; import { clsx, msToString } from '@web/uiUtils'; import { ansi2html } from '@web/ansi2html'; import { PlaceholderPanel } from './placeholderPanel'; @@ -106,9 +106,9 @@ export function useConsoleTabModel(model: modelUtil.MultiTraceModel | undefined, export const ConsoleTab: React.FunctionComponent<{ boundaries: Boundaries, consoleModel: ConsoleTabModel, - selectedTime: Boundaries | undefined, - onEntryHovered: (entry: ConsoleEntry | undefined) => void, - onAccepted: (entry: ConsoleEntry) => void, + selectedTime?: Boundaries | undefined, + onEntryHovered?: (entry: ConsoleEntry | undefined) => void, + onAccepted?: (entry: ConsoleEntry) => void, }> = ({ consoleModel, boundaries, onEntryHovered, onAccepted }) => { if (!consoleModel.entries.length) return <PlaceholderPanel text='No console entries' />; diff --git a/packages/trace-viewer/src/ui/copyToClipboard.css b/packages/trace-viewer/src/ui/copyToClipboard.css new file mode 100644 index 0000000000..08e6d26da1 --- /dev/null +++ b/packages/trace-viewer/src/ui/copyToClipboard.css @@ -0,0 +1,22 @@ +/* + 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. +*/ + +.copy-to-clipboard-text-button { + background-color: var(--vscode-editor-inactiveSelectionBackground); + border: none; + padding: 4px 12px; + cursor: pointer; +} \ No newline at end of file diff --git a/packages/trace-viewer/src/ui/copyToClipboard.tsx b/packages/trace-viewer/src/ui/copyToClipboard.tsx index 3e570ede56..1eb989d08e 100644 --- a/packages/trace-viewer/src/ui/copyToClipboard.tsx +++ b/packages/trace-viewer/src/ui/copyToClipboard.tsx @@ -16,19 +16,25 @@ import * as React from 'react'; import { ToolbarButton } from '@web/components/toolbarButton'; +import './copyToClipboard.css'; export const CopyToClipboard: React.FunctionComponent<{ - value: string, + value: string | (() => Promise<string>), description?: string, }> = ({ value, description }) => { const [icon, setIcon] = React.useState('copy'); const handleCopy = React.useCallback(() => { - navigator.clipboard.writeText(value).then(() => { - setIcon('check'); - setTimeout(() => { - setIcon('copy'); - }, 3000); + const valuePromise = typeof value === 'function' ? value() : Promise.resolve(value); + valuePromise.then(value => { + navigator.clipboard.writeText(value).then(() => { + setIcon('check'); + setTimeout(() => { + setIcon('copy'); + }, 3000); + }, () => { + setIcon('close'); + }); }, () => { setIcon('close'); }); @@ -36,3 +42,15 @@ export const CopyToClipboard: React.FunctionComponent<{ }, [value]); return <ToolbarButton title={description ? description : 'Copy'} icon={icon} onClick={handleCopy}/>; }; + +export const CopyToClipboardTextButton: React.FunctionComponent<{ + value: string | (() => Promise<string>), + description: string, +}> = ({ value, description }) => { + const handleCopy = React.useCallback(async () => { + const valueToCopy = typeof value === 'function' ? await value() : value; + await navigator.clipboard.writeText(valueToCopy); + }, [value]); + + return <ToolbarButton title={description} onClick={handleCopy} className='copy-to-clipboard-text-button'>{description}</ToolbarButton>; +}; diff --git a/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx b/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx index 1f1b5327da..3500b93b1f 100644 --- a/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx +++ b/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx @@ -15,7 +15,7 @@ */ import * as React from 'react'; -import type { ContextEntry } from '../entries'; +import type { ContextEntry } from '../types/entries'; import { MultiTraceModel } from './modelUtil'; import './embeddedWorkbenchLoader.css'; import { Workbench } from './workbench'; diff --git a/packages/trace-viewer/src/ui/filmStrip.tsx b/packages/trace-viewer/src/ui/filmStrip.tsx index a0818c10ed..dd8a0cb0ac 100644 --- a/packages/trace-viewer/src/ui/filmStrip.tsx +++ b/packages/trace-viewer/src/ui/filmStrip.tsx @@ -15,10 +15,10 @@ */ import './filmStrip.css'; -import type { Boundaries, Size } from '../geometry'; +import type { Boundaries, Size } from './geometry'; import * as React from 'react'; import { useMeasure, upperBound } from '@web/uiUtils'; -import type { PageEntry } from '../entries'; +import type { PageEntry } from '../types/entries'; import type { ActionTraceEventInContext, MultiTraceModel } from './modelUtil'; import { renderAction } from './actionList'; import type { Language } from '@isomorphic/locatorGenerators'; diff --git a/packages/trace-viewer/src/geometry.ts b/packages/trace-viewer/src/ui/geometry.ts similarity index 100% rename from packages/trace-viewer/src/geometry.ts rename to packages/trace-viewer/src/ui/geometry.ts diff --git a/packages/trace-viewer/src/ui/inspectorTab.tsx b/packages/trace-viewer/src/ui/inspectorTab.tsx index ba37cf1bd1..c608cde21b 100644 --- a/packages/trace-viewer/src/ui/inspectorTab.tsx +++ b/packages/trace-viewer/src/ui/inspectorTab.tsx @@ -17,18 +17,17 @@ import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper'; import type { Language } from '@web/components/codeMirrorWrapper'; import { ToolbarButton } from '@web/components/toolbarButton'; -import { copy, useSetting } from '@web/uiUtils'; +import { copy } from '@web/uiUtils'; import * as React from 'react'; import './sourceTab.css'; export const InspectorTab: React.FunctionComponent<{ + showScreenshot: boolean, sdkLanguage: Language, setIsInspecting: (isInspecting: boolean) => void, highlightedLocator: string, setHighlightedLocator: (locator: string) => void, -}> = ({ sdkLanguage, setIsInspecting, highlightedLocator, setHighlightedLocator }) => { - const [showScreenshot] = useSetting('screenshot-instead-of-snapshot', false); - +}> = ({ showScreenshot, sdkLanguage, setIsInspecting, highlightedLocator, setHighlightedLocator }) => { return <div className='vbox' style={{ backgroundColor: 'var(--vscode-sideBar-background)' }}> <div style={{ margin: '10px 0px 10px 10px', color: 'var(--vscode-editorCodeLens-foreground)', flex: 'none' }}>Locator</div> <div style={{ margin: '0 10px 10px', flex: 'auto' }}> diff --git a/packages/trace-viewer/src/ui/modelUtil.ts b/packages/trace-viewer/src/ui/modelUtil.ts index a544d4dc3f..b639a4c03d 100644 --- a/packages/trace-viewer/src/ui/modelUtil.ts +++ b/packages/trace-viewer/src/ui/modelUtil.ts @@ -18,7 +18,7 @@ import type { Language } from '@isomorphic/locatorGenerators'; import type { ResourceSnapshot } from '@trace/snapshot'; import type * as trace from '@trace/trace'; import type { ActionTraceEvent } from '@trace/trace'; -import type { ContextEntry, PageEntry } from '../entries'; +import type { ActionEntry, ContextEntry, PageEntry } from '../types/entries'; import type { StackFrame } from '@protocol/channels'; const contextSymbol = Symbol('context'); @@ -39,9 +39,8 @@ export type SourceModel = { content: string | undefined; }; -export type ActionTraceEventInContext = ActionTraceEvent & { +export type ActionTraceEventInContext = ActionEntry & { context: ContextEntry; - log: { time: number, message: string }[]; }; export type ActionTreeItem = { @@ -312,7 +311,7 @@ function monotonicTimeDeltaBetweenLibraryAndRunner(nonPrimaryContexts: ContextEn for (const action of context.actions) { if (!action.startTime) continue; - const key = matchByStepId ? action.stepId! : `${action.apiName}@${(action as any).wallTime}`; + const key = matchByStepId ? action.callId! : `${action.apiName}@${(action as any).wallTime}`; const libraryAction = libraryActions.get(key); if (libraryAction) return action.startTime - libraryAction.startTime; diff --git a/packages/trace-viewer/src/ui/networkResourceDetails.css b/packages/trace-viewer/src/ui/networkResourceDetails.css index 59989b89dd..13582d40cf 100644 --- a/packages/trace-viewer/src/ui/networkResourceDetails.css +++ b/packages/trace-viewer/src/ui/networkResourceDetails.css @@ -49,6 +49,12 @@ overflow: hidden; } +.network-request-details-copy { + display: flex; + margin: 8px 10px; + gap: 8px; +} + .network-font-preview { font-family: font-preview; font-size: 30px; diff --git a/packages/trace-viewer/src/ui/networkResourceDetails.tsx b/packages/trace-viewer/src/ui/networkResourceDetails.tsx index 8df091b262..7fe5c7f732 100644 --- a/packages/trace-viewer/src/ui/networkResourceDetails.tsx +++ b/packages/trace-viewer/src/ui/networkResourceDetails.tsx @@ -20,6 +20,8 @@ import './networkResourceDetails.css'; import { TabbedPane } from '@web/components/tabbedPane'; import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper'; import { ToolbarButton } from '@web/components/toolbarButton'; +import { generateCurlCommand, generateFetchCall } from '../third_party/devtools'; +import { CopyToClipboardTextButton } from './copyToClipboard'; export const NetworkResourceDetails: React.FunctionComponent<{ resource: ResourceSnapshot; @@ -90,6 +92,12 @@ const RequestTab: React.FunctionComponent<{ </> : null} <div className='network-request-details-header'>Request Headers</div> <div className='network-request-details-headers'>{resource.request.headers.map(pair => `${pair.name}: ${pair.value}`).join('\n')}</div> + + <div className='network-request-details-copy'> + <CopyToClipboardTextButton description='Copy as cURL' value={() => generateCurlCommand(resource)} /> + <CopyToClipboardTextButton description='Copy as Fetch' value={() => generateFetchCall(resource)} /> + </div> + {requestBody && <div className='network-request-details-header'>Request Body</div>} {requestBody && <CodeMirrorWrapper text={requestBody.text} mimeType={requestBody.mimeType} readOnly lineNumbers={true}/>} </div>; diff --git a/packages/trace-viewer/src/ui/networkTab.tsx b/packages/trace-viewer/src/ui/networkTab.tsx index 207dd33547..ec9156d9e6 100644 --- a/packages/trace-viewer/src/ui/networkTab.tsx +++ b/packages/trace-viewer/src/ui/networkTab.tsx @@ -16,7 +16,7 @@ import type { Entry } from '@trace/har'; import * as React from 'react'; -import type { Boundaries } from '../geometry'; +import type { Boundaries } from './geometry'; import './networkTab.css'; import { NetworkResourceDetails } from './networkResourceDetails'; import { bytesToString, msToString } from '@web/uiUtils'; @@ -24,7 +24,7 @@ import { PlaceholderPanel } from './placeholderPanel'; import { context, type MultiTraceModel } from './modelUtil'; import { GridView, type RenderedGridCell } from '@web/components/gridView'; import { SplitView } from '@web/components/splitView'; -import type { ContextEntry } from '../entries'; +import type { ContextEntry } from '../types/entries'; import { NetworkFilters, defaultFilterState, type FilterState, type ResourceType } from './networkFilters'; type NetworkTabModel = { @@ -65,7 +65,7 @@ export function useNetworkTabModel(model: MultiTraceModel | undefined, selectedT export const NetworkTab: React.FunctionComponent<{ boundaries: Boundaries, networkModel: NetworkTabModel, - onEntryHovered: (entry: Entry | undefined) => void, + onEntryHovered?: (entry: Entry | undefined) => void, }> = ({ boundaries, networkModel, onEntryHovered }) => { const [sorting, setSorting] = React.useState<Sorting | undefined>(undefined); const [selectedEntry, setSelectedEntry] = React.useState<RenderedEntry | undefined>(undefined); @@ -95,7 +95,7 @@ export const NetworkTab: React.FunctionComponent<{ items={renderedEntries} selectedItem={selectedEntry} onSelected={item => setSelectedEntry(item)} - onHighlighted={item => onEntryHovered(item?.resource)} + onHighlighted={item => onEntryHovered?.(item?.resource)} columns={visibleColumns(!!selectedEntry, renderedEntries)} columnTitle={columnTitle} columnWidths={columnWidths} diff --git a/packages/trace-viewer/src/ui/recorder/DEPS.list b/packages/trace-viewer/src/ui/recorder/DEPS.list new file mode 100644 index 0000000000..a504a7dba1 --- /dev/null +++ b/packages/trace-viewer/src/ui/recorder/DEPS.list @@ -0,0 +1,5 @@ +[*] +@isomorphic/** +@trace/** +@web/** +../** diff --git a/packages/trace-viewer/src/ui/recorder/actionListView.tsx b/packages/trace-viewer/src/ui/recorder/actionListView.tsx new file mode 100644 index 0000000000..a68df255ea --- /dev/null +++ b/packages/trace-viewer/src/ui/recorder/actionListView.tsx @@ -0,0 +1,63 @@ +/* + 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. +*/ + +import type * as actionTypes from '@recorder/actions'; +import { ListView } from '@web/components/listView'; +import * as React from 'react'; +import '../actionList.css'; +import { traceParamsForAction } from '@isomorphic/recorderUtils'; +import { asLocator } from '@isomorphic/locatorGenerators'; +import type { Language } from '@isomorphic/locatorGenerators'; + +const ActionList = ListView<actionTypes.ActionInContext>; + +export const ActionListView: React.FC<{ + sdkLanguage: Language, + actions: actionTypes.ActionInContext[], + selectedAction: actionTypes.ActionInContext | undefined, + onSelectedAction: (action: actionTypes.ActionInContext | undefined) => void, +}> = ({ + sdkLanguage, + actions, + selectedAction, + onSelectedAction, +}) => { + const render = React.useCallback((action: actionTypes.ActionInContext) => { + return renderAction(sdkLanguage, action); + }, [sdkLanguage]); + return <div className='vbox'> + <ActionList + name='actions' + items={actions} + selectedItem={selectedAction} + onSelected={onSelectedAction} + render={render} /> + </div>; +}; + +export const renderAction = (sdkLanguage: Language, action: actionTypes.ActionInContext) => { + const { method, params } = traceParamsForAction(action); + const locator = params.selector ? asLocator(sdkLanguage || 'javascript', params.selector) : undefined; + + const apiName = `page.${method}`; + return <> + <div className='action-title' title={apiName}> + <span>{apiName}</span> + {locator && <div className='action-selector' title={locator}>{locator}</div>} + {method === 'goto' && params.url && <div className='action-url' title={params.url}>{params.url}</div>} + </div> + </>; +}; diff --git a/packages/trace-viewer/src/ui/recorder/backendContext.tsx b/packages/trace-viewer/src/ui/recorder/backendContext.tsx new file mode 100644 index 0000000000..312281001e --- /dev/null +++ b/packages/trace-viewer/src/ui/recorder/backendContext.tsx @@ -0,0 +1,118 @@ +/* + 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. +*/ + +import type * as actionTypes from '@recorder/actions'; +import type { Mode, Source } from '@recorder/recorderTypes'; +import * as React from 'react'; + +export const BackendContext = React.createContext<Backend | undefined>(undefined); + +export const BackendProvider: React.FunctionComponent<React.PropsWithChildren<{ + guid: string, +}>> = ({ guid, children }) => { + const [connection, setConnection] = React.useState<Connection | undefined>(undefined); + const [mode, setMode] = React.useState<Mode>('none'); + const [actions, setActions] = React.useState<{ actions: actionTypes.ActionInContext[], sources: Source[] }>({ actions: [], sources: [] }); + const callbacks = React.useRef({ setMode, setActions }); + + React.useEffect(() => { + const wsURL = new URL(`../${guid}`, window.location.toString()); + wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:'); + const webSocket = new WebSocket(wsURL.toString()); + setConnection(new Connection(webSocket, callbacks.current)); + return () => { + webSocket.close(); + }; + }, [guid]); + + const backend = React.useMemo(() => { + return connection ? { mode, actions: actions.actions, sources: actions.sources, connection } : undefined; + }, [actions, mode, connection]); + + return <BackendContext.Provider value={backend}> + {children} + </BackendContext.Provider>; +}; + +export type Backend = { + actions: actionTypes.ActionInContext[], + sources: Source[], + connection: Connection, +}; + +type ConnectionCallbacks = { + setMode: (mode: Mode) => void; + setActions: (data: { actions: actionTypes.ActionInContext[], sources: Source[] }) => void; +}; + +class Connection { + private _lastId = 0; + private _webSocket: WebSocket; + private _callbacks = new Map<number, { resolve: (arg: any) => void, reject: (arg: Error) => void }>(); + private _options: ConnectionCallbacks; + + constructor(webSocket: WebSocket, options: ConnectionCallbacks) { + this._webSocket = webSocket; + this._callbacks = new Map(); + this._options = options; + + this._webSocket.addEventListener('message', event => { + const message = JSON.parse(event.data); + const { id, result, error, method, params } = message; + if (id) { + const callback = this._callbacks.get(id); + if (!callback) + return; + this._callbacks.delete(id); + if (error) + callback.reject(new Error(error)); + else + callback.resolve(result); + } else { + this._dispatchEvent(method, params); + } + }); + } + + setMode(mode: Mode) { + this._sendMessageNoReply('setMode', { mode }); + } + + private async _sendMessage(method: string, params?: any): Promise<any> { + const id = ++this._lastId; + const message = { id, method, params }; + this._webSocket.send(JSON.stringify(message)); + return new Promise((resolve, reject) => { + this._callbacks.set(id, { resolve, reject }); + }); + } + + private _sendMessageNoReply(method: string, params?: any) { + this._sendMessage(method, params).catch(() => { }); + } + + private _dispatchEvent(method: string, params?: any) { + if (method === 'setMode') { + const { mode } = params as { mode: Mode }; + this._options.setMode(mode); + } + if (method === 'setActions') { + const { actions, sources } = params as { actions: actionTypes.ActionInContext[], sources: Source[] }; + this._options.setActions({ actions: actions.filter(a => a.action.name !== 'openPage' && a.action.name !== 'closePage'), sources }); + (window as any).playwrightSourcesEchoForTest = sources; + } + } +} diff --git a/packages/trace-viewer/src/ui/recorder/modelContext.tsx b/packages/trace-viewer/src/ui/recorder/modelContext.tsx new file mode 100644 index 0000000000..9db52e1964 --- /dev/null +++ b/packages/trace-viewer/src/ui/recorder/modelContext.tsx @@ -0,0 +1,70 @@ +/* + 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. +*/ + +import { sha1 } from '@web/uiUtils'; +import * as React from 'react'; +import type { ContextEntry } from '../../types/entries'; +import { MultiTraceModel } from '../modelUtil'; + +export const ModelContext = React.createContext<MultiTraceModel | undefined>(undefined); + +export const ModelProvider: React.FunctionComponent<React.PropsWithChildren<{ + trace: string, +}>> = ({ trace, children }) => { + const [model, setModel] = React.useState<{ model: MultiTraceModel, sha1: string } | undefined>(); + const [counter, setCounter] = React.useState(0); + const pollTimer = React.useRef<NodeJS.Timeout | null>(null); + + React.useEffect(() => { + if (pollTimer.current) + clearTimeout(pollTimer.current); + + // Start polling running test. + pollTimer.current = setTimeout(async () => { + try { + const result = await loadSingleTraceFile(trace); + if (result.sha1 !== model?.sha1) + setModel(result); + } catch { + setModel(undefined); + } finally { + setCounter(counter + 1); + } + }, 500); + return () => { + if (pollTimer.current) + clearTimeout(pollTimer.current); + }; + }, [counter, model, trace]); + + return <ModelContext.Provider value={model?.model}> + {children} + </ModelContext.Provider>; +}; + +async function loadSingleTraceFile(url: string): Promise<{ model: MultiTraceModel, sha1: string }> { + const params = new URLSearchParams(); + params.set('trace', url); + const response = await fetch(`contexts?${params.toString()}`); + const contextEntries = await response.json() as ContextEntry[]; + + const tokens: string[] = []; + for (const entry of contextEntries) { + entry.actions.forEach(a => tokens.push(a.type + '@' + a.startTime + '-' + a.endTime)); + entry.events.forEach(e => tokens.push(e.type + '@' + e.time)); + } + return { model: new MultiTraceModel(contextEntries), sha1: await sha1(tokens.join('|')) }; +} diff --git a/packages/trace-viewer/src/ui/recorderView.css b/packages/trace-viewer/src/ui/recorder/recorderView.css similarity index 100% rename from packages/trace-viewer/src/ui/recorderView.css rename to packages/trace-viewer/src/ui/recorder/recorderView.css diff --git a/packages/trace-viewer/src/ui/recorder/recorderView.tsx b/packages/trace-viewer/src/ui/recorder/recorderView.tsx new file mode 100644 index 0000000000..ed36efbbb6 --- /dev/null +++ b/packages/trace-viewer/src/ui/recorder/recorderView.tsx @@ -0,0 +1,295 @@ +/* + 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. +*/ + +import type * as actionTypes from '@recorder/actions'; +import { SourceChooser } from '@web/components/sourceChooser'; +import { SplitView } from '@web/components/splitView'; +import type { TabbedPaneTabModel } from '@web/components/tabbedPane'; +import { TabbedPane } from '@web/components/tabbedPane'; +import { Toolbar } from '@web/components/toolbar'; +import { ToolbarButton, ToolbarSeparator } from '@web/components/toolbarButton'; +import { toggleTheme } from '@web/theme'; +import { copy, useSetting } from '@web/uiUtils'; +import * as React from 'react'; +import { ConsoleTab, useConsoleTabModel } from '../consoleTab'; +import type { Boundaries } from '../geometry'; +import { InspectorTab } from '../inspectorTab'; +import type * as modelUtil from '../modelUtil'; +import type { SourceLocation } from '../modelUtil'; +import { NetworkTab, useNetworkTabModel } from '../networkTab'; +import { collectSnapshots, extendSnapshot, SnapshotView } from '../snapshotTab'; +import { SourceTab } from '../sourceTab'; +import { ModelContext, ModelProvider } from './modelContext'; +import './recorderView.css'; +import { ActionListView } from './actionListView'; +import { BackendContext, BackendProvider } from './backendContext'; +import type { Language } from '@isomorphic/locatorGenerators'; + +export const RecorderView: React.FunctionComponent = () => { + const searchParams = new URLSearchParams(window.location.search); + const guid = searchParams.get('ws')!; + const trace = searchParams.get('trace') + '.json'; + + return <BackendProvider guid={guid}> + <ModelProvider trace={trace}> + <Workbench /> + </ModelProvider> + </BackendProvider>; +}; + +export const Workbench: React.FunctionComponent = () => { + const backend = React.useContext(BackendContext); + const model = React.useContext(ModelContext); + const [fileId, setFileId] = React.useState<string | undefined>(); + const [selectedStartTime, setSelectedStartTime] = React.useState<number | undefined>(undefined); + const [isInspecting, setIsInspecting] = React.useState(false); + const [highlightedLocatorInProperties, setHighlightedLocatorInProperties] = React.useState<string>(''); + const [highlightedLocatorInTrace, setHighlightedLocatorInTrace] = React.useState<string>(''); + const [traceCallId, setTraceCallId] = React.useState<string | undefined>(); + + const setSelectedAction = React.useCallback((action: actionTypes.ActionInContext | undefined) => { + setSelectedStartTime(action?.startTime); + }, []); + + const selectedAction = React.useMemo(() => { + return backend?.actions.find(a => a.startTime === selectedStartTime); + }, [backend?.actions, selectedStartTime]); + + React.useEffect(() => { + const callId = model?.actions.find(a => a.endTime && a.endTime === selectedAction?.endTime)?.callId; + if (callId) + setTraceCallId(callId); + }, [model, selectedAction]); + + const source = React.useMemo(() => backend?.sources.find(s => s.id === fileId) || backend?.sources[0], [backend?.sources, fileId]); + const sourceLocation = React.useMemo(() => { + if (!source) + return undefined; + const sourceLocation: SourceLocation = { + file: '', + line: 0, + column: 0, + source: { + errors: [], + content: source.text + } + }; + return sourceLocation; + }, [source]); + + const sdkLanguage: Language = source?.language || 'javascript'; + + const { boundaries } = React.useMemo(() => { + const boundaries = { minimum: model?.startTime || 0, maximum: model?.endTime || 30000 }; + if (boundaries.minimum > boundaries.maximum) { + boundaries.minimum = 0; + boundaries.maximum = 30000; + } + // Leave some nice free space on the right hand side. + boundaries.maximum += (boundaries.maximum - boundaries.minimum) / 20; + return { boundaries }; + }, [model]); + + const locatorPickedInTrace = React.useCallback((locator: string) => { + setHighlightedLocatorInProperties(locator); + setHighlightedLocatorInTrace(''); + setIsInspecting(false); + }, []); + + const locatorTypedInProperties = React.useCallback((locator: string) => { + setHighlightedLocatorInTrace(locator); + setHighlightedLocatorInProperties(locator); + }, []); + + const actionList = <ActionListView + sdkLanguage={sdkLanguage} + actions={backend?.actions || []} + selectedAction={selectedAction} + onSelectedAction={setSelectedAction} + />; + + const actionsTab: TabbedPaneTabModel = { + id: 'actions', + title: 'Actions', + component: actionList, + }; + + const toolbar = <Toolbar sidebarBackground> + <div style={{ width: 4 }}></div> + <ToolbarButton icon='inspect' title='Pick locator' toggled={isInspecting} onClick={() => { + setIsInspecting(!isInspecting); + }} /> + <ToolbarButton icon='eye' title='Assert visibility' onClick={() => { + }} /> + <ToolbarButton icon='whole-word' title='Assert text' onClick={() => { + }} /> + <ToolbarButton icon='symbol-constant' title='Assert value' onClick={() => { + }} /> + <ToolbarSeparator /> + <ToolbarButton icon='files' title='Copy' disabled={!source || !source.text} onClick={() => { + if (source?.text) + copy(source.text); + }}></ToolbarButton> + <div style={{ flex: 'auto' }}></div> + <div>Target:</div> + <SourceChooser fileId={fileId} sources={backend?.sources || []} setFileId={fileId => { + setFileId(fileId); + }} /> + <ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton> + </Toolbar>; + + const sidebarTabbedPane = <TabbedPane tabs={[actionsTab]} />; + const traceView = <TraceView + sdkLanguage={sdkLanguage} + callId={traceCallId} + isInspecting={isInspecting} + setIsInspecting={setIsInspecting} + highlightedLocator={highlightedLocatorInTrace} + setHighlightedLocator={locatorPickedInTrace} />; + const propertiesView = <PropertiesView + sdkLanguage={sdkLanguage} + boundaries={boundaries} + setIsInspecting={setIsInspecting} + highlightedLocator={highlightedLocatorInProperties} + setHighlightedLocator={locatorTypedInProperties} + sourceLocation={sourceLocation} />; + + return <div className='vbox workbench'> + <SplitView + sidebarSize={250} + orientation={'horizontal'} + settingName='recorderActionListSidebar' + sidebarIsFirst + main={<SplitView + sidebarSize={250} + orientation='vertical' + settingName='recorderPropertiesSidebar' + main={<div className='vbox'> + {toolbar} + {traceView} + </div>} + sidebar={propertiesView} + />} + sidebar={sidebarTabbedPane} + /> + </div>; +}; + +const PropertiesView: React.FunctionComponent<{ + sdkLanguage: Language, + boundaries: Boundaries, + setIsInspecting: (value: boolean) => void, + highlightedLocator: string, + setHighlightedLocator: (locator: string) => void, + sourceLocation: modelUtil.SourceLocation | undefined, +}> = ({ + sdkLanguage, + boundaries, + setIsInspecting, + highlightedLocator, + setHighlightedLocator, + sourceLocation, +}) => { + const model = React.useContext(ModelContext); + const consoleModel = useConsoleTabModel(model, boundaries); + const networkModel = useNetworkTabModel(model, boundaries); + const sourceModel = React.useRef(new Map<string, modelUtil.SourceModel>()); + const [selectedPropertiesTab, setSelectedPropertiesTab] = useSetting<string>('recorderPropertiesTab', 'source'); + + const inspectorTab: TabbedPaneTabModel = { + id: 'inspector', + title: 'Locator', + render: () => <InspectorTab + showScreenshot={false} + sdkLanguage={sdkLanguage} + setIsInspecting={setIsInspecting} + highlightedLocator={highlightedLocator} + setHighlightedLocator={setHighlightedLocator} />, + }; + + const sourceTab: TabbedPaneTabModel = { + id: 'source', + title: 'Source', + render: () => <SourceTab + sources={sourceModel.current} + stackFrameLocation={'right'} + fallbackLocation={sourceLocation} + /> + }; + const consoleTab: TabbedPaneTabModel = { + id: 'console', + title: 'Console', + count: consoleModel.entries.length, + render: () => <ConsoleTab boundaries={boundaries} consoleModel={consoleModel} /> + }; + const networkTab: TabbedPaneTabModel = { + id: 'network', + title: 'Network', + count: networkModel.resources.length, + render: () => <NetworkTab boundaries={boundaries} networkModel={networkModel} /> + }; + + const tabs: TabbedPaneTabModel[] = [ + sourceTab, + inspectorTab, + consoleTab, + networkTab, + ]; + + return <TabbedPane + tabs={tabs} + selectedTab={selectedPropertiesTab} + setSelectedTab={setSelectedPropertiesTab} + />; +}; + +const TraceView: React.FunctionComponent<{ + sdkLanguage: Language, + callId: string | undefined, + isInspecting: boolean; + setIsInspecting: (value: boolean) => void; + highlightedLocator: string; + setHighlightedLocator: (locator: string) => void; +}> = ({ + sdkLanguage, + callId, + isInspecting, + setIsInspecting, + highlightedLocator, + setHighlightedLocator, +}) => { + const model = React.useContext(ModelContext); + const action = React.useMemo(() => { + return model?.actions.find(a => a.callId === callId); + }, [model, callId]); + + const snapshot = React.useMemo(() => { + const snapshot = collectSnapshots(action); + return snapshot.action || snapshot.after || snapshot.before; + }, [action]); + const snapshotUrls = React.useMemo(() => { + return snapshot ? extendSnapshot(snapshot) : undefined; + }, [snapshot]); + + return <SnapshotView + sdkLanguage={sdkLanguage} + testIdAttributeName='data-testid' + isInspecting={isInspecting} + setIsInspecting={setIsInspecting} + highlightedLocator={highlightedLocator} + setHighlightedLocator={setHighlightedLocator} + snapshotUrls={snapshotUrls} />; +}; diff --git a/packages/trace-viewer/src/ui/recorderView.tsx b/packages/trace-viewer/src/ui/recorderView.tsx deleted file mode 100644 index 940fd146a9..0000000000 --- a/packages/trace-viewer/src/ui/recorderView.tsx +++ /dev/null @@ -1,168 +0,0 @@ -/* - 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. -*/ - -import * as React from 'react'; -import './recorderView.css'; -import { MultiTraceModel } from './modelUtil'; -import type { SourceLocation } from './modelUtil'; -import { Workbench } from './workbench'; -import type { Mode, Source } from '@recorder/recorderTypes'; -import type { ContextEntry } from '../entries'; - -const searchParams = new URLSearchParams(window.location.search); -const guid = searchParams.get('ws'); -const trace = searchParams.get('trace') + '.json'; - -export const RecorderView: React.FunctionComponent = () => { - const [connection, setConnection] = React.useState<Connection | null>(null); - const [sources, setSources] = React.useState<Source[]>([]); - React.useEffect(() => { - const wsURL = new URL(`../${guid}`, window.location.toString()); - wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:'); - const webSocket = new WebSocket(wsURL.toString()); - setConnection(new Connection(webSocket, { setSources })); - return () => { - webSocket.close(); - }; - }, []); - - React.useEffect(() => { - if (!connection) - return; - connection.setMode('recording'); - }, [connection]); - - return <div className='vbox workbench-loader'> - <TraceView - traceLocation={trace} - sources={sources} /> - </div>; -}; - -export const TraceView: React.FC<{ - traceLocation: string, - sources: Source[], -}> = ({ traceLocation, sources }) => { - const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean } | undefined>(); - const [counter, setCounter] = React.useState(0); - const pollTimer = React.useRef<NodeJS.Timeout | null>(null); - - React.useEffect(() => { - if (pollTimer.current) - clearTimeout(pollTimer.current); - - // Start polling running test. - pollTimer.current = setTimeout(async () => { - try { - const model = await loadSingleTraceFile(traceLocation); - setModel({ model, isLive: true }); - } catch { - setModel(undefined); - } finally { - setCounter(counter + 1); - } - }, 500); - return () => { - if (pollTimer.current) - clearTimeout(pollTimer.current); - }; - }, [counter, traceLocation]); - - const fallbackLocation = React.useMemo(() => { - if (!sources.length) - return undefined; - const fallbackLocation: SourceLocation = { - file: '', - line: 0, - column: 0, - source: { - errors: [], - content: sources[0].text - } - }; - return fallbackLocation; - }, [sources]); - - return <Workbench - key='workbench' - model={model?.model} - showSourcesFirst={true} - fallbackLocation={fallbackLocation} - isLive={true} - />; -}; - -async function loadSingleTraceFile(url: string): Promise<MultiTraceModel> { - const params = new URLSearchParams(); - params.set('trace', url); - const response = await fetch(`contexts?${params.toString()}`); - const contextEntries = await response.json() as ContextEntry[]; - return new MultiTraceModel(contextEntries); -} - -class Connection { - private _lastId = 0; - private _webSocket: WebSocket; - private _callbacks = new Map<number, { resolve: (arg: any) => void, reject: (arg: Error) => void }>(); - private _options: { setSources: (sources: Source[]) => void; }; - - constructor(webSocket: WebSocket, options: { setSources: (sources: Source[]) => void }) { - this._webSocket = webSocket; - this._callbacks = new Map(); - this._options = options; - - this._webSocket.addEventListener('message', event => { - const message = JSON.parse(event.data); - const { id, result, error, method, params } = message; - if (id) { - const callback = this._callbacks.get(id); - if (!callback) - return; - this._callbacks.delete(id); - if (error) - callback.reject(new Error(error)); - else - callback.resolve(result); - } else { - this._dispatchEvent(method, params); - } - }); - } - - setMode(mode: Mode) { - this._sendMessageNoReply('setMode', { mode }); - } - - private async _sendMessage(method: string, params?: any): Promise<any> { - const id = ++this._lastId; - const message = { id, method, params }; - this._webSocket.send(JSON.stringify(message)); - return new Promise((resolve, reject) => { - this._callbacks.set(id, { resolve, reject }); - }); - } - - private _sendMessageNoReply(method: string, params?: any) { - this._sendMessage(method, params).catch(() => { }); - } - - private _dispatchEvent(method: string, params?: any) { - if (method === 'setSources') { - const { sources } = params as { sources: Source[] }; - this._options.setSources(sources); - } - } -} diff --git a/packages/trace-viewer/src/ui/snapshotTab.css b/packages/trace-viewer/src/ui/snapshotTab.css index 2677cfe53a..926685dc81 100644 --- a/packages/trace-viewer/src/ui/snapshotTab.css +++ b/packages/trace-viewer/src/ui/snapshotTab.css @@ -15,12 +15,8 @@ */ .snapshot-tab { - display: flex; - flex: auto; - flex-direction: column; align-items: stretch; outline: none; - --browser-frame-header-height: 40px; overflow: hidden; } @@ -73,6 +69,7 @@ margin: 1px; padding: 10px; position: relative; + --browser-frame-header-height: 40px; } .snapshot-container { diff --git a/packages/trace-viewer/src/ui/snapshotTab.tsx b/packages/trace-viewer/src/ui/snapshotTab.tsx index 9dafa10b96..82e4282c6f 100644 --- a/packages/trace-viewer/src/ui/snapshotTab.tsx +++ b/packages/trace-viewer/src/ui/snapshotTab.tsx @@ -20,7 +20,7 @@ import type { ActionTraceEvent } from '@trace/trace'; import { context, type MultiTraceModel, pageForAction, prevInList } from './modelUtil'; import { Toolbar } from '@web/components/toolbar'; import { ToolbarButton } from '@web/components/toolbarButton'; -import { clsx, useMeasure, useSetting } from '@web/uiUtils'; +import { clsx, useMeasure } from '@web/uiUtils'; import { InjectedScript } from '@injected/injectedScript'; import { Recorder } from '@injected/recorder/recorder'; import ConsoleAPI from '@injected/consoleApi'; @@ -40,7 +40,7 @@ function findClosest<T>(items: T[], metric: (v: T) => number, target: number) { }); } -export const SnapshotTab: React.FunctionComponent<{ +export const SnapshotTabsView: React.FunctionComponent<{ action: ActionTraceEvent | undefined, model?: MultiTraceModel, sdkLanguage: Language, @@ -50,63 +50,69 @@ export const SnapshotTab: React.FunctionComponent<{ highlightedLocator: string, setHighlightedLocator: (locator: string) => void, openPage?: (url: string, target?: string) => Window | any, -}> = ({ action, model, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator, openPage }) => { - const [measure, ref] = useMeasure<HTMLDivElement>(); +}> = ({ action, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator, openPage }) => { const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action'); - const [showScreenshotInsteadOfSnapshot] = useSetting('screenshot-instead-of-snapshot', false); + const showScreenshotInsteadOfSnapshot = false; - type Snapshot = { action: ActionTraceEvent, snapshotName: string, point?: { x: number, y: number }, hasInputTarget?: boolean }; - const { snapshots } = React.useMemo(() => { - if (!action) - return { snapshots: {} }; - - // if the action has no beforeSnapshot, use the last available afterSnapshot. - let beforeSnapshot: Snapshot | undefined = action.beforeSnapshot ? { action, snapshotName: action.beforeSnapshot } : undefined; - let a = action; - while (!beforeSnapshot && a) { - a = prevInList(a); - beforeSnapshot = a?.afterSnapshot ? { action: a, snapshotName: a?.afterSnapshot } : undefined; - } - const afterSnapshot: Snapshot | undefined = action.afterSnapshot ? { action, snapshotName: action.afterSnapshot } : beforeSnapshot; - const actionSnapshot: Snapshot | undefined = action.inputSnapshot ? { action, snapshotName: action.inputSnapshot, hasInputTarget: true } : afterSnapshot; - if (actionSnapshot) - actionSnapshot.point = action.point; - return { snapshots: { action: actionSnapshot, before: beforeSnapshot, after: afterSnapshot } }; + const snapshots = React.useMemo(() => { + return collectSnapshots(action); }, [action]); - - const { snapshotInfoUrl, snapshotUrl, popoutUrl, point } = React.useMemo(() => { + const snapshotUrls = React.useMemo(() => { const snapshot = snapshots[snapshotTab]; - if (!snapshot) - return { snapshotUrl: kBlankSnapshotUrl }; - - const params = new URLSearchParams(); - params.set('trace', context(snapshot.action).traceUrl); - params.set('name', snapshot.snapshotName); - if (snapshot.point) { - params.set('pointX', String(snapshot.point.x)); - params.set('pointY', String(snapshot.point.y)); - if (snapshot.hasInputTarget) - params.set('hasInputTarget', '1'); - } - const snapshotUrl = new URL(`snapshot/${snapshot.action.pageId}?${params.toString()}`, window.location.href).toString(); - const snapshotInfoUrl = new URL(`snapshotInfo/${snapshot.action.pageId}?${params.toString()}`, window.location.href).toString(); - - const popoutParams = new URLSearchParams(); - popoutParams.set('r', snapshotUrl); - popoutParams.set('trace', context(snapshot.action).traceUrl); - if (snapshot.point) { - popoutParams.set('pointX', String(snapshot.point.x)); - popoutParams.set('pointY', String(snapshot.point.y)); - if (snapshot.hasInputTarget) - params.set('hasInputTarget', '1'); - } - const popoutUrl = new URL(`snapshot.html?${popoutParams.toString()}`, window.location.href).toString(); - return { snapshots, snapshotInfoUrl, snapshotUrl, popoutUrl, point: snapshot.point }; + return snapshot ? extendSnapshot(snapshot) : undefined; }, [snapshots, snapshotTab]); + return <div className='snapshot-tab vbox'> + <Toolbar> + <ToolbarButton className='pick-locator' title={showScreenshotInsteadOfSnapshot ? 'Disable "screenshots instead of snapshots" to pick a locator' : 'Pick locator'} icon='target' toggled={isInspecting} onClick={() => setIsInspecting(!isInspecting)} disabled={showScreenshotInsteadOfSnapshot} /> + {['action', 'before', 'after'].map(tab => { + return <TabbedPaneTab + key={tab} + id={tab} + title={renderTitle(tab)} + selected={snapshotTab === tab} + onSelect={() => setSnapshotTab(tab as 'action' | 'before' | 'after')} + ></TabbedPaneTab>; + })} + <div style={{ flex: 'auto' }}></div> + <ToolbarButton icon='link-external' title={showScreenshotInsteadOfSnapshot ? 'Not available when showing screenshot' : 'Open snapshot in a new tab'} disabled={!snapshotUrls?.popoutUrl || showScreenshotInsteadOfSnapshot} onClick={() => { + if (!openPage) + openPage = window.open; + const win = openPage(snapshotUrls?.popoutUrl || '', '_blank'); + win?.addEventListener('DOMContentLoaded', () => { + const injectedScript = new InjectedScript(win as any, false, sdkLanguage, testIdAttributeName, 1, 'chromium', []); + new ConsoleAPI(injectedScript); + }); + }} /> + </Toolbar> + {!showScreenshotInsteadOfSnapshot && <SnapshotView + snapshotUrls={snapshotUrls} + sdkLanguage={sdkLanguage} + testIdAttributeName={testIdAttributeName} + isInspecting={isInspecting} + setIsInspecting={setIsInspecting} + highlightedLocator={highlightedLocator} + setHighlightedLocator={setHighlightedLocator} + />} + {showScreenshotInsteadOfSnapshot && <ScreenshotView + action={action} + snapshotUrls={snapshotUrls} + snapshot={snapshots[snapshotTab]} />} + </div>; +}; + +export const SnapshotView: React.FunctionComponent<{ + snapshotUrls: SnapshotUrls | undefined, + sdkLanguage: Language, + testIdAttributeName: string, + isInspecting: boolean, + setIsInspecting: (isInspecting: boolean) => void, + highlightedLocator: string, + setHighlightedLocator: (locator: string) => void, +}> = ({ snapshotUrls, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator }) => { const iframeRef0 = React.useRef<HTMLIFrameElement>(null); const iframeRef1 = React.useRef<HTMLIFrameElement>(null); - const [snapshotInfo, setSnapshotInfo] = React.useState<{ viewport: typeof kDefaultViewport, url: string, timestamp?: number, wallTime?: undefined }>({ viewport: kDefaultViewport, url: '' }); + const [snapshotInfo, setSnapshotInfo] = React.useState<SnapshotInfo>({ viewport: kDefaultViewport, url: '' }); const loadingRef = React.useRef({ iteration: 0, visibleIframe: 0 }); React.useEffect(() => { @@ -115,17 +121,7 @@ export const SnapshotTab: React.FunctionComponent<{ const newVisibleIframe = 1 - loadingRef.current.visibleIframe; loadingRef.current.iteration = thisIteration; - const newSnapshotInfo = { url: '', viewport: kDefaultViewport, timestamp: undefined, wallTime: undefined }; - if (snapshotInfoUrl) { - const response = await fetch(snapshotInfoUrl); - const info = await response.json(); - if (!info.error) { - newSnapshotInfo.url = info.url; - newSnapshotInfo.viewport = info.viewport; - newSnapshotInfo.timestamp = info.timestamp; - newSnapshotInfo.wallTime = info.wallTime; - } - } + const newSnapshotInfo = await fetchSnapshotInfo(snapshotUrls?.snapshotInfoUrl); // Interrupted by another load - bail out. if (loadingRef.current.iteration !== thisIteration) @@ -140,6 +136,7 @@ export const SnapshotTab: React.FunctionComponent<{ iframe.addEventListener('error', loadedCallback); // Try preventing history entry from being created. + const snapshotUrl = snapshotUrls?.snapshotUrl || kBlankSnapshotUrl; if (iframe.contentWindow) iframe.contentWindow.location.replace(snapshotUrl); else @@ -159,33 +156,10 @@ export const SnapshotTab: React.FunctionComponent<{ loadingRef.current.visibleIframe = newVisibleIframe; setSnapshotInfo(newSnapshotInfo); })(); - }, [snapshotUrl, snapshotInfoUrl]); - - const windowHeaderHeight = 40; - const snapshotContainerSize = { - width: snapshotInfo.viewport.width, - height: snapshotInfo.viewport.height + windowHeaderHeight, - }; - const scale = Math.min(measure.width / snapshotContainerSize.width, measure.height / snapshotContainerSize.height, 1); - const translate = { - x: (measure.width - snapshotContainerSize.width) / 2, - y: (measure.height - snapshotContainerSize.height) / 2, - }; - - const page = action ? pageForAction(action) : undefined; - const screencastFrame = React.useMemo( - () => { - if (snapshotInfo.wallTime && page?.screencastFrames[0]?.frameSwapWallTime) - return findClosest(page.screencastFrames, frame => frame.frameSwapWallTime!, snapshotInfo.wallTime); - - if (snapshotInfo.timestamp && page?.screencastFrames) - return findClosest(page.screencastFrames, frame => frame.timestamp, snapshotInfo.timestamp); - }, - [page?.screencastFrames, snapshotInfo.timestamp, snapshotInfo.wallTime] - ); + }, [snapshotUrls]); return <div - className='snapshot-tab' + className='vbox' tabIndex={0} onKeyDown={event => { if (event.key === 'Escape') { @@ -210,46 +184,72 @@ export const SnapshotTab: React.FunctionComponent<{ setHighlightedLocator={setHighlightedLocator} iframe={iframeRef1.current} iteration={loadingRef.current.iteration} /> - <Toolbar> - <ToolbarButton className='pick-locator' title={showScreenshotInsteadOfSnapshot ? 'Disable "screenshots instead of snapshots" to pick a locator' : 'Pick locator'} icon='target' toggled={isInspecting} onClick={() => setIsInspecting(!isInspecting)} disabled={showScreenshotInsteadOfSnapshot} /> - {['action', 'before', 'after'].map(tab => { - return <TabbedPaneTab - key={tab} - id={tab} - title={renderTitle(tab)} - selected={snapshotTab === tab} - onSelect={() => setSnapshotTab(tab as 'action' | 'before' | 'after')} - ></TabbedPaneTab>; - })} - <div style={{ flex: 'auto' }}></div> - <ToolbarButton icon='link-external' title={showScreenshotInsteadOfSnapshot ? 'Not available when showing screenshot' : 'Open snapshot in a new tab'} disabled={!popoutUrl || showScreenshotInsteadOfSnapshot} onClick={() => { - if (!openPage) - openPage = window.open; - const win = openPage(popoutUrl || '', '_blank'); - win?.addEventListener('DOMContentLoaded', () => { - const injectedScript = new InjectedScript(win as any, false, sdkLanguage, testIdAttributeName, 1, 'chromium', []); - new ConsoleAPI(injectedScript); - }); - }}></ToolbarButton> - </Toolbar> - <div ref={ref} className='snapshot-wrapper'> - <div className='snapshot-container' style={{ - width: snapshotContainerSize.width + 'px', - height: snapshotContainerSize.height + 'px', - transform: `translate(${translate.x}px, ${translate.y}px) scale(${scale})`, - }}> - <BrowserFrame url={snapshotInfo.url} /> - {(showScreenshotInsteadOfSnapshot && screencastFrame) && ( - <> - {point && <ClickPointer point={point} />} - <img alt={`Screenshot of ${action?.apiName} > ${renderTitle(snapshotTab)}`} src={`sha1/${screencastFrame.sha1}`} width={screencastFrame.width} height={screencastFrame.height} /> - </> - )} - <div className='snapshot-switcher' style={showScreenshotInsteadOfSnapshot ? { display: 'none' } : undefined}> - <iframe ref={iframeRef0} name='snapshot' title='DOM Snapshot' className={clsx(loadingRef.current.visibleIframe === 0 && 'snapshot-visible')}></iframe> - <iframe ref={iframeRef1} name='snapshot' title='DOM Snapshot' className={clsx(loadingRef.current.visibleIframe === 1 && 'snapshot-visible')}></iframe> - </div> + <SnapshotWrapper snapshotInfo={snapshotInfo}> + <div className='snapshot-switcher'> + <iframe ref={iframeRef0} name='snapshot' title='DOM Snapshot' className={clsx(loadingRef.current.visibleIframe === 0 && 'snapshot-visible')}></iframe> + <iframe ref={iframeRef1} name='snapshot' title='DOM Snapshot' className={clsx(loadingRef.current.visibleIframe === 1 && 'snapshot-visible')}></iframe> </div> + </SnapshotWrapper> + </div>; +}; + +export const ScreenshotView: React.FunctionComponent<{ + action: ActionTraceEvent | undefined, + snapshotUrls: SnapshotUrls | undefined, + snapshot: Snapshot | undefined, +}> = ({ action, snapshotUrls, snapshot }) => { + const [snapshotInfo, setSnapshotInfo] = React.useState<SnapshotInfo>({ viewport: kDefaultViewport, url: '' }); + React.useEffect(() => { + fetchSnapshotInfo(snapshotUrls?.snapshotInfoUrl).then(setSnapshotInfo); + }, [snapshotUrls?.snapshotInfoUrl]); + + const page = action ? pageForAction(action) : undefined; + const screencastFrame = React.useMemo(() => { + if (snapshotInfo.wallTime && page?.screencastFrames[0]?.frameSwapWallTime) + return findClosest(page.screencastFrames, frame => frame.frameSwapWallTime!, snapshotInfo.wallTime); + + if (snapshotInfo.timestamp && page?.screencastFrames) + return findClosest(page.screencastFrames, frame => frame.timestamp, snapshotInfo.timestamp); + }, + [page?.screencastFrames, snapshotInfo.timestamp, snapshotInfo.wallTime]); + + const point = snapshot?.point; + + return <SnapshotWrapper snapshotInfo={snapshotInfo}> + {screencastFrame && ( + <> + {point && <ClickPointer point={point} />} + <img alt={`Screenshot of ${action?.apiName}`} src={`sha1/${screencastFrame.sha1}`} width={screencastFrame.width} height={screencastFrame.height} /> + </> + )} + </SnapshotWrapper>; +}; + +const SnapshotWrapper: React.FunctionComponent<React.PropsWithChildren<{ + snapshotInfo: SnapshotInfo, +}>> = ({ snapshotInfo, children }) => { + const [measure, ref] = useMeasure<HTMLDivElement>(); + + const windowHeaderHeight = 40; + const snapshotContainerSize = { + width: snapshotInfo.viewport.width, + height: snapshotInfo.viewport.height + windowHeaderHeight, + }; + + const scale = Math.min(measure.width / snapshotContainerSize.width, measure.height / snapshotContainerSize.height, 1); + const translate = { + x: (measure.width - snapshotContainerSize.width) / 2, + y: (measure.height - snapshotContainerSize.height) / 2, + }; + + return <div ref={ref} className='snapshot-wrapper'> + <div className='snapshot-container' style={{ + width: snapshotContainerSize.width + 'px', + height: snapshotContainerSize.height + 'px', + transform: `translate(${translate.x}px, ${translate.y}px) scale(${scale})`, + }}> + <BrowserFrame url={snapshotInfo.url} /> + {children} </div> </div>; }; @@ -325,5 +325,90 @@ function createRecorders(recorders: { recorder: Recorder, frameSelector: string } } -const kDefaultViewport = { width: 1280, height: 720 }; +export type Snapshot = { + action: ActionTraceEvent; + snapshotName: string; + point?: { x: number, y: number }; + hasInputTarget?: boolean; +}; + +export type SnapshotInfo = { + url: string; + viewport: { width: number, height: number }; + timestamp?: number; + wallTime?: undefined; +}; + +export type Snapshots = { + action?: Snapshot; + before?: Snapshot; + after?: Snapshot; +}; + +export type SnapshotUrls = { + snapshotInfoUrl: string; + snapshotUrl: string; + popoutUrl: string; +}; + +export function collectSnapshots(action: ActionTraceEvent | undefined): Snapshots { + if (!action) + return {}; + + // if the action has no beforeSnapshot, use the last available afterSnapshot. + let beforeSnapshot: Snapshot | undefined = action.beforeSnapshot ? { action, snapshotName: action.beforeSnapshot } : undefined; + let a = action; + while (!beforeSnapshot && a) { + a = prevInList(a); + beforeSnapshot = a?.afterSnapshot ? { action: a, snapshotName: a?.afterSnapshot } : undefined; + } + const afterSnapshot: Snapshot | undefined = action.afterSnapshot ? { action, snapshotName: action.afterSnapshot } : beforeSnapshot; + const actionSnapshot: Snapshot | undefined = action.inputSnapshot ? { action, snapshotName: action.inputSnapshot, hasInputTarget: true } : afterSnapshot; + if (actionSnapshot) + actionSnapshot.point = action.point; + return { action: actionSnapshot, before: beforeSnapshot, after: afterSnapshot }; +} + +export function extendSnapshot(snapshot: Snapshot): SnapshotUrls { + const params = new URLSearchParams(); + params.set('trace', context(snapshot.action).traceUrl); + params.set('name', snapshot.snapshotName); + if (snapshot.point) { + params.set('pointX', String(snapshot.point.x)); + params.set('pointY', String(snapshot.point.y)); + if (snapshot.hasInputTarget) + params.set('hasInputTarget', '1'); + } + const snapshotUrl = new URL(`snapshot/${snapshot.action.pageId}?${params.toString()}`, window.location.href).toString(); + const snapshotInfoUrl = new URL(`snapshotInfo/${snapshot.action.pageId}?${params.toString()}`, window.location.href).toString(); + + const popoutParams = new URLSearchParams(); + popoutParams.set('r', snapshotUrl); + popoutParams.set('trace', context(snapshot.action).traceUrl); + if (snapshot.point) { + popoutParams.set('pointX', String(snapshot.point.x)); + popoutParams.set('pointY', String(snapshot.point.y)); + if (snapshot.hasInputTarget) + params.set('hasInputTarget', '1'); + } + const popoutUrl = new URL(`snapshot.html?${popoutParams.toString()}`, window.location.href).toString(); + return { snapshotInfoUrl, snapshotUrl, popoutUrl }; +} + +export async function fetchSnapshotInfo(snapshotInfoUrl: string | undefined) { + const result = { url: '', viewport: kDefaultViewport, timestamp: undefined, wallTime: undefined }; + if (snapshotInfoUrl) { + const response = await fetch(snapshotInfoUrl); + const info = await response.json(); + if (!info.error) { + result.url = info.url; + result.viewport = info.viewport; + result.timestamp = info.timestamp; + result.wallTime = info.wallTime; + } + } + return result; +} + +export const kDefaultViewport = { width: 1280, height: 720 }; const kBlankSnapshotUrl = 'data:text/html,<body style="background: #ddd"></body>'; diff --git a/packages/trace-viewer/src/ui/sourceTab.tsx b/packages/trace-viewer/src/ui/sourceTab.tsx index ce54b34d53..d130499207 100644 --- a/packages/trace-viewer/src/ui/sourceTab.tsx +++ b/packages/trace-viewer/src/ui/sourceTab.tsx @@ -28,7 +28,7 @@ import { ToolbarButton } from '@web/components/toolbarButton'; import { Toolbar } from '@web/components/toolbar'; export const SourceTab: React.FunctionComponent<{ - stack: StackFrame[] | undefined, + stack?: StackFrame[], stackFrameLocation: 'bottom' | 'right', sources: Map<string, SourceModel>, rootDir?: string, diff --git a/packages/trace-viewer/src/ui/timeline.tsx b/packages/trace-viewer/src/ui/timeline.tsx index 2dfeb9adfe..e2dbfed8c1 100644 --- a/packages/trace-viewer/src/ui/timeline.tsx +++ b/packages/trace-viewer/src/ui/timeline.tsx @@ -17,7 +17,7 @@ import { clsx, msToString, useMeasure } from '@web/uiUtils'; import { GlassPane } from '@web/shared/glassPane'; import * as React from 'react'; -import type { Boundaries } from '../geometry'; +import type { Boundaries } from './geometry'; import { FilmStrip } from './filmStrip'; import type { FilmStripPreviewPoint } from './filmStrip'; import type { ActionTraceEventInContext, MultiTraceModel } from './modelUtil'; diff --git a/packages/trace-viewer/src/ui/uiModeTraceView.tsx b/packages/trace-viewer/src/ui/uiModeTraceView.tsx index c0763fee3d..ed63a2adab 100644 --- a/packages/trace-viewer/src/ui/uiModeTraceView.tsx +++ b/packages/trace-viewer/src/ui/uiModeTraceView.tsx @@ -20,7 +20,7 @@ import '@web/common.css'; import '@web/third_party/vscode/codicon.css'; import type * as reporterTypes from 'playwright/types/testReporter'; import React from 'react'; -import type { ContextEntry } from '../entries'; +import type { ContextEntry } from '../types/entries'; import type { SourceLocation } from './modelUtil'; import { MultiTraceModel } from './modelUtil'; import { Workbench } from './workbench'; diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index a97716bdc4..a5f1af7d99 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -56,9 +56,7 @@ const queryParams = { grepInvert: searchParams.get('grepInvert') || undefined, projects: searchParams.getAll('project'), workers: searchParams.get('workers') || undefined, - timeout: searchParams.has('timeout') ? +searchParams.get('timeout')! : undefined, headed: searchParams.has('headed'), - outputDir: searchParams.get('outputDir') || undefined, updateSnapshots: (searchParams.get('updateSnapshots') as 'all' | 'none' | 'missing' | undefined) || undefined, reporters: searchParams.has('reporter') ? searchParams.getAll('reporter') : undefined, pathSeparator: searchParams.get('pathSeparator') || '/', @@ -102,13 +100,10 @@ export const UIModeView: React.FC<{}> = ({ const onRevealSource = React.useCallback(() => setRevealSource(true), [setRevealSource]); const showTestingOptions = false; - const [singleWorker, setSingleWorker] = React.useState(queryParams.workers === '1'); - const [showBrowser, setShowBrowser] = React.useState(queryParams.headed); - const [updateSnapshots, setUpdateSnapshots] = React.useState(queryParams.updateSnapshots === 'all'); - const [showRouteActions, setShowRouteActions] = useSetting('show-route-actions', true); + const [singleWorker, setSingleWorker] = React.useState(false); + const [showBrowser, setShowBrowser] = React.useState(false); + const [updateSnapshots, setUpdateSnapshots] = React.useState(false); const [darkMode, setDarkMode] = useDarkModeSetting(); - const [showScreenshot, setShowScreenshot] = useSetting('screenshot-instead-of-snapshot', false); - const inputRef = React.useRef<HTMLInputElement>(null); @@ -188,14 +183,12 @@ export const UIModeView: React.FC<{}> = ({ interceptStdio: true, watchTestDirs: true }); - const { status, report } = await testServerConnection.runGlobalSetup({ - outputDir: queryParams.outputDir, - }); + const { status, report } = await testServerConnection.runGlobalSetup({}); teleSuiteUpdater.processGlobalReport(report); if (status !== 'passed') return; - const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args, grep: queryParams.grep, grepInvert: queryParams.grepInvert, outputDir: queryParams.outputDir }); + const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args, grep: queryParams.grep, grepInvert: queryParams.grepInvert }); teleSuiteUpdater.processListReport(result.report); testServerConnection.onReport(params => { @@ -292,13 +285,9 @@ export const UIModeView: React.FC<{}> = ({ grepInvert: queryParams.grepInvert, testIds: [...testIds], projects: [...projectFilters].filter(([_, v]) => v).map(([p]) => p), - // When started with `--workers=1`, the setting allows to undo that. - // Otherwise, fallback to the cli `--workers=X` argument. - workers: singleWorker ? '1' : (queryParams.workers === '1' ? undefined : queryParams.workers), - timeout: queryParams.timeout, - headed: showBrowser, - outputDir: queryParams.outputDir, - updateSnapshots: updateSnapshots ? 'all' : queryParams.updateSnapshots, + ...(singleWorker ? { workers: '1' } : {}), + ...(showBrowser ? { headed: true } : {}), + ...(updateSnapshots ? { updateSnapshots: 'all' } : {}), reporters: queryParams.reporters, trace: 'on', }); @@ -320,7 +309,7 @@ export const UIModeView: React.FC<{}> = ({ commandQueue.current = commandQueue.current.then(async () => { setIsLoading(true); try { - const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args, grep: queryParams.grep, grepInvert: queryParams.grepInvert, outputDir: queryParams.outputDir }); + const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args, grep: queryParams.grep, grepInvert: queryParams.grepInvert }); teleSuiteUpdater.processListReport(result.report); } catch (e) { // eslint-disable-next-line no-console @@ -526,8 +515,6 @@ export const UIModeView: React.FC<{}> = ({ </Toolbar> {settingsVisible && <SettingsView settings={[ { value: darkMode, set: setDarkMode, title: 'Dark mode' }, - { value: showRouteActions, set: setShowRouteActions, title: 'Show route actions' }, - { value: showScreenshot, set: setShowScreenshot, title: 'Show screenshot instead of snapshot' }, ]} />} </div> } diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index e1ce2298ae..f5471ca81c 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -24,9 +24,8 @@ import type { ErrorDescription } from './errorsTab'; import type { ConsoleEntry } from './consoleTab'; import { ConsoleTab, useConsoleTabModel } from './consoleTab'; import type * as modelUtil from './modelUtil'; -import { isRouteAction } from './modelUtil'; import { NetworkTab, useNetworkTabModel } from './networkTab'; -import { SnapshotTab } from './snapshotTab'; +import { SnapshotTabsView } from './snapshotTab'; import { SourceTab } from './sourceTab'; import { TabbedPane } from '@web/components/tabbedPane'; import type { TabbedPaneTabModel } from '@web/components/tabbedPane'; @@ -34,7 +33,7 @@ import { Timeline } from './timeline'; import { MetadataView } from './metadataView'; import { AttachmentsTab } from './attachmentsTab'; import { AnnotationsTab } from './annotationsTab'; -import type { Boundaries } from '../geometry'; +import type { Boundaries } from './geometry'; import { InspectorTab } from './inspectorTab'; import { ToolbarButton } from '@web/components/toolbarButton'; import { useSetting, msToString, clsx } from '@web/uiUtils'; @@ -42,7 +41,6 @@ import type { Entry } from '@trace/har'; import './workbench.css'; import { testStatusIcon, testStatusText } from './testUtils'; import type { UITestStatus } from './testUtils'; -import { SettingsView } from './settingsView'; export const Workbench: React.FunctionComponent<{ model?: modelUtil.MultiTraceModel, @@ -50,6 +48,7 @@ export const Workbench: React.FunctionComponent<{ rootDir?: string, fallbackLocation?: modelUtil.SourceLocation, isLive?: boolean, + hideTimeline?: boolean, status?: UITestStatus, annotations?: { type: string; description?: string; }[]; inert?: boolean, @@ -57,11 +56,10 @@ export const Workbench: React.FunctionComponent<{ onOpenExternally?: (location: modelUtil.SourceLocation) => void, revealSource?: boolean, showSettings?: boolean, -}> = ({ model, showSourcesFirst, rootDir, fallbackLocation, isLive, status, annotations, inert, openPage, onOpenExternally, revealSource, showSettings }) => { +}> = ({ model, showSourcesFirst, rootDir, fallbackLocation, isLive, hideTimeline, status, annotations, inert, openPage, onOpenExternally, revealSource, showSettings }) => { const [selectedCallId, setSelectedCallId] = React.useState<string | undefined>(undefined); const [revealedError, setRevealedError] = React.useState<ErrorDescription | undefined>(undefined); - - const [highlightedAction, setHighlightedAction] = React.useState<modelUtil.ActionTraceEventInContext | undefined>(); + const [highlightedCallId, setHighlightedCallId] = React.useState<string | undefined>(); const [highlightedEntry, setHighlightedEntry] = React.useState<Entry | undefined>(); const [highlightedConsoleMessage, setHighlightedConsoleMessage] = React.useState<ConsoleEntry | undefined>(); const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions'); @@ -70,18 +68,21 @@ export const Workbench: React.FunctionComponent<{ const [highlightedLocator, setHighlightedLocator] = React.useState<string>(''); const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>(); const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom'); - const [showRouteActions, setShowRouteActions] = useSetting('show-route-actions', true); - const [showScreenshot, setShowScreenshot] = useSetting('screenshot-instead-of-snapshot', false); - - const filteredActions = React.useMemo(() => { - return (model?.actions || []).filter(action => showRouteActions || !isRouteAction(action)); - }, [model, showRouteActions]); + const showScreenshot = false; const setSelectedAction = React.useCallback((action: modelUtil.ActionTraceEventInContext | undefined) => { setSelectedCallId(action?.callId); setRevealedError(undefined); }, []); + const highlightedAction = React.useMemo(() => { + return model?.actions.find(a => a.callId === highlightedCallId); + }, [model, highlightedCallId]); + + const setHighlightedAction = React.useCallback((highlightedAction: modelUtil.ActionTraceEventInContext | undefined) => { + setHighlightedCallId(highlightedAction?.callId); + }, []); + const sources = React.useMemo(() => model?.sources || new Map<string, modelUtil.SourceModel>(), [model]); React.useEffect(() => { @@ -113,16 +114,16 @@ export const Workbench: React.FunctionComponent<{ } }, [model, selectedCallId]); - const revealedStack = React.useMemo(() => { - if (revealedError) - return revealedError.stack; - return selectedAction?.stack; - }, [selectedAction, revealedError]); - const activeAction = React.useMemo(() => { return highlightedAction || selectedAction; }, [selectedAction, highlightedAction]); + const revealedStack = React.useMemo(() => { + if (revealedError) + return revealedError.stack; + return activeAction?.stack; + }, [activeAction, revealedError]); + const onActionSelected = React.useCallback((action: modelUtil.ActionTraceEventInContext) => { setSelectedAction(action); setHighlightedAction(undefined); @@ -163,6 +164,7 @@ export const Workbench: React.FunctionComponent<{ id: 'inspector', title: 'Locator', render: () => <InspectorTab + showScreenshot={showScreenshot} sdkLanguage={sdkLanguage} setIsInspecting={setIsInspecting} highlightedLocator={highlightedLocator} @@ -291,7 +293,7 @@ export const Workbench: React.FunctionComponent<{ </div>} <ActionList sdkLanguage={sdkLanguage} - actions={filteredActions} + actions={model?.actions || []} selectedAction={model ? selectedAction : undefined} selectedTime={selectedTime} setSelectedTime={setSelectedTime} @@ -307,17 +309,9 @@ export const Workbench: React.FunctionComponent<{ title: 'Metadata', component: <MetadataView model={model}/> }; - const settingsTab: TabbedPaneTabModel = { - id: 'settings', - title: 'Settings', - component: <SettingsView settings={[ - { value: showRouteActions, set: setShowRouteActions, title: 'Show route actions' }, - { value: showScreenshot, set: setShowScreenshot, title: 'Show screenshot instead of snapshot' } - ]}/>, - }; return <div className='vbox workbench' {...(inert ? { inert: 'true' } : {})}> - <Timeline + {!hideTimeline && <Timeline model={model} consoleEntries={consoleModel.entries} boundaries={boundaries} @@ -328,7 +322,7 @@ export const Workbench: React.FunctionComponent<{ sdkLanguage={sdkLanguage} selectedTime={selectedTime} setSelectedTime={setSelectedTime} - /> + />} <SplitView sidebarSize={250} orientation={sidebarLocation === 'bottom' ? 'vertical' : 'horizontal'} settingName='propertiesSidebar' @@ -337,7 +331,7 @@ export const Workbench: React.FunctionComponent<{ orientation='horizontal' sidebarIsFirst settingName='actionListSidebar' - main={<SnapshotTab + main={<SnapshotTabsView action={activeAction} model={model} sdkLanguage={sdkLanguage} @@ -349,7 +343,7 @@ export const Workbench: React.FunctionComponent<{ openPage={openPage} />} sidebar={ <TabbedPane - tabs={showSettings ? [actionsTab, metadataTab, settingsTab] : [actionsTab, metadataTab]} + tabs={[actionsTab, metadataTab]} selectedTab={selectedNavigatorTab} setSelectedTab={setSelectedNavigatorTab} /> diff --git a/packages/trace-viewer/src/ui/workbenchLoader.tsx b/packages/trace-viewer/src/ui/workbenchLoader.tsx index 4764b3d6a8..19793d4b57 100644 --- a/packages/trace-viewer/src/ui/workbenchLoader.tsx +++ b/packages/trace-viewer/src/ui/workbenchLoader.tsx @@ -16,7 +16,7 @@ import { ToolbarButton } from '@web/components/toolbarButton'; import * as React from 'react'; -import type { ContextEntry } from '../entries'; +import type { ContextEntry } from '../types/entries'; import { MultiTraceModel } from './modelUtil'; import './workbenchLoader.css'; import { toggleTheme } from '@web/theme'; diff --git a/packages/trace-viewer/vite.sw.config.ts b/packages/trace-viewer/vite.sw.config.ts index c0dba9f124..dc621448b9 100644 --- a/packages/trace-viewer/vite.sw.config.ts +++ b/packages/trace-viewer/vite.sw.config.ts @@ -41,11 +41,11 @@ export default defineConfig({ emptyOutDir: false, rollupOptions: { input: { - sw: path.resolve(__dirname, 'src/sw.ts'), + sw: path.resolve(__dirname, 'src/sw-main.ts'), }, output: { - entryFileNames: info => '[name].bundle.js', - assetFileNames: () => '[name].[hash][extname]', + entryFileNames: info => 'sw.bundle.js', + assetFileNames: () => 'sw.[hash][extname]', manualChunks: undefined, }, }, diff --git a/packages/web/src/components/sourceChooser.css b/packages/web/src/components/sourceChooser.css new file mode 100644 index 0000000000..60ac74bf85 --- /dev/null +++ b/packages/web/src/components/sourceChooser.css @@ -0,0 +1,20 @@ +/* + 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. +*/ + +.source-chooser { + border: none; + background: none; + outline: none; + color: var(--vscode-sideBarTitle-foreground); + min-width: 100px; +} diff --git a/packages/web/src/components/sourceChooser.tsx b/packages/web/src/components/sourceChooser.tsx new file mode 100644 index 0000000000..0645480a03 --- /dev/null +++ b/packages/web/src/components/sourceChooser.tsx @@ -0,0 +1,58 @@ +/** + * 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. + */ + +import * as React from 'react'; +import type { Source } from '@recorder/recorderTypes'; + +export const SourceChooser: React.FC<{ + sources: Source[], + fileId: string | undefined, + setFileId: (fileId: string) => void, +}> = ({ sources, fileId, setFileId }) => { + return <select className='source-chooser' hidden={!sources.length} value={fileId} onChange={event => { + setFileId(event.target.selectedOptions[0].value); + }}>{renderSourceOptions(sources)}</select>; +}; + +function renderSourceOptions(sources: Source[]): React.ReactNode { + const transformTitle = (title: string): string => title.replace(/.*[/\\]([^/\\]+)/, '$1'); + const renderOption = (source: Source): React.ReactNode => ( + <option key={source.id} value={source.id}>{transformTitle(source.label)}</option> + ); + + const hasGroup = sources.some(s => s.group); + if (hasGroup) { + const groups = new Set(sources.map(s => s.group)); + return [...groups].filter(Boolean).map(group => ( + <optgroup label={group} key={group}> + {sources.filter(s => s.group === group).map(source => renderOption(source))} + </optgroup> + )); + } + + return sources.map(source => renderOption(source)); +} + +export function emptySource(): Source { + return { + id: 'default', + isRecorded: false, + text: '', + language: 'javascript', + label: '', + highlight: [] + }; +} \ No newline at end of file diff --git a/packages/web/src/components/tabbedPane.tsx b/packages/web/src/components/tabbedPane.tsx index b9872294df..5df94ec4c3 100644 --- a/packages/web/src/components/tabbedPane.tsx +++ b/packages/web/src/components/tabbedPane.tsx @@ -32,11 +32,13 @@ export const TabbedPane: React.FunctionComponent<{ tabs: TabbedPaneTabModel[], leftToolbar?: React.ReactElement[], rightToolbar?: React.ReactElement[], - selectedTab: string, - setSelectedTab: (tab: string) => void, + selectedTab?: string, + setSelectedTab?: (tab: string) => void, dataTestId?: string, mode?: 'default' | 'select', }> = ({ tabs, selectedTab, setSelectedTab, leftToolbar, rightToolbar, dataTestId, mode }) => { + if (!selectedTab) + selectedTab = tabs[0].id; if (!mode) mode = 'default'; return <div className='tabbed-pane' data-testid={dataTestId}> @@ -60,7 +62,7 @@ export const TabbedPane: React.FunctionComponent<{ </div>} {mode === 'select' && <div style={{ flex: 'auto', display: 'flex', height: '100%', overflow: 'hidden' }}> <select style={{ width: '100%', background: 'none', cursor: 'pointer' }} onChange={e => { - setSelectedTab(tabs[e.currentTarget.selectedIndex].id); + setSelectedTab?.(tabs[e.currentTarget.selectedIndex].id); }}> {tabs.map(tab => { let suffix = ''; @@ -95,10 +97,10 @@ export const TabbedPaneTab: React.FunctionComponent<{ count?: number, errorCount?: number, selected?: boolean, - onSelect: (id: string) => void + onSelect?: (id: string) => void }> = ({ id, title, count, errorCount, selected, onSelect }) => { return <div className={clsx('tabbed-pane-tab', selected && 'selected')} - onClick={() => onSelect(id)} + onClick={() => onSelect?.(id)} title={title} key={id}> <div className='tabbed-pane-tab-label'>{title}</div> diff --git a/packages/web/src/components/toolbar.css b/packages/web/src/components/toolbar.css index 8c58f96f65..8bebf79c62 100644 --- a/packages/web/src/components/toolbar.css +++ b/packages/web/src/components/toolbar.css @@ -24,6 +24,10 @@ padding-right: 4px; } +.toolbar.toolbar-sidebar-background { + background-color: var(--vscode-sideBar-background); +} + .toolbar:after { content: ''; display: block; diff --git a/packages/web/src/components/toolbar.tsx b/packages/web/src/components/toolbar.tsx index c7dd6843d9..a81b834f4a 100644 --- a/packages/web/src/components/toolbar.tsx +++ b/packages/web/src/components/toolbar.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; type ToolbarProps = { noShadow?: boolean; noMinHeight?: boolean; + sidebarBackground?: boolean; className?: string; onClick?: (e: React.MouseEvent) => void; }; @@ -30,7 +31,8 @@ export const Toolbar: React.FC<React.PropsWithChildren<ToolbarProps>> = ({ children, noMinHeight, className, + sidebarBackground, onClick, }) => { - return <div className={clsx('toolbar', noShadow && 'no-shadow', noMinHeight && 'no-min-height', className)} onClick={onClick}>{children}</div>; + return <div className={clsx('toolbar', noShadow && 'no-shadow', noMinHeight && 'no-min-height', className, sidebarBackground && 'toolbar-sidebar-background')} onClick={onClick}>{children}</div>; }; diff --git a/packages/web/src/components/toolbarButton.tsx b/packages/web/src/components/toolbarButton.tsx index 455f0979f0..00b9babd59 100644 --- a/packages/web/src/components/toolbarButton.tsx +++ b/packages/web/src/components/toolbarButton.tsx @@ -17,6 +17,7 @@ import './toolbarButton.css'; import '../third_party/vscode/codicon.css'; import * as React from 'react'; +import { clsx } from '@web/uiUtils'; export interface ToolbarButtonProps { title: string, @@ -40,11 +41,8 @@ export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps> testId, className, }) => { - className = (className || '') + ` toolbar-button ${icon}`; - if (toggled) - className += ' toggled'; return <button - className={className} + className={clsx(className, 'toolbar-button', icon, toggled && 'toggled')} onMouseDown={preventDefault} onClick={onClick} onDoubleClick={preventDefault} diff --git a/packages/web/src/uiUtils.ts b/packages/web/src/uiUtils.ts index 49459fe29c..2697177c6f 100644 --- a/packages/web/src/uiUtils.ts +++ b/packages/web/src/uiUtils.ts @@ -162,7 +162,7 @@ export function useSetting<S>(name: string | undefined, defaultValue: S): [S, Re declare global { interface Window { - saveSettings?(): Promise<void>; + saveSettings?(): void; } } @@ -203,5 +203,10 @@ export function clsx(...classes: (string | undefined | false)[]) { return classes.filter(Boolean).join(' '); } +export async function sha1(str: string): Promise<string> { + const buffer = new TextEncoder().encode(str); + return Array.from(new Uint8Array(await crypto.subtle.digest('SHA-1', buffer))).map(b => b.toString(16).padStart(2, '0')).join(''); +} + const kControlCodesRe = '\\u0000-\\u0020\\u007f-\\u009f'; export const kWebLinkRe = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|www\\.)[^\\s' + kControlCodesRe + '"]{2,}[^\\s' + kControlCodesRe + '"\')}\\],:;.!?]', 'ug'); diff --git a/tests/android/androidTest.ts b/tests/android/androidTest.ts index 7bfff3fbf3..e81e4a401f 100644 --- a/tests/android/androidTest.ts +++ b/tests/android/androidTest.ts @@ -19,23 +19,32 @@ import type { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi'; import type { AndroidDevice, BrowserContext } from 'playwright-core'; export { expect } from '@playwright/test'; -type AndroidWorkerFixtures = PageWorkerFixtures & { +type AndroidTestFixtures = { androidDevice: AndroidDevice; +}; + +type AndroidWorkerFixtures = PageWorkerFixtures & { + androidDeviceWorker: AndroidDevice; androidContext: BrowserContext; }; -export const androidTest = baseTest.extend<PageTestFixtures, AndroidWorkerFixtures>({ - androidDevice: [async ({ playwright }, run) => { +async function closeAllActivities(device: AndroidDevice) { + await device.shell('am force-stop com.google.android.googlequicksearchbox'); + await device.shell('am force-stop org.chromium.webview_shell'); + await device.shell('am force-stop com.android.chrome'); +} + +export const androidTest = baseTest.extend<PageTestFixtures & AndroidTestFixtures, AndroidWorkerFixtures>({ + androidDeviceWorker: [async ({ playwright }, run) => { const device = (await playwright._android.devices())[0]; - await device.shell('am force-stop org.chromium.webview_shell'); - await device.shell('am force-stop com.android.chrome'); + await closeAllActivities(device); device.setDefaultTimeout(90000); await run(device); await device.close(); }, { scope: 'worker' }], - browserVersion: [async ({ androidDevice }, run) => { - const browserVersion = (await androidDevice.shell('dumpsys package com.android.chrome')) + browserVersion: [async ({ androidDeviceWorker }, run) => { + const browserVersion = (await androidDeviceWorker.shell('dumpsys package com.android.chrome')) .toString('utf8') .split('\n') .find(line => line.includes('versionName='))! @@ -53,8 +62,14 @@ export const androidTest = baseTest.extend<PageTestFixtures, AndroidWorkerFixtur electronMajorVersion: [0, { scope: 'worker' }], isWebView2: [false, { scope: 'worker' }], - androidContext: [async ({ androidDevice }, run) => { - const context = await androidDevice.launchBrowser(); + androidDevice: async ({ androidDeviceWorker }, use) => { + await closeAllActivities(androidDeviceWorker); + await use(androidDeviceWorker); + await closeAllActivities(androidDeviceWorker); + }, + + androidContext: [async ({ androidDeviceWorker }, run) => { + const context = await androidDeviceWorker.launchBrowser(); const [page] = context.pages(); await page.goto('data:text/html,Default page'); await run(context); diff --git a/tests/android/browser.spec.ts b/tests/android/browser.spec.ts index d1b54206b9..61595a8795 100644 --- a/tests/android/browser.spec.ts +++ b/tests/android/browser.spec.ts @@ -17,10 +17,6 @@ import fs from 'fs'; import { androidTest as test, expect } from './androidTest'; -test.afterAll(async ({ androidDevice }) => { - await androidDevice.shell('am force-stop com.android.chrome'); -}); - test('androidDevice.model', async function({ androidDevice }) { expect(androidDevice.model()).toContain('sdk_gphone'); expect(androidDevice.model()).toContain('x86_64'); diff --git a/tests/android/device.spec.ts b/tests/android/device.spec.ts index 1288e7320b..8fed656450 100644 --- a/tests/android/device.spec.ts +++ b/tests/android/device.spec.ts @@ -15,7 +15,6 @@ */ import fs from 'fs'; -import { join } from 'path'; import { PNG } from 'playwright-core/lib/utilsBundle'; import { androidTest as test, expect } from './androidTest'; @@ -55,40 +54,7 @@ test('androidDevice.push', async function({ androidDevice }) { }); test('androidDevice.fill', async function({ androidDevice }) { - test.fixme(true, 'Hangs on the bots'); - await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); await androidDevice.fill({ res: 'org.chromium.webview_shell:id/url_field' }, 'Hello'); expect((await androidDevice.info({ res: 'org.chromium.webview_shell:id/url_field' })).text).toBe('Hello'); }); - -test('androidDevice.options.omitDriverInstall', async function({ playwright }) { - test.skip(true, 'Android._driverPromise gets cached and is in a closed state. Its stored inside the androidDevice worker fixture.'); - const devices = await playwright._android.devices({ omitDriverInstall: true }); - - const androidDevice = devices[0]; - await androidDevice.shell(`cmd package uninstall com.microsoft.playwright.androiddriver`); - await androidDevice.shell(`cmd package uninstall com.microsoft.playwright.androiddriver.test`); - - await androidDevice.shell('am start -a android.intent.action.VIEW -d about:blank com.android.chrome'); - - let fillStatus = ''; - androidDevice.fill({ res: 'com.android.chrome:id/url_bar' }, 'Hello').then(() => { - fillStatus = 'success'; - }).catch(() => { - fillStatus = 'error'; - }); - - // install and start driver - for (const file of ['android-driver.apk', 'android-driver-target.apk']) { - const filePath = join(require.resolve('playwright-core'), '..', 'bin', file); - await androidDevice.installApk(await fs.promises.readFile(filePath)); - } - androidDevice.shell('am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner').catch(e => console.error(e)); - - // wait for finishing fill operation - while (!fillStatus) - await new Promise(f => setTimeout(f, 200)); - - expect(fillStatus).toBe('success'); -}); diff --git a/tests/android/webview.spec.ts b/tests/android/webview.spec.ts index ca7a114bb1..650f8f299c 100644 --- a/tests/android/webview.spec.ts +++ b/tests/android/webview.spec.ts @@ -16,17 +16,7 @@ import { androidTest as test, expect } from './androidTest'; -test.beforeEach(async ({ androidDevice }) => { - await androidDevice.shell('am force-stop com.google.android.googlequicksearchbox'); -}); - -test.afterEach(async ({ androidDevice }) => { - await androidDevice.shell('am force-stop org.chromium.webview_shell'); - await androidDevice.shell('am force-stop com.android.chrome'); -}); - test('androidDevice.webView', async function({ androidDevice }) { - test.slow(); expect(androidDevice.webViews().length).toBe(0); await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' }); @@ -52,8 +42,6 @@ test('should navigate page internally', async function({ androidDevice }) { }); test('should navigate page externally', async function({ androidDevice }) { - test.fixme(true, 'Hangs on the bots'); - expect(androidDevice.webViews().length).toBe(0); await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' }); @@ -68,7 +56,6 @@ test('should navigate page externally', async function({ androidDevice }) { }); test('select webview from socketName', async function({ androidDevice }) { - test.slow(); const context = await androidDevice.launchBrowser(); const newPage = await context.newPage(); await newPage.goto('about:blank'); diff --git a/tests/assets/extension-with-logging/background.js b/tests/assets/extension-with-logging/background.js new file mode 100644 index 0000000000..3780fa3f21 --- /dev/null +++ b/tests/assets/extension-with-logging/background.js @@ -0,0 +1,5 @@ +console.log("Service worker script loaded"); + +chrome.runtime.onInstalled.addListener(() => { + console.log("Extension installed"); +}); \ No newline at end of file diff --git a/tests/assets/extension-with-logging/content.js b/tests/assets/extension-with-logging/content.js new file mode 100644 index 0000000000..e718206c2a --- /dev/null +++ b/tests/assets/extension-with-logging/content.js @@ -0,0 +1 @@ +console.log("Test console log from a third-party execution context"); diff --git a/tests/assets/extension-with-logging/manifest.json b/tests/assets/extension-with-logging/manifest.json new file mode 100644 index 0000000000..429dc77980 --- /dev/null +++ b/tests/assets/extension-with-logging/manifest.json @@ -0,0 +1,17 @@ +{ + "manifest_version": 3, + "name": "Console Log Extension", + "version": "1.0", + "background": { + "service_worker": "background.js" + }, + "permissions": [ + "tabs" + ], + "content_scripts": [ + { + "matches": ["<all_urls>"], + "js": ["content.js"] + } + ] + } \ No newline at end of file diff --git a/tests/assets/input/background-fixed.html b/tests/assets/input/background-fixed.html new file mode 100644 index 0000000000..5093939a5b --- /dev/null +++ b/tests/assets/input/background-fixed.html @@ -0,0 +1,16 @@ +<head> +<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5"> +<style> + +body { + background: #fff url("/pptr.png") no-repeat center 30px; + background-attachment: fixed; + margin-top: 36px; +} + +</style> +</head> +<body> + <div style="height: 3000px; background-color: antiquewhite;">tall</div> + <button>Click me</button> +</body> diff --git a/tests/assets/rgb.avif b/tests/assets/rgb.avif new file mode 100644 index 0000000000..d79bff8a21 Binary files /dev/null and b/tests/assets/rgb.avif differ diff --git a/tests/assets/trace-remote-time-diff.zip b/tests/assets/trace-remote-time-diff.zip new file mode 100644 index 0000000000..e7d7c54c35 Binary files /dev/null and b/tests/assets/trace-remote-time-diff.zip differ diff --git a/tests/bidi/expectations/bidi-firefox-nightly-library.txt b/tests/bidi/expectations/bidi-firefox-nightly-library.txt new file mode 100644 index 0000000000..21ba52fea1 --- /dev/null +++ b/tests/bidi/expectations/bidi-firefox-nightly-library.txt @@ -0,0 +1,1949 @@ +library/beforeunload.spec.ts › should access page after beforeunload [fail] +library/beforeunload.spec.ts › should be able to navigate away from page with beforeunload [fail] +library/beforeunload.spec.ts › should close browser with beforeunload page [pass] +library/beforeunload.spec.ts › should close browsercontext with beforeunload page [pass] +library/beforeunload.spec.ts › should close page with beforeunload listener [pass] +library/beforeunload.spec.ts › should not stall on evaluate when dismissing beforeunload [fail] +library/beforeunload.spec.ts › should run beforeunload if asked for @smoke [fail] +library/browser.spec.ts › should create new page @smoke [pass] +library/browser.spec.ts › should dispatch page.on(close) upon browser.close and reject evaluate [pass] +library/browser.spec.ts › should return browserType [pass] +library/browser.spec.ts › should throw upon second create new page [pass] +library/browser.spec.ts › version should work [pass] +library/browsercontext-add-cookies.spec.ts › should add cookies with empty value [pass] +library/browsercontext-add-cookies.spec.ts › should allow unnamed cookies [fail] +library/browsercontext-add-cookies.spec.ts › should be able to set unsecure cookie for HTTP website [pass] +library/browsercontext-add-cookies.spec.ts › should default to setting secure cookie for HTTPS websites [pass] +library/browsercontext-add-cookies.spec.ts › should have |expires| set to |-1| for session cookies [pass] +library/browsercontext-add-cookies.spec.ts › should isolate cookies between launches [pass] +library/browsercontext-add-cookies.spec.ts › should isolate cookies in browser contexts [pass] +library/browsercontext-add-cookies.spec.ts › should isolate persistent cookies [pass] +library/browsercontext-add-cookies.spec.ts › should isolate send cookie header [pass] +library/browsercontext-add-cookies.spec.ts › should isolate session cookies [pass] +library/browsercontext-add-cookies.spec.ts › should not block third party SameSite=None cookies [fail] +library/browsercontext-add-cookies.spec.ts › should not set a cookie on a data URL page [pass] +library/browsercontext-add-cookies.spec.ts › should not set a cookie with blank page URL [pass] +library/browsercontext-add-cookies.spec.ts › should roundtrip cookie [pass] +library/browsercontext-add-cookies.spec.ts › should send cookie header [pass] +library/browsercontext-add-cookies.spec.ts › should set a cookie on a different domain [pass] +library/browsercontext-add-cookies.spec.ts › should set a cookie with a path [pass] +library/browsercontext-add-cookies.spec.ts › should set cookie with reasonable defaults [pass] +library/browsercontext-add-cookies.spec.ts › should set cookies for a frame [fail] +library/browsercontext-add-cookies.spec.ts › should set multiple cookies [pass] +library/browsercontext-add-cookies.spec.ts › should set secure cookies on secure WebSocket [fail] +library/browsercontext-add-cookies.spec.ts › should work @smoke [pass] +library/browsercontext-add-cookies.spec.ts › should work with expires=-1 [pass] +library/browsercontext-add-cookies.spec.ts › should(not) block third party cookies [pass] +library/browsercontext-add-init-script.spec.ts › should work with browser context scripts @smoke [pass] +library/browsercontext-add-init-script.spec.ts › should work with browser context scripts for already created pages [pass] +library/browsercontext-add-init-script.spec.ts › should work with browser context scripts with a path [pass] +library/browsercontext-add-init-script.spec.ts › should work without navigation in popup [fail] +library/browsercontext-add-init-script.spec.ts › should work without navigation, after all bindings [pass] +library/browsercontext-base-url.spec.ts › should be able to match a URL relative to its given URL with urlMatcher [fail] +library/browsercontext-base-url.spec.ts › should construct a new URL when a baseURL in browser.newContext is passed to page.goto @smoke [pass] +library/browsercontext-base-url.spec.ts › should construct a new URL when a baseURL in browser.newPage is passed to page.goto [pass] +library/browsercontext-base-url.spec.ts › should construct a new URL when a baseURL in browserType.launchPersistentContext is passed to page.goto [pass] +library/browsercontext-base-url.spec.ts › should construct the URLs correctly when a baseURL with a trailing slash in browser.newPage is passed to page.goto [pass] +library/browsercontext-base-url.spec.ts › should construct the URLs correctly when a baseURL without a trailing slash in browser.newPage is passed to page.goto [pass] +library/browsercontext-base-url.spec.ts › should not construct a new URL when valid URLs are passed [pass] +library/browsercontext-base-url.spec.ts › should not construct a new URL with baseURL when a glob was used [pass] +library/browsercontext-basic.spec.ts › close() should abort waitForEvent [pass] +library/browsercontext-basic.spec.ts › close() should be callable twice [pass] +library/browsercontext-basic.spec.ts › close() should work for empty context [pass] +library/browsercontext-basic.spec.ts › default user agent [pass] +library/browsercontext-basic.spec.ts › setContent should work after disabling javascript [fail] +library/browsercontext-basic.spec.ts › should be able to click across browser contexts [fail] +library/browsercontext-basic.spec.ts › should be able to navigate after disabling javascript [pass] +library/browsercontext-basic.spec.ts › should close all belonging pages once closing context [fail] +library/browsercontext-basic.spec.ts › should create new context @smoke [pass] +library/browsercontext-basic.spec.ts › should disable javascript [fail] +library/browsercontext-basic.spec.ts › should emulate media in cross-process iframe [fail] +library/browsercontext-basic.spec.ts › should emulate media in popup [fail] +library/browsercontext-basic.spec.ts › should emulate navigator.onLine [fail] +library/browsercontext-basic.spec.ts › should isolate localStorage and cookies @smoke [pass] +library/browsercontext-basic.spec.ts › should make a copy of default viewport [pass] +library/browsercontext-basic.spec.ts › should not allow deviceScaleFactor with null viewport [pass] +library/browsercontext-basic.spec.ts › should not allow isMobile with null viewport [pass] +library/browsercontext-basic.spec.ts › should not hang on promises after disabling javascript [pass] +library/browsercontext-basic.spec.ts › should not report frameless pages on error [pass] +library/browsercontext-basic.spec.ts › should pass self to close event [pass] +library/browsercontext-basic.spec.ts › should propagate default viewport to the page [pass] +library/browsercontext-basic.spec.ts › should respect deviceScaleFactor [pass] +library/browsercontext-basic.spec.ts › should return all of the pages [pass] +library/browsercontext-basic.spec.ts › should work with offline option [fail] +library/browsercontext-basic.spec.ts › window.open should use parent tab context [pass] +library/browsercontext-clearcookies.spec.ts › should clear cookies [pass] +library/browsercontext-clearcookies.spec.ts › should isolate cookies when clearing [pass] +library/browsercontext-clearcookies.spec.ts › should remove cookies by domain [pass] +library/browsercontext-clearcookies.spec.ts › should remove cookies by name [pass] +library/browsercontext-clearcookies.spec.ts › should remove cookies by name and domain [pass] +library/browsercontext-clearcookies.spec.ts › should remove cookies by name regex [pass] +library/browsercontext-clearcookies.spec.ts › should remove cookies by path [pass] +library/browsercontext-cookies.spec.ts › should add cookies with an expiration [pass] +library/browsercontext-cookies.spec.ts › should be able to send third party cookies via an iframe [fail] +library/browsercontext-cookies.spec.ts › should get a cookie @smoke [pass] +library/browsercontext-cookies.spec.ts › should get a non-session cookie [pass] +library/browsercontext-cookies.spec.ts › should get cookies from multiple urls [pass] +library/browsercontext-cookies.spec.ts › should get multiple cookies [pass] +library/browsercontext-cookies.spec.ts › should parse cookie with large Max-Age correctly [pass] +library/browsercontext-cookies.spec.ts › should properly report "Lax" sameSite cookie [pass] +library/browsercontext-cookies.spec.ts › should properly report "Strict" sameSite cookie [pass] +library/browsercontext-cookies.spec.ts › should properly report httpOnly cookie [pass] +library/browsercontext-cookies.spec.ts › should return cookies with empty value [pass] +library/browsercontext-cookies.spec.ts › should return no cookies in pristine browser context [pass] +library/browsercontext-cookies.spec.ts › should return secure cookies based on HTTP(S) protocol [pass] +library/browsercontext-cookies.spec.ts › should support requestStorageAccess [fail] +library/browsercontext-cookies.spec.ts › should work with subdomain cookie [pass] +library/browsercontext-credentials.spec.ts › should fail with correct credentials and mismatching hostname [pass] +library/browsercontext-credentials.spec.ts › should fail with correct credentials and mismatching port [pass] +library/browsercontext-credentials.spec.ts › should fail with correct credentials and mismatching scheme [pass] +library/browsercontext-credentials.spec.ts › should fail with wrong credentials [timeout] +library/browsercontext-credentials.spec.ts › should fail without credentials [timeout] +library/browsercontext-credentials.spec.ts › should return resource body [fail] +library/browsercontext-credentials.spec.ts › should work with correct credentials @smoke [fail] +library/browsercontext-credentials.spec.ts › should work with correct credentials and matching origin [fail] +library/browsercontext-credentials.spec.ts › should work with correct credentials and matching origin case insensitive [fail] +library/browsercontext-credentials.spec.ts › should work with setHTTPCredentials [timeout] +library/browsercontext-csp.spec.ts › should bypass CSP header [fail] +library/browsercontext-csp.spec.ts › should bypass CSP in iframes as well [fail] +library/browsercontext-csp.spec.ts › should bypass CSP meta tag @smoke [fail] +library/browsercontext-csp.spec.ts › should bypass after cross-process navigation [fail] +library/browsercontext-device.spec.ts › device › should emulate viewport and screen size [fail] +library/browsercontext-device.spec.ts › device › should emulate viewport without screen size [fail] +library/browsercontext-device.spec.ts › device › should reset scroll top after a navigation [pass] +library/browsercontext-device.spec.ts › device › should scroll to a precise position with mobile scale [pass] +library/browsercontext-device.spec.ts › device › should scroll to click [pass] +library/browsercontext-device.spec.ts › device › should scroll twice when emulated [fail] +library/browsercontext-device.spec.ts › device › should support clicking [pass] +library/browsercontext-device.spec.ts › device › should work @smoke [fail] +library/browsercontext-dsf.spec.ts › should fetch hidpi assets [fail] +library/browsercontext-dsf.spec.ts › should fetch lodpi assets @smoke [pass] +library/browsercontext-events.spec.ts › console event should work @smoke [pass] +library/browsercontext-events.spec.ts › console event should work in immediately closed popup [fail] +library/browsercontext-events.spec.ts › console event should work in popup [pass] +library/browsercontext-events.spec.ts › console event should work in popup 2 [fail] +library/browsercontext-events.spec.ts › dialog event should work @smoke [pass] +library/browsercontext-events.spec.ts › dialog event should work in immediately closed popup [timeout] +library/browsercontext-events.spec.ts › dialog event should work in popup [timeout] +library/browsercontext-events.spec.ts › dialog event should work in popup 2 [pass] +library/browsercontext-events.spec.ts › dialog event should work with inline script tag [fail] +library/browsercontext-events.spec.ts › weberror event should work [fail] +library/browsercontext-expose-function.spec.ts › expose binding should work [pass] +library/browsercontext-expose-function.spec.ts › exposeBindingHandle should work [pass] +library/browsercontext-expose-function.spec.ts › should be callable from-inside addInitScript [pass] +library/browsercontext-expose-function.spec.ts › should throw for duplicate registrations [pass] +library/browsercontext-expose-function.spec.ts › should work [fail] +library/browsercontext-expose-function.spec.ts › should work with CSP [fail] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › br decompression › should not fail if response content-length header is missing (br) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › br decompression › should not fail with an empty response with content-length header (Z_BUF_ERROR) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › br decompression › should not fail with an empty response without content-length header (Z_BUF_ERROR) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › br decompression › should not fail with chunked responses (without Content-Length header) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › br decompression › should support decompression [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › deflate decompression › should not fail if response content-length header is missing (deflate) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › deflate decompression › should not fail with an empty response with content-length header (Z_BUF_ERROR) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › deflate decompression › should not fail with an empty response without content-length header (Z_BUF_ERROR) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › deflate decompression › should not fail with chunked responses (without Content-Length header) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › deflate decompression › should support decompression [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › gzip decompression › should not fail if response content-length header is missing (gzip) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › gzip decompression › should not fail with an empty response with content-length header (Z_BUF_ERROR) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › gzip decompression › should not fail with an empty response without content-length header (Z_BUF_ERROR) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › gzip decompression › should not fail with chunked responses (without Content-Length header) [pass] +library/browsercontext-fetch-algorithms.spec.ts › algorithms › gzip decompression › should support decompression [pass] +library/browsercontext-fetch-happy-eyeballs.spec.ts › get should work [pass] +library/browsercontext-fetch-happy-eyeballs.spec.ts › get should work on request fixture [pass] +library/browsercontext-fetch-happy-eyeballs.spec.ts › https post should work with ignoreHTTPSErrors option [pass] +library/browsercontext-fetch-happy-eyeballs.spec.ts › should work with ip6 and port as the host [pass] +library/browsercontext-fetch.spec.ts › context request should export same storage state as context [pass] +library/browsercontext-fetch.spec.ts › delete should support failOnStatusCode [pass] +library/browsercontext-fetch.spec.ts › delete should support params passed as URLSearchParams [pass] +library/browsercontext-fetch.spec.ts › delete should support params passed as object [pass] +library/browsercontext-fetch.spec.ts › delete should support params passed as string [pass] +library/browsercontext-fetch.spec.ts › delete should support post data [pass] +library/browsercontext-fetch.spec.ts › deleteshould support ignoreHTTPSErrors option [pass] +library/browsercontext-fetch.spec.ts › fetch should not throw on long set-cookie value [pass] +library/browsercontext-fetch.spec.ts › fetch should support failOnStatusCode [pass] +library/browsercontext-fetch.spec.ts › fetch should support params passed as URLSearchParams [pass] +library/browsercontext-fetch.spec.ts › fetch should support params passed as object [pass] +library/browsercontext-fetch.spec.ts › fetch should support params passed as string [pass] +library/browsercontext-fetch.spec.ts › fetch should work [pass] +library/browsercontext-fetch.spec.ts › fetchshould support ignoreHTTPSErrors option [pass] +library/browsercontext-fetch.spec.ts › get should support failOnStatusCode [pass] +library/browsercontext-fetch.spec.ts › get should support params passed as URLSearchParams [pass] +library/browsercontext-fetch.spec.ts › get should support params passed as object [pass] +library/browsercontext-fetch.spec.ts › get should support params passed as string [pass] +library/browsercontext-fetch.spec.ts › get should support post data [pass] +library/browsercontext-fetch.spec.ts › get should work @smoke [pass] +library/browsercontext-fetch.spec.ts › getshould support ignoreHTTPSErrors option [pass] +library/browsercontext-fetch.spec.ts › head should support failOnStatusCode [pass] +library/browsercontext-fetch.spec.ts › head should support params passed as URLSearchParams [pass] +library/browsercontext-fetch.spec.ts › head should support params passed as object [pass] +library/browsercontext-fetch.spec.ts › head should support params passed as string [pass] +library/browsercontext-fetch.spec.ts › head should support post data [pass] +library/browsercontext-fetch.spec.ts › headshould support ignoreHTTPSErrors option [pass] +library/browsercontext-fetch.spec.ts › patch should support failOnStatusCode [pass] +library/browsercontext-fetch.spec.ts › patch should support params passed as URLSearchParams [pass] +library/browsercontext-fetch.spec.ts › patch should support params passed as object [pass] +library/browsercontext-fetch.spec.ts › patch should support params passed as string [pass] +library/browsercontext-fetch.spec.ts › patch should support post data [pass] +library/browsercontext-fetch.spec.ts › patchshould support ignoreHTTPSErrors option [pass] +library/browsercontext-fetch.spec.ts › post should support failOnStatusCode [pass] +library/browsercontext-fetch.spec.ts › post should support params passed as URLSearchParams [pass] +library/browsercontext-fetch.spec.ts › post should support params passed as object [pass] +library/browsercontext-fetch.spec.ts › post should support params passed as string [pass] +library/browsercontext-fetch.spec.ts › post should support post data [pass] +library/browsercontext-fetch.spec.ts › postshould support ignoreHTTPSErrors option [pass] +library/browsercontext-fetch.spec.ts › put should support failOnStatusCode [pass] +library/browsercontext-fetch.spec.ts › put should support params passed as URLSearchParams [pass] +library/browsercontext-fetch.spec.ts › put should support params passed as object [pass] +library/browsercontext-fetch.spec.ts › put should support params passed as string [pass] +library/browsercontext-fetch.spec.ts › put should support post data [pass] +library/browsercontext-fetch.spec.ts › putshould support ignoreHTTPSErrors option [pass] +library/browsercontext-fetch.spec.ts › should abort requests when browser context closes [pass] +library/browsercontext-fetch.spec.ts › should accept bool and numeric params [pass] +library/browsercontext-fetch.spec.ts › should add cookies from Set-Cookie header [pass] +library/browsercontext-fetch.spec.ts › should add default headers [pass] +library/browsercontext-fetch.spec.ts › should add default headers to redirects [pass] +library/browsercontext-fetch.spec.ts › should add session cookies to request [pass] +library/browsercontext-fetch.spec.ts › should allow to override default headers [pass] +library/browsercontext-fetch.spec.ts › should dispose [pass] +library/browsercontext-fetch.spec.ts › should dispose when context closes [pass] +library/browsercontext-fetch.spec.ts › should encode to application/json by default [pass] +library/browsercontext-fetch.spec.ts › should follow redirects [pass] +library/browsercontext-fetch.spec.ts › should follow redirects correctly when Location header contains UTF-8 characters [pass] +library/browsercontext-fetch.spec.ts › should handle cookies on redirects [pass] +library/browsercontext-fetch.spec.ts › should inherit ignoreHTTPSErrors from context [pass] +library/browsercontext-fetch.spec.ts › should not add context cookie if cookie header passed as a parameter [pass] +library/browsercontext-fetch.spec.ts › should not hang on a brotli encoded Range request [pass] +library/browsercontext-fetch.spec.ts › should not lose body while handling Set-Cookie header [pass] +library/browsercontext-fetch.spec.ts › should not work after context dispose [pass] +library/browsercontext-fetch.spec.ts › should not work after dispose [pass] +library/browsercontext-fetch.spec.ts › should override request parameters [pass] +library/browsercontext-fetch.spec.ts › should preserve cookie order from Set-Cookie header [pass] +library/browsercontext-fetch.spec.ts › should propagate custom headers with redirects [pass] +library/browsercontext-fetch.spec.ts › should propagate extra http headers with redirects [fail] +library/browsercontext-fetch.spec.ts › should remove cookie with expires far in the past [pass] +library/browsercontext-fetch.spec.ts › should remove cookie with negative max-age [pass] +library/browsercontext-fetch.spec.ts › should resolve url relative to baseURL [pass] +library/browsercontext-fetch.spec.ts › should respect timeout after redirects [pass] +library/browsercontext-fetch.spec.ts › should retry on ECONNRESET [pass] +library/browsercontext-fetch.spec.ts › should return error with wrong credentials [pass] +library/browsercontext-fetch.spec.ts › should return raw headers [pass] +library/browsercontext-fetch.spec.ts › should send content-length [pass] +library/browsercontext-fetch.spec.ts › should send secure cookie over http for localhost [pass] +library/browsercontext-fetch.spec.ts › should serialize data to json regardless of content-type [pass] +library/browsercontext-fetch.spec.ts › should set domain=localhost cookie [pass] +library/browsercontext-fetch.spec.ts › should support HTTPCredentials.send for browser.newPage [pass] +library/browsercontext-fetch.spec.ts › should support HTTPCredentials.send for newContext [pass] +library/browsercontext-fetch.spec.ts › should support SameSite cookie attribute over https [pass] +library/browsercontext-fetch.spec.ts › should support a timeout of 0 [pass] +library/browsercontext-fetch.spec.ts › should support application/x-www-form-urlencoded [pass] +library/browsercontext-fetch.spec.ts › should support application/x-www-form-urlencoded with param lists [pass] +library/browsercontext-fetch.spec.ts › should support brotli compression [pass] +library/browsercontext-fetch.spec.ts › should support cookie with empty value [pass] +library/browsercontext-fetch.spec.ts › should support deflate compression [pass] +library/browsercontext-fetch.spec.ts › should support gzip compression [pass] +library/browsercontext-fetch.spec.ts › should support https [pass] +library/browsercontext-fetch.spec.ts › should support multipart/form-data [pass] +library/browsercontext-fetch.spec.ts › should support multipart/form-data and keep the order [pass] +library/browsercontext-fetch.spec.ts › should support multipart/form-data with ReadStream values [pass] +library/browsercontext-fetch.spec.ts › should support repeating names in multipart/form-data [unknown] +library/browsercontext-fetch.spec.ts › should support set-cookie with SameSite and without Secure attribute over HTTP [pass] +library/browsercontext-fetch.spec.ts › should support timeout option [pass] +library/browsercontext-fetch.spec.ts › should throw informative error on corrupted brotli body [pass] +library/browsercontext-fetch.spec.ts › should throw informative error on corrupted deflate body [pass] +library/browsercontext-fetch.spec.ts › should throw informative error on corrupted gzip body [pass] +library/browsercontext-fetch.spec.ts › should throw nice error on unsupported data type [pass] +library/browsercontext-fetch.spec.ts › should throw on invalid header value [pass] +library/browsercontext-fetch.spec.ts › should throw on network error [pass] +library/browsercontext-fetch.spec.ts › should throw on network error after redirect [pass] +library/browsercontext-fetch.spec.ts › should throw on network error when sending body [pass] +library/browsercontext-fetch.spec.ts › should throw on network error when sending body after redirect [pass] +library/browsercontext-fetch.spec.ts › should throw on non-http(s) protocol [pass] +library/browsercontext-fetch.spec.ts › should update host header on redirect [pass] +library/browsercontext-fetch.spec.ts › should work with connectOverCDP [unknown] +library/browsercontext-fetch.spec.ts › should work with http credentials [pass] +library/browsercontext-fetch.spec.ts › should work with setHTTPCredentials [pass] +library/browsercontext-har.spec.ts › by default should abort requests not found in har [pass] +library/browsercontext-har.spec.ts › context.unrouteAll should stop context.routeFromHAR [fail] +library/browsercontext-har.spec.ts › fallback:continue should continue requests on bad har [pass] +library/browsercontext-har.spec.ts › fallback:continue should continue when not found in har [pass] +library/browsercontext-har.spec.ts › newPage should fulfill from har, matching the method and following redirects [pass] +library/browsercontext-har.spec.ts › page.unrouteAll should stop page.routeFromHAR [pass] +library/browsercontext-har.spec.ts › should apply overrides before routing from har [pass] +library/browsercontext-har.spec.ts › should change document URL after redirected navigation [timeout] +library/browsercontext-har.spec.ts › should change document URL after redirected navigation on click [fail] +library/browsercontext-har.spec.ts › should context.routeFromHAR, matching the method and following redirects [pass] +library/browsercontext-har.spec.ts › should disambiguate by header [fail] +library/browsercontext-har.spec.ts › should fulfill from har with content in a file [pass] +library/browsercontext-har.spec.ts › should goBack to redirected navigation [timeout] +library/browsercontext-har.spec.ts › should goForward to redirected navigation [timeout] +library/browsercontext-har.spec.ts › should ignore aborted requests [pass] +library/browsercontext-har.spec.ts › should ignore boundary when matching multipart/form-data body [timeout] +library/browsercontext-har.spec.ts › should only context.routeFromHAR requests matching url filter [pass] +library/browsercontext-har.spec.ts › should only handle requests matching url filter [pass] +library/browsercontext-har.spec.ts › should only page.routeFromHAR requests matching url filter [pass] +library/browsercontext-har.spec.ts › should page.routeFromHAR, matching the method and following redirects [pass] +library/browsercontext-har.spec.ts › should produce extracted zip [fail] +library/browsercontext-har.spec.ts › should record overridden requests to har [timeout] +library/browsercontext-har.spec.ts › should reload redirected navigation [timeout] +library/browsercontext-har.spec.ts › should round-trip extracted har.zip [fail] +library/browsercontext-har.spec.ts › should round-trip har with postData [fail] +library/browsercontext-har.spec.ts › should round-trip har.zip [fail] +library/browsercontext-har.spec.ts › should support regex filter [fail] +library/browsercontext-har.spec.ts › should update extracted har.zip for page [fail] +library/browsercontext-har.spec.ts › should update har.zip for context [fail] +library/browsercontext-har.spec.ts › should update har.zip for page [fail] +library/browsercontext-har.spec.ts › should update har.zip for page with different options [fail] +library/browsercontext-locale.spec.ts › should affect Intl.DateTimeFormat().resolvedOptions().locale [fail] +library/browsercontext-locale.spec.ts › should affect accept-language header @smoke [fail] +library/browsercontext-locale.spec.ts › should affect navigator.language [fail] +library/browsercontext-locale.spec.ts › should affect navigator.language in popups [fail] +library/browsercontext-locale.spec.ts › should be isolated between contexts [fail] +library/browsercontext-locale.spec.ts › should format date [fail] +library/browsercontext-locale.spec.ts › should format number [fail] +library/browsercontext-locale.spec.ts › should format number in popups [timeout] +library/browsercontext-locale.spec.ts › should format number in workers [fail] +library/browsercontext-locale.spec.ts › should not change default locale in another context [fail] +library/browsercontext-locale.spec.ts › should work for multiple pages sharing same process [pass] +library/browsercontext-network-event.spec.ts › BrowserContext.Events.Request [fail] +library/browsercontext-network-event.spec.ts › BrowserContext.Events.RequestFailed [fail] +library/browsercontext-network-event.spec.ts › BrowserContext.Events.RequestFinished [pass] +library/browsercontext-network-event.spec.ts › BrowserContext.Events.Response [fail] +library/browsercontext-network-event.spec.ts › should fire events in proper order [pass] +library/browsercontext-network-event.spec.ts › should not fire events for favicon or favicon redirects [unknown] +library/browsercontext-page-event.spec.ts › should fire page lifecycle events [fail] +library/browsercontext-page-event.spec.ts › should have about:blank for empty url with domcontentloaded [timeout] +library/browsercontext-page-event.spec.ts › should have about:blank url with domcontentloaded [pass] +library/browsercontext-page-event.spec.ts › should have an opener [pass] +library/browsercontext-page-event.spec.ts › should have url [pass] +library/browsercontext-page-event.spec.ts › should have url after domcontentloaded [pass] +library/browsercontext-page-event.spec.ts › should not crash while redirecting of original request was missed [pass] +library/browsercontext-page-event.spec.ts › should not hang on ctrl-click during provisional load [fail] +library/browsercontext-page-event.spec.ts › should report initialized pages [pass] +library/browsercontext-page-event.spec.ts › should report when a new page is created and closed [pass] +library/browsercontext-page-event.spec.ts › should work with Ctrl-clicking [fail] +library/browsercontext-page-event.spec.ts › should work with Shift-clicking [fail] +library/browsercontext-pages.spec.ts › frame.focus should work multiple times [fail] +library/browsercontext-pages.spec.ts › page.context should return the correct instance [pass] +library/browsercontext-pages.spec.ts › should click the button with deviceScaleFactor set [fail] +library/browsercontext-pages.spec.ts › should click the button with offset with page scale [pass] +library/browsercontext-pages.spec.ts › should click with disabled javascript [pass] +library/browsercontext-pages.spec.ts › should keep selection in multiple pages [fail] +library/browsercontext-pages.spec.ts › should not be visible in context.pages [pass] +library/browsercontext-pages.spec.ts › should not hang with touch-enabled viewports [pass] +library/browsercontext-pages.spec.ts › should not leak listeners during navigation of 20 pages [pass] +library/browsercontext-pages.spec.ts › should return bounding box with page scale [pass] +library/browsercontext-proxy.spec.ts › does launch without a port [pass] +library/browsercontext-proxy.spec.ts › should authenticate [fail] +library/browsercontext-proxy.spec.ts › should authenticate with empty password [fail] +library/browsercontext-proxy.spec.ts › should exclude patterns [fail] +library/browsercontext-proxy.spec.ts › should isolate proxy credentials between contexts [fail] +library/browsercontext-proxy.spec.ts › should isolate proxy credentials between contexts on navigation [fail] +library/browsercontext-proxy.spec.ts › should proxy local network requests › by default › link-local [fail] +library/browsercontext-proxy.spec.ts › should proxy local network requests › by default › localhost [fail] +library/browsercontext-proxy.spec.ts › should proxy local network requests › by default › loopback address [fail] +library/browsercontext-proxy.spec.ts › should proxy local network requests › with other bypasses › link-local [fail] +library/browsercontext-proxy.spec.ts › should proxy local network requests › with other bypasses › localhost [fail] +library/browsercontext-proxy.spec.ts › should proxy local network requests › with other bypasses › loopback address [fail] +library/browsercontext-proxy.spec.ts › should set cookie for top-level domain [fail] +library/browsercontext-proxy.spec.ts › should throw for bad server value [pass] +library/browsercontext-proxy.spec.ts › should throw for socks4 authentication [pass] +library/browsercontext-proxy.spec.ts › should throw for socks5 authentication [pass] +library/browsercontext-proxy.spec.ts › should use ipv6 proxy [fail] +library/browsercontext-proxy.spec.ts › should use proxy [fail] +library/browsercontext-proxy.spec.ts › should use proxy for https urls [timeout] +library/browsercontext-proxy.spec.ts › should use proxy for second page [fail] +library/browsercontext-proxy.spec.ts › should use proxy twice [fail] +library/browsercontext-proxy.spec.ts › should use socks proxy [fail] +library/browsercontext-proxy.spec.ts › should use socks proxy in second page [fail] +library/browsercontext-proxy.spec.ts › should work when passing the proxy only on the context level [fail] +library/browsercontext-proxy.spec.ts › should work with IP:PORT notion [fail] +library/browsercontext-reuse.spec.ts › should continue issuing events after closing the reused page [pass] +library/browsercontext-reuse.spec.ts › should ignore binding from beforeunload [fail] +library/browsercontext-reuse.spec.ts › should not cache resources [fail] +library/browsercontext-reuse.spec.ts › should re-add binding after reset [pass] +library/browsercontext-reuse.spec.ts › should reset mouse position [fail] +library/browsercontext-reuse.spec.ts › should reset serviceworker [pass] +library/browsercontext-reuse.spec.ts › should reset serviceworker that hangs in importScripts [pass] +library/browsercontext-reuse.spec.ts › should reset tracing [pass] +library/browsercontext-reuse.spec.ts › should work with clock emulation [pass] +library/browsercontext-route.spec.ts › should chain fallback [fail] +library/browsercontext-route.spec.ts › should chain fallback into page [pass] +library/browsercontext-route.spec.ts › should chain fallback w/ dynamic URL [fail] +library/browsercontext-route.spec.ts › should fall back async [fail] +library/browsercontext-route.spec.ts › should fall back to context.route [fail] +library/browsercontext-route.spec.ts › should ignore secure Set-Cookie header for insecure requests [pass] +library/browsercontext-route.spec.ts › should intercept [fail] +library/browsercontext-route.spec.ts › should not chain abort [pass] +library/browsercontext-route.spec.ts › should not chain fulfill [fail] +library/browsercontext-route.spec.ts › should overwrite post body with empty string [fail] +library/browsercontext-route.spec.ts › should support Set-Cookie header [pass] +library/browsercontext-route.spec.ts › should support async handler w/ times [fail] +library/browsercontext-route.spec.ts › should support the times parameter with route matching [fail] +library/browsercontext-route.spec.ts › should unroute [fail] +library/browsercontext-route.spec.ts › should use Set-Cookie header in future requests [fail] +library/browsercontext-route.spec.ts › should work if handler with times parameter was removed from another handler [fail] +library/browsercontext-route.spec.ts › should work with ignoreHTTPSErrors [fail] +library/browsercontext-route.spec.ts › should yield to page.route [fail] +library/browsercontext-service-worker-policy.spec.ts › block › blocks service worker registration [timeout] +library/browsercontext-service-worker-policy.spec.ts › block › should not throw error on about:blank [pass] +library/browsercontext-service-worker-policy.spec.ts › should allow service workers by default [pass] +library/browsercontext-set-extra-http-headers.spec.ts › should override extra headers from browser context [fail] +library/browsercontext-set-extra-http-headers.spec.ts › should throw for non-string header values [pass] +library/browsercontext-storage-state.spec.ts › should capture cookies [pass] +library/browsercontext-storage-state.spec.ts › should capture local storage [pass] +library/browsercontext-storage-state.spec.ts › should handle malformed file [pass] +library/browsercontext-storage-state.spec.ts › should handle missing file [pass] +library/browsercontext-storage-state.spec.ts › should not emit events about internal page [fail] +library/browsercontext-storage-state.spec.ts › should not restore localStorage twice [pass] +library/browsercontext-storage-state.spec.ts › should round-trip through the file [pass] +library/browsercontext-storage-state.spec.ts › should roundtrip local storage in third-party context [fail] +library/browsercontext-storage-state.spec.ts › should serialize storageState with lone surrogates [fail] +library/browsercontext-storage-state.spec.ts › should set local storage [pass] +library/browsercontext-storage-state.spec.ts › should set local storage in third-party context [fail] +library/browsercontext-storage-state.spec.ts › should work when service worker is intefering [pass] +library/browsercontext-strict.spec.ts › should not fail page.textContent in non-strict mode [fail] +library/browsercontext-strict.spec.ts › strict context mode › should fail page.click in strict mode [fail] +library/browsercontext-strict.spec.ts › strict context mode › should fail page.textContent in strict mode [fail] +library/browsercontext-strict.spec.ts › strict context mode › should opt out of strict mode [fail] +library/browsercontext-timezone-id.spec.ts › should affect Intl.DateTimeFormat().resolvedOptions().timeZone [fail] +library/browsercontext-timezone-id.spec.ts › should not change default timezone in another context [fail] +library/browsercontext-timezone-id.spec.ts › should throw for invalid timezone IDs when creating pages [fail] +library/browsercontext-timezone-id.spec.ts › should work @smoke [fail] +library/browsercontext-timezone-id.spec.ts › should work for multiple pages sharing same process [timeout] +library/browsercontext-user-agent.spec.ts › custom user agent for download [fail] +library/browsercontext-user-agent.spec.ts › should emulate device user-agent [fail] +library/browsercontext-user-agent.spec.ts › should make a copy of default options [fail] +library/browsercontext-user-agent.spec.ts › should work [fail] +library/browsercontext-user-agent.spec.ts › should work for navigator.userAgentData and sec-ch-ua headers [unknown] +library/browsercontext-user-agent.spec.ts › should work for subframes [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › default mobile viewports to 980 width [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › mouse should work with mobile viewports and cross process navigations [pass] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › respect meta viewport tag [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should be detectable [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should detect touch when applying viewport with touches [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should emulate the hover media feature [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should fire orientationchange event [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should scroll when emulating a mobile viewport [pass] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should support landscape emulation [pass] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should support mobile emulation [pass] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should support touch emulation [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › should support window.orientation emulation [fail] +library/browsercontext-viewport-mobile.spec.ts › mobile viewport › view scale should reset after navigation [pass] +library/browsercontext-viewport.spec.ts › WebKit Windows headed should have a minimal viewport [unknown] +library/browsercontext-viewport.spec.ts › should be able to get correct orientation angle on non-mobile devices [pass] +library/browsercontext-viewport.spec.ts › should drag with high dpi [fail] +library/browsercontext-viewport.spec.ts › should emulate availWidth and availHeight [fail] +library/browsercontext-viewport.spec.ts › should emulate device height [fail] +library/browsercontext-viewport.spec.ts › should emulate device width [fail] +library/browsercontext-viewport.spec.ts › should get the proper default viewport size [pass] +library/browsercontext-viewport.spec.ts › should not have touch by default [pass] +library/browsercontext-viewport.spec.ts › should report null viewportSize when given null viewport [pass] +library/browsercontext-viewport.spec.ts › should return correct outerWidth and outerHeight [pass] +library/browsercontext-viewport.spec.ts › should set both screen and viewport options [fail] +library/browsercontext-viewport.spec.ts › should set the proper viewport size [pass] +library/browsercontext-viewport.spec.ts › should set window.screen.orientation.type for mobile devices [fail] +library/browsercontext-viewport.spec.ts › should support touch with null viewport [fail] +library/browsercontext-viewport.spec.ts › should throw on tap if hasTouch is not enabled [fail] +library/browsertype-basic.spec.ts › browserType.executablePath should work [unknown] +library/browsertype-basic.spec.ts › browserType.name should work [fail] +library/browsertype-basic.spec.ts › should throw when trying to connect with not-chromium [pass] +library/browsertype-connect.spec.ts › launchServer only › should be able to reconnect to a browser 12 times without warnings [timeout] +library/browsertype-connect.spec.ts › launchServer only › should properly disconnect when connection closes from the server side [timeout] +library/browsertype-connect.spec.ts › launchServer only › should work with cluster [timeout] +library/browsertype-connect.spec.ts › launchServer › disconnected event should be emitted when browser is closed or server is closed [timeout] +library/browsertype-connect.spec.ts › launchServer › disconnected event should have browser as argument [timeout] +library/browsertype-connect.spec.ts › launchServer › setInputFiles should preserve lastModified timestamp [timeout] +library/browsertype-connect.spec.ts › launchServer › should be able to connect 20 times to a single server without warnings [timeout] +library/browsertype-connect.spec.ts › launchServer › should be able to connect two browsers at the same time [timeout] +library/browsertype-connect.spec.ts › launchServer › should be able to connect when the wsEndpoint is passed as an option [timeout] +library/browsertype-connect.spec.ts › launchServer › should be able to reconnect to a browser [timeout] +library/browsertype-connect.spec.ts › launchServer › should be able to visit ipv6 [timeout] +library/browsertype-connect.spec.ts › launchServer › should be able to visit ipv6 through localhost [timeout] +library/browsertype-connect.spec.ts › launchServer › should connect over http [timeout] +library/browsertype-connect.spec.ts › launchServer › should connect over wss [timeout] +library/browsertype-connect.spec.ts › launchServer › should emit close events on pages and contexts [timeout] +library/browsertype-connect.spec.ts › launchServer › should error when saving download after deletion [timeout] +library/browsertype-connect.spec.ts › launchServer › should filter launch options [timeout] +library/browsertype-connect.spec.ts › launchServer › should fulfill with global fetch result [timeout] +library/browsertype-connect.spec.ts › launchServer › should handle exceptions during connect [timeout] +library/browsertype-connect.spec.ts › launchServer › should ignore page.pause when headed [timeout] +library/browsertype-connect.spec.ts › launchServer › should not throw on close after disconnect [timeout] +library/browsertype-connect.spec.ts › launchServer › should print HTTP error [pass] +library/browsertype-connect.spec.ts › launchServer › should print custom ws close error [pass] +library/browsertype-connect.spec.ts › launchServer › should print ws error [pass] +library/browsertype-connect.spec.ts › launchServer › should properly disconnect when connection closes from the client side [timeout] +library/browsertype-connect.spec.ts › launchServer › should record trace with sources [timeout] +library/browsertype-connect.spec.ts › launchServer › should reject navigation when browser closes [timeout] +library/browsertype-connect.spec.ts › launchServer › should reject waitForEvent before browser.close finishes [timeout] +library/browsertype-connect.spec.ts › launchServer › should reject waitForEvent before browser.onDisconnect fires [timeout] +library/browsertype-connect.spec.ts › launchServer › should reject waitForSelector when browser closes [timeout] +library/browsertype-connect.spec.ts › launchServer › should respect selectors [timeout] +library/browsertype-connect.spec.ts › launchServer › should save download [timeout] +library/browsertype-connect.spec.ts › launchServer › should save har [timeout] +library/browsertype-connect.spec.ts › launchServer › should saveAs videos from remote browser [timeout] +library/browsertype-connect.spec.ts › launchServer › should send default User-Agent and X-Playwright-Browser headers with connect request [fail] +library/browsertype-connect.spec.ts › launchServer › should send extra headers with connect request [pass] +library/browsertype-connect.spec.ts › launchServer › should set the browser connected state [timeout] +library/browsertype-connect.spec.ts › launchServer › should support slowmo option [timeout] +library/browsertype-connect.spec.ts › launchServer › should terminate network waiters [timeout] +library/browsertype-connect.spec.ts › launchServer › should throw when calling waitForNavigation after disconnect [timeout] +library/browsertype-connect.spec.ts › launchServer › should throw when used after isConnected returns false [timeout] +library/browsertype-connect.spec.ts › launchServer › should timeout in connect while connecting [pass] +library/browsertype-connect.spec.ts › launchServer › should timeout in socket while connecting [pass] +library/browsertype-connect.spec.ts › launchServer › should upload large file [timeout] +library/browsertype-connect.spec.ts › launchServer › socks proxy › should check proxy pattern on the client [unknown] +library/browsertype-connect.spec.ts › launchServer › socks proxy › should forward non-forwarded requests [unknown] +library/browsertype-connect.spec.ts › launchServer › socks proxy › should lead to the error page for forwarded requests when the connection is refused [unknown] +library/browsertype-connect.spec.ts › launchServer › socks proxy › should proxy based on the pattern [unknown] +library/browsertype-connect.spec.ts › launchServer › socks proxy › should proxy ipv6 localhost requests @smoke [unknown] +library/browsertype-connect.spec.ts › launchServer › socks proxy › should proxy local.playwright requests [unknown] +library/browsertype-connect.spec.ts › launchServer › socks proxy › should proxy localhost requests @smoke [unknown] +library/browsertype-connect.spec.ts › launchServer › socks proxy › should proxy localhost requests from fetch api [unknown] +library/browsertype-connect.spec.ts › run-server › disconnected event should be emitted when browser is closed or server is closed [fail] +library/browsertype-connect.spec.ts › run-server › disconnected event should have browser as argument [fail] +library/browsertype-connect.spec.ts › run-server › setInputFiles should preserve lastModified timestamp [fail] +library/browsertype-connect.spec.ts › run-server › should be able to connect 20 times to a single server without warnings [fail] +library/browsertype-connect.spec.ts › run-server › should be able to connect two browsers at the same time [fail] +library/browsertype-connect.spec.ts › run-server › should be able to connect when the wsEndpoint is passed as an option [fail] +library/browsertype-connect.spec.ts › run-server › should be able to reconnect to a browser [fail] +library/browsertype-connect.spec.ts › run-server › should be able to visit ipv6 [fail] +library/browsertype-connect.spec.ts › run-server › should be able to visit ipv6 through localhost [fail] +library/browsertype-connect.spec.ts › run-server › should connect over http [fail] +library/browsertype-connect.spec.ts › run-server › should connect over wss [fail] +library/browsertype-connect.spec.ts › run-server › should emit close events on pages and contexts [fail] +library/browsertype-connect.spec.ts › run-server › should error when saving download after deletion [fail] +library/browsertype-connect.spec.ts › run-server › should filter launch options [fail] +library/browsertype-connect.spec.ts › run-server › should fulfill with global fetch result [fail] +library/browsertype-connect.spec.ts › run-server › should handle exceptions during connect [pass] +library/browsertype-connect.spec.ts › run-server › should ignore page.pause when headed [fail] +library/browsertype-connect.spec.ts › run-server › should not throw on close after disconnect [fail] +library/browsertype-connect.spec.ts › run-server › should print HTTP error [pass] +library/browsertype-connect.spec.ts › run-server › should print custom ws close error [pass] +library/browsertype-connect.spec.ts › run-server › should print ws error [pass] +library/browsertype-connect.spec.ts › run-server › should properly disconnect when connection closes from the client side [fail] +library/browsertype-connect.spec.ts › run-server › should record trace with sources [fail] +library/browsertype-connect.spec.ts › run-server › should reject navigation when browser closes [fail] +library/browsertype-connect.spec.ts › run-server › should reject waitForEvent before browser.close finishes [fail] +library/browsertype-connect.spec.ts › run-server › should reject waitForEvent before browser.onDisconnect fires [fail] +library/browsertype-connect.spec.ts › run-server › should reject waitForSelector when browser closes [fail] +library/browsertype-connect.spec.ts › run-server › should respect selectors [fail] +library/browsertype-connect.spec.ts › run-server › should save download [fail] +library/browsertype-connect.spec.ts › run-server › should save har [fail] +library/browsertype-connect.spec.ts › run-server › should saveAs videos from remote browser [fail] +library/browsertype-connect.spec.ts › run-server › should send default User-Agent and X-Playwright-Browser headers with connect request [fail] +library/browsertype-connect.spec.ts › run-server › should send extra headers with connect request [pass] +library/browsertype-connect.spec.ts › run-server › should set the browser connected state [fail] +library/browsertype-connect.spec.ts › run-server › should support slowmo option [fail] +library/browsertype-connect.spec.ts › run-server › should terminate network waiters [fail] +library/browsertype-connect.spec.ts › run-server › should throw when calling waitForNavigation after disconnect [fail] +library/browsertype-connect.spec.ts › run-server › should throw when used after isConnected returns false [fail] +library/browsertype-connect.spec.ts › run-server › should timeout in connect while connecting [pass] +library/browsertype-connect.spec.ts › run-server › should timeout in socket while connecting [pass] +library/browsertype-connect.spec.ts › run-server › should upload large file [fail] +library/browsertype-connect.spec.ts › run-server › socks proxy › should check proxy pattern on the client [fail] +library/browsertype-connect.spec.ts › run-server › socks proxy › should forward non-forwarded requests [fail] +library/browsertype-connect.spec.ts › run-server › socks proxy › should lead to the error page for forwarded requests when the connection is refused [fail] +library/browsertype-connect.spec.ts › run-server › socks proxy › should proxy based on the pattern [fail] +library/browsertype-connect.spec.ts › run-server › socks proxy › should proxy ipv6 localhost requests @smoke [fail] +library/browsertype-connect.spec.ts › run-server › socks proxy › should proxy local.playwright requests [fail] +library/browsertype-connect.spec.ts › run-server › socks proxy › should proxy localhost requests @smoke [fail] +library/browsertype-connect.spec.ts › run-server › socks proxy › should proxy localhost requests from fetch api [fail] +library/browsertype-connect.spec.ts › should refuse connecting when versions do not match [pass] +library/browsertype-launch-selenium.spec.ts › selenium grid 3.141.59 hub + node chromium [unknown] +library/browsertype-launch-selenium.spec.ts › selenium grid 3.141.59 standalone chromium [unknown] +library/browsertype-launch-selenium.spec.ts › selenium grid 3.141.59 standalone chromium through run-driver [unknown] +library/browsertype-launch-selenium.spec.ts › selenium grid 3.141.59 standalone non-chromium [unknown] +library/browsertype-launch-selenium.spec.ts › selenium grid 4.8.3 hub + node chromium [unknown] +library/browsertype-launch-selenium.spec.ts › selenium grid 4.8.3 standalone chromium [unknown] +library/browsertype-launch-selenium.spec.ts › selenium grid 4.8.3 standalone chromium broken driver [unknown] +library/browsertype-launch-server.spec.ts › launch server › should default to random wsPath [fail] +library/browsertype-launch-server.spec.ts › launch server › should fire "close" event during kill [fail] +library/browsertype-launch-server.spec.ts › launch server › should fire close event [fail] +library/browsertype-launch-server.spec.ts › launch server › should log protocol [fail] +library/browsertype-launch-server.spec.ts › launch server › should provide an error when ws endpoint is incorrect [fail] +library/browsertype-launch-server.spec.ts › launch server › should return child_process instance [fail] +library/browsertype-launch-server.spec.ts › launch server › should work [fail] +library/browsertype-launch-server.spec.ts › launch server › should work when wsPath is missing leading slash [fail] +library/browsertype-launch-server.spec.ts › launch server › should work with host [fail] +library/browsertype-launch-server.spec.ts › launch server › should work with port [fail] +library/browsertype-launch-server.spec.ts › launch server › should work with wsPath [fail] +library/browsertype-launch.spec.ts › should accept objects as options [pass] +library/browsertype-launch.spec.ts › should allow await using [pass] +library/browsertype-launch.spec.ts › should be callable twice [pass] +library/browsertype-launch.spec.ts › should fire close event for all contexts [pass] +library/browsertype-launch.spec.ts › should handle exception [pass] +library/browsertype-launch.spec.ts › should handle timeout [pass] +library/browsertype-launch.spec.ts › should reject all promises when browser is closed [pass] +library/browsertype-launch.spec.ts › should reject if executable path is invalid [pass] +library/browsertype-launch.spec.ts › should reject if launched browser fails immediately [fail] +library/browsertype-launch.spec.ts › should report launch log [pass] +library/browsertype-launch.spec.ts › should throw if page argument is passed [fail] +library/browsertype-launch.spec.ts › should throw if port option is passed [pass] +library/browsertype-launch.spec.ts › should throw if port option is passed for persistent context [pass] +library/browsertype-launch.spec.ts › should throw if userDataDir is passed as an argument [pass] +library/browsertype-launch.spec.ts › should throw if userDataDir option is passed [pass] +library/capabilities.spec.ts › Intl.ListFormat should work [pass] +library/capabilities.spec.ts › SharedArrayBuffer should work @smoke [fail] +library/capabilities.spec.ts › Web Assembly should work @smoke [pass] +library/capabilities.spec.ts › WebSocket should work @smoke [pass] +library/capabilities.spec.ts › loading in HTMLImageElement.prototype [pass] +library/capabilities.spec.ts › make sure that XMLHttpRequest upload events are emitted correctly [pass] +library/capabilities.spec.ts › navigator.clipboard should be present [pass] +library/capabilities.spec.ts › requestFullscreen [pass] +library/capabilities.spec.ts › service worker should cover the iframe [pass] +library/capabilities.spec.ts › service worker should register in an iframe [pass] +library/capabilities.spec.ts › serviceWorker should intercept document request [pass] +library/capabilities.spec.ts › should not crash on page with mp4 @smoke [fail] +library/capabilities.spec.ts › should not crash on showDirectoryPicker [unknown] +library/capabilities.spec.ts › should not crash on storage.getDirectory() [pass] +library/capabilities.spec.ts › should play audio @smoke [fail] +library/capabilities.spec.ts › should play video @smoke [pass] +library/capabilities.spec.ts › should play webm video @smoke [pass] +library/capabilities.spec.ts › should respect CSP @smoke [fail] +library/capabilities.spec.ts › should send no Content-Length header for GET requests with a Content-Type [pass] +library/capabilities.spec.ts › should set CloseEvent.wasClean to false when the server terminates a WebSocket connection [pass] +library/capabilities.spec.ts › should support webgl 2 @smoke [pass] +library/capabilities.spec.ts › should support webgl @smoke [pass] +library/capabilities.spec.ts › webkit should define window.safari [unknown] +library/capabilities.spec.ts › window.GestureEvent in WebKit [pass] +library/channels.spec.ts › exposeFunction should not leak [pass] +library/channels.spec.ts › should not generate dispatchers for subresources w/o listeners [pass] +library/channels.spec.ts › should scope CDPSession handles [unknown] +library/channels.spec.ts › should scope browser handles [pass] +library/channels.spec.ts › should scope context handles [pass] +library/channels.spec.ts › should work with the domain module [timeout] +library/chromium/bfcache.spec.ts › bindings should work after restoring from bfcache [fail] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › serviceWorker(), and fromServiceWorker() work [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › setExtraHTTPHeaders [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › setOffline [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should intercept only serviceworker request, not page [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should intercept service worker importScripts [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should intercept service worker requests (main and within) [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should intercept service worker update requests [unknown] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should produce network events, routing, and annotations for Service Worker [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should produce network events, routing, and annotations for Service Worker (advanced) [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should report failure (due to content-type) of main service worker request [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should report failure (due to redirect) of main service worker request [timeout] +library/chromium/chromium.spec.ts › PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 › should report intercepted service worker requests in HAR [timeout] +library/chromium/chromium.spec.ts › Page.route should work with intervention headers [fail] +library/chromium/chromium.spec.ts › http credentials › httpCredentials [timeout] +library/chromium/chromium.spec.ts › serviceWorkers() should return current workers [timeout] +library/chromium/chromium.spec.ts › should close service worker together with the context [timeout] +library/chromium/chromium.spec.ts › should create a worker from a service worker [timeout] +library/chromium/chromium.spec.ts › should create a worker from service worker with noop routing [timeout] +library/chromium/chromium.spec.ts › should emit new service worker on update [timeout] +library/chromium/chromium.spec.ts › should not create a worker from a shared worker [pass] +library/chromium/chromium.spec.ts › should pass args with spaces [fail] +library/chromium/connect-over-cdp.spec.ts › emulate media should not be affected by second connectOverCDP [unknown] +library/chromium/connect-over-cdp.spec.ts › setInputFiles should preserve lastModified timestamp [fail] +library/chromium/connect-over-cdp.spec.ts › should allow tracing over cdp session [fail] +library/chromium/connect-over-cdp.spec.ts › should be able to connect via localhost [fail] +library/chromium/connect-over-cdp.spec.ts › should cleanup artifacts dir after connectOverCDP disconnects due to ws close [fail] +library/chromium/connect-over-cdp.spec.ts › should connect over a ws endpoint [fail] +library/chromium/connect-over-cdp.spec.ts › should connect to an existing cdp session [fail] +library/chromium/connect-over-cdp.spec.ts › should connect to an existing cdp session twice [fail] +library/chromium/connect-over-cdp.spec.ts › should connect to an existing cdp session when passed as a first argument [fail] +library/chromium/connect-over-cdp.spec.ts › should connect to existing page with iframe and navigate [fail] +library/chromium/connect-over-cdp.spec.ts › should connect to existing service workers [fail] +library/chromium/connect-over-cdp.spec.ts › should connect via https [fail] +library/chromium/connect-over-cdp.spec.ts › should connectOverCDP and manage downloads in default context [fail] +library/chromium/connect-over-cdp.spec.ts › should print custom ws close error [fail] +library/chromium/connect-over-cdp.spec.ts › should report all pages in an existing browser [fail] +library/chromium/connect-over-cdp.spec.ts › should report an expected error when the endpoint URL JSON webSocketDebuggerUrl is undefined [fail] +library/chromium/connect-over-cdp.spec.ts › should report an expected error when the endpointURL returns a non-expected status code [fail] +library/chromium/connect-over-cdp.spec.ts › should return valid browser from context.browser() [fail] +library/chromium/connect-over-cdp.spec.ts › should send default User-Agent header with connect request [timeout] +library/chromium/connect-over-cdp.spec.ts › should send extra headers with connect request [timeout] +library/chromium/connect-over-cdp.spec.ts › should use logger in default context [fail] +library/chromium/connect-over-cdp.spec.ts › should use proxy with connectOverCDP [fail] +library/chromium/css-coverage.spec.ts › should NOT report scripts across navigations [fail] +library/chromium/css-coverage.spec.ts › should ignore injected stylesheets [fail] +library/chromium/css-coverage.spec.ts › should report multiple stylesheets [fail] +library/chromium/css-coverage.spec.ts › should report sourceURLs [fail] +library/chromium/css-coverage.spec.ts › should report stylesheets across navigations [fail] +library/chromium/css-coverage.spec.ts › should report stylesheets that have no coverage [fail] +library/chromium/css-coverage.spec.ts › should work [fail] +library/chromium/css-coverage.spec.ts › should work with a recently loaded stylesheet [fail] +library/chromium/css-coverage.spec.ts › should work with complicated usecases [fail] +library/chromium/css-coverage.spec.ts › should work with media queries [fail] +library/chromium/disable-web-security.spec.ts › test init script w/ --disable-web-security [pass] +library/chromium/disable-web-security.spec.ts › test utility world in popup w/ --disable-web-security [pass] +library/chromium/js-coverage.spec.ts › should NOT report scripts across navigations when enabled [fail] +library/chromium/js-coverage.spec.ts › should ignore eval() scripts by default [fail] +library/chromium/js-coverage.spec.ts › should not hang when there is a debugger statement [fail] +library/chromium/js-coverage.spec.ts › should report multiple scripts [fail] +library/chromium/js-coverage.spec.ts › should report scripts across navigations when disabled [fail] +library/chromium/js-coverage.spec.ts › should report sourceURLs [fail] +library/chromium/js-coverage.spec.ts › should work [fail] +library/chromium/js-coverage.spec.ts › shouldn't ignore eval() scripts if reportAnonymousScripts is true [fail] +library/chromium/launcher.spec.ts › should not create pages automatically [fail] +library/chromium/launcher.spec.ts › should not throw with remote-debugging-port argument [fail] +library/chromium/launcher.spec.ts › should open devtools when "devtools: true" option is given [unknown] +library/chromium/launcher.spec.ts › should return background pages [timeout] +library/chromium/launcher.spec.ts › should return background pages when recording video [timeout] +library/chromium/launcher.spec.ts › should support request/response events when using backgroundPage() [timeout] +library/chromium/launcher.spec.ts › should throw with remote-debugging-pipe argument [fail] +library/chromium/oopif.spec.ts › ElementHandle.boundingBox() should work [pass] +library/chromium/oopif.spec.ts › contentFrame should work [pass] +library/chromium/oopif.spec.ts › should allow cdp sessions on oopifs [fail] +library/chromium/oopif.spec.ts › should be able to click in iframe [fail] +library/chromium/oopif.spec.ts › should click [fail] +library/chromium/oopif.spec.ts › should click a button when it overlays oopif [pass] +library/chromium/oopif.spec.ts › should emit filechooser event for iframe [fail] +library/chromium/oopif.spec.ts › should emulate media [fail] +library/chromium/oopif.spec.ts › should emulate offline [fail] +library/chromium/oopif.spec.ts › should expose function [pass] +library/chromium/oopif.spec.ts › should get the proper viewport [unknown] +library/chromium/oopif.spec.ts › should handle oopif detach [pass] +library/chromium/oopif.spec.ts › should handle remote -> local -> remote transitions [pass] +library/chromium/oopif.spec.ts › should intercept response body from oopif [fail] +library/chromium/oopif.spec.ts › should load oopif iframes with subresources and route [pass] +library/chromium/oopif.spec.ts › should not throw on exposeFunction when oopif detaches [pass] +library/chromium/oopif.spec.ts › should report google.com frame with headed [pass] +library/chromium/oopif.spec.ts › should report main requests [pass] +library/chromium/oopif.spec.ts › should report oopif frames [pass] +library/chromium/oopif.spec.ts › should respect route [pass] +library/chromium/oopif.spec.ts › should support addInitScript [pass] +library/chromium/oopif.spec.ts › should support context options [fail] +library/chromium/oopif.spec.ts › should support exposeFunction [fail] +library/chromium/oopif.spec.ts › should take screenshot [fail] +library/chromium/session.spec.ts › should be able to detach session [fail] +library/chromium/session.spec.ts › should detach when page closes [fail] +library/chromium/session.spec.ts › should enable and disable domains independently [fail] +library/chromium/session.spec.ts › should not break page.close() [fail] +library/chromium/session.spec.ts › should only accept a page or frame [pass] +library/chromium/session.spec.ts › should reject protocol calls when page closes [fail] +library/chromium/session.spec.ts › should send events [fail] +library/chromium/session.spec.ts › should throw if target is part of main [fail] +library/chromium/session.spec.ts › should throw nice errors [fail] +library/chromium/session.spec.ts › should work [fail] +library/chromium/session.spec.ts › should work with main frame [fail] +library/chromium/session.spec.ts › should work with newBrowserCDPSession [fail] +library/chromium/tracing.spec.ts › should create directories as needed [fail] +library/chromium/tracing.spec.ts › should output a trace [fail] +library/chromium/tracing.spec.ts › should return a buffer [fail] +library/chromium/tracing.spec.ts › should run with custom categories if provided [fail] +library/chromium/tracing.spec.ts › should support a buffer without a path [fail] +library/chromium/tracing.spec.ts › should throw if tracing on two pages [fail] +library/chromium/tracing.spec.ts › should work without options [fail] +library/client-certificates.spec.ts › browser › persistentContext › should pass with matching certificates [fail] +library/client-certificates.spec.ts › browser › persistentContext › validate input [pass] +library/client-certificates.spec.ts › browser › should fail with matching certificates in legacy pfx format [pass] +library/client-certificates.spec.ts › browser › should fail with no client certificates [fail] +library/client-certificates.spec.ts › browser › should fail with self-signed client certificates [fail] +library/client-certificates.spec.ts › browser › should handle TLS renegotiation with client certificates [fail] +library/client-certificates.spec.ts › browser › should handle rejected certificate in handshake with HTTP/2 [pass] +library/client-certificates.spec.ts › browser › should have ignoreHTTPSErrors=false by default [fail] +library/client-certificates.spec.ts › browser › should keep supporting http [pass] +library/client-certificates.spec.ts › browser › should not hang on tls errors during TLS 1.2 handshake [fail] +library/client-certificates.spec.ts › browser › should pass with matching certificates [fail] +library/client-certificates.spec.ts › browser › should pass with matching certificates and trailing slash [fail] +library/client-certificates.spec.ts › browser › should pass with matching certificates and when a http proxy is used [fail] +library/client-certificates.spec.ts › browser › should pass with matching certificates and when a socks proxy is used [fail] +library/client-certificates.spec.ts › browser › should pass with matching certificates in pfx format [fail] +library/client-certificates.spec.ts › browser › should pass with matching certificates in pfx format when passing as content [fail] +library/client-certificates.spec.ts › browser › should pass with matching certificates on context APIRequestContext instance [pass] +library/client-certificates.spec.ts › browser › should pass with matching certificates when passing as content [fail] +library/client-certificates.spec.ts › browser › should return target connection errors when using http2 [unknown] +library/client-certificates.spec.ts › browser › should throw a http error if the pfx passphrase is incorect [pass] +library/client-certificates.spec.ts › browser › support http2 [fail] +library/client-certificates.spec.ts › browser › support http2 if the browser only supports http1.1 [unknown] +library/client-certificates.spec.ts › browser › validate input [pass] +library/client-certificates.spec.ts › fetch › pass with trusted client certificates [pass] +library/client-certificates.spec.ts › fetch › pass with trusted client certificates and when a http proxy is used [pass] +library/client-certificates.spec.ts › fetch › pass with trusted client certificates and when a socks proxy is used [pass] +library/client-certificates.spec.ts › fetch › pass with trusted client certificates in pfx format [pass] +library/client-certificates.spec.ts › fetch › should fail with matching certificates in legacy pfx format [pass] +library/client-certificates.spec.ts › fetch › should fail with no client certificates provided [pass] +library/client-certificates.spec.ts › fetch › should keep supporting http [pass] +library/client-certificates.spec.ts › fetch › should throw a http error if the pfx passphrase is incorect [pass] +library/client-certificates.spec.ts › fetch › should throw with untrusted client certs [pass] +library/client-certificates.spec.ts › fetch › should work in the browser with request interception [pass] +library/client-certificates.spec.ts › fetch › validate input [pass] +library/clock.spec.ts › Intl API › Creates a RelativeTimeFormat like normal [pass] +library/clock.spec.ts › Intl API › Executes formatRange like normal [pass] +library/clock.spec.ts › Intl API › Executes formatRangeToParts like normal [pass] +library/clock.spec.ts › Intl API › Executes resolvedOptions like normal [pass] +library/clock.spec.ts › Intl API › Executes supportedLocalesOf like normal [pass] +library/clock.spec.ts › Intl API › formatToParts via isFirstOfMonth -> Returns false when passed a timestamp argument that is not first of the month [pass] +library/clock.spec.ts › Intl API › formatToParts via isFirstOfMonth -> Returns false when passed no timestamp and system time is not first of the month [pass] +library/clock.spec.ts › Intl API › formatToParts via isFirstOfMonth -> Returns true when passed a timestamp argument that is first of the month [pass] +library/clock.spec.ts › Intl API › formatToParts via isFirstOfMonth -> Returns true when passed no timestamp and system time is first of the month [pass] +library/clock.spec.ts › cancelAnimationFrame › does not remove interval [pass] +library/clock.spec.ts › cancelAnimationFrame › does not remove timeout [pass] +library/clock.spec.ts › cancelAnimationFrame › ignores null argument [pass] +library/clock.spec.ts › cancelAnimationFrame › removes animation frame [pass] +library/clock.spec.ts › cancelIdleCallback › removes idle callback [pass] +library/clock.spec.ts › clearInterval › ignores null argument [pass] +library/clock.spec.ts › clearInterval › removes interval [pass] +library/clock.spec.ts › clearInterval › removes interval with undefined interval [pass] +library/clock.spec.ts › clearInterval › removes timeout [pass] +library/clock.spec.ts › clearTimeout › ignores null argument [pass] +library/clock.spec.ts › clearTimeout › removes interval [pass] +library/clock.spec.ts › clearTimeout › removes interval with undefined interval [pass] +library/clock.spec.ts › clearTimeout › removes timeout [pass] +library/clock.spec.ts › date › creates Date objects representing clock time [pass] +library/clock.spec.ts › date › creates real Date objects [pass] +library/clock.spec.ts › date › creates regular date when passing a date as RFC 2822 string [pass] +library/clock.spec.ts › date › creates regular date when passing a date as string [pass] +library/clock.spec.ts › date › creates regular date when passing timestamp [pass] +library/clock.spec.ts › date › creates regular date when passing y, m, d [pass] +library/clock.spec.ts › date › creates regular date when passing y, m, d, h [pass] +library/clock.spec.ts › date › creates regular date when passing y, m, d, h, m [pass] +library/clock.spec.ts › date › creates regular date when passing y, m, d, h, m, s [pass] +library/clock.spec.ts › date › creates regular date when passing y, m, d, h, m, s, ms [pass] +library/clock.spec.ts › date › creates regular date when passing year, month [pass] +library/clock.spec.ts › date › listens to system clock changes [pass] +library/clock.spec.ts › date › listens to ticking clock [pass] +library/clock.spec.ts › date › mirrors UTC method [pass] +library/clock.spec.ts › date › mirrors native Date.prototype [pass] +library/clock.spec.ts › date › mirrors parse method [pass] +library/clock.spec.ts › date › mirrors toUTCString method [pass] +library/clock.spec.ts › date › provides date constructor [pass] +library/clock.spec.ts › date › returns clock.now() [pass] +library/clock.spec.ts › date › returns date as string representing clock time [pass] +library/clock.spec.ts › date › returns date as string when called as function [pass] +library/clock.spec.ts › date › returns date as string when calling with arguments [pass] +library/clock.spec.ts › date › returns date as string when calling with timestamp [pass] +library/clock.spec.ts › date › supports now method if present [pass] +library/clock.spec.ts › fastForward › handles multiple pending timers and types [pass] +library/clock.spec.ts › fastForward › ignores timers which wouldn't be run [pass] +library/clock.spec.ts › fastForward › pushes back execution time for skipped timers [pass] +library/clock.spec.ts › pauseAt › fire target timers [pass] +library/clock.spec.ts › pauseAt › pause at target time [pass] +library/clock.spec.ts › pauseAt › returns consumed clicks [pass] +library/clock.spec.ts › performance.now() › should listen to multiple ticks in performance.now [pass] +library/clock.spec.ts › performance.now() › should run along with clock.tick [pass] +library/clock.spec.ts › performance.now() › should run with ticks with timers set [pass] +library/clock.spec.ts › performance.now() › should start at 0 [pass] +library/clock.spec.ts › requestAnimationFrame › returns numeric id or object with numeric id [pass] +library/clock.spec.ts › requestAnimationFrame › returns unique id [pass] +library/clock.spec.ts › requestAnimationFrame › should be called with performance.now() even when performance unavailable [pass] +library/clock.spec.ts › requestAnimationFrame › should be called with performance.now() when available [pass] +library/clock.spec.ts › requestAnimationFrame › should call callback once [pass] +library/clock.spec.ts › requestAnimationFrame › should properly schedule callback for 3rd frame [pass] +library/clock.spec.ts › requestAnimationFrame › should run every 16ms [pass] +library/clock.spec.ts › requestAnimationFrame › should schedule for next frame if on current frame [pass] +library/clock.spec.ts › requestAnimationFrame › should schedule two callbacks before the next frame at the same time [pass] +library/clock.spec.ts › requestAnimationFrame › throws if no arguments [pass] +library/clock.spec.ts › requestIdleCallback › doesn't runs if there are any timers and no timeout option [pass] +library/clock.spec.ts › requestIdleCallback › returns numeric id [pass] +library/clock.spec.ts › requestIdleCallback › returns unique id [pass] +library/clock.spec.ts › requestIdleCallback › runs after all timers [pass] +library/clock.spec.ts › requestIdleCallback › runs no later than timeout option even if there are any timers [pass] +library/clock.spec.ts › requestIdleCallback › throws if no arguments [pass] +library/clock.spec.ts › runFor › calls function with global object or null (strict mode) as this [pass] +library/clock.spec.ts › runFor › creates updated Date while ticking [pass] +library/clock.spec.ts › runFor › creates updated Date while ticking promises [pass] +library/clock.spec.ts › runFor › does not fire canceled intervals [pass] +library/clock.spec.ts › runFor › does not fire intervals canceled in a promise [pass] +library/clock.spec.ts › runFor › does not silently catch errors [pass] +library/clock.spec.ts › runFor › does not trigger without sufficient delay [pass] +library/clock.spec.ts › runFor › fires nested setTimeout calls in user-created promises properly [pass] +library/clock.spec.ts › runFor › fires nested setTimeout calls properly [pass] +library/clock.spec.ts › runFor › fires promise timers in correct order [pass] +library/clock.spec.ts › runFor › fires timer in intervals of "13" [pass] +library/clock.spec.ts › runFor › fires timer in intervals of 13 [pass] +library/clock.spec.ts › runFor › fires timers in correct order [pass] +library/clock.spec.ts › runFor › is not influenced by forward system clock changes [pass] +library/clock.spec.ts › runFor › is not influenced by forward system clock changes 2 [pass] +library/clock.spec.ts › runFor › is not influenced by forward system clock changes in promises [pass] +library/clock.spec.ts › runFor › is not influenced by forward system clock changes when an error is thrown [pass] +library/clock.spec.ts › runFor › is not influenced by forward system clock changes when an error is thrown 2 [pass] +library/clock.spec.ts › runFor › mini integration test [pass] +library/clock.spec.ts › runFor › should settle chained user-created promises [pass] +library/clock.spec.ts › runFor › should settle local nested promises before calling timeouts [pass] +library/clock.spec.ts › runFor › should settle local promises before calling timeouts [pass] +library/clock.spec.ts › runFor › should settle multiple user-created promises [pass] +library/clock.spec.ts › runFor › should settle nested user-created promises [pass] +library/clock.spec.ts › runFor › should settle user-created promises [pass] +library/clock.spec.ts › runFor › should settle user-created promises before calling more timeouts [pass] +library/clock.spec.ts › runFor › should settle user-created promises even if some throw [pass] +library/clock.spec.ts › runFor › throws for negative minutes [pass] +library/clock.spec.ts › runFor › throws on negative ticks [pass] +library/clock.spec.ts › runFor › triggers after sufficient delay [pass] +library/clock.spec.ts › runFor › triggers even when some throw [pass] +library/clock.spec.ts › runFor › triggers immediately without specified delay [pass] +library/clock.spec.ts › runFor › triggers in the order scheduled [pass] +library/clock.spec.ts › runFor › triggers multiple simultaneous timers [pass] +library/clock.spec.ts › runFor › triggers multiple simultaneous timers with zero callAt [pass] +library/clock.spec.ts › runFor › triggers simultaneous timers [pass] +library/clock.spec.ts › runFor › triggers timeouts and intervals in the order scheduled [pass] +library/clock.spec.ts › runFor › waits after setTimeout was called [pass] +library/clock.spec.ts › setInterval › does not schedule recurring timeout when cleared [pass] +library/clock.spec.ts › setInterval › does not throw if |undefined| or |null| is passed as a callback [pass] +library/clock.spec.ts › setInterval › is not influenced by backward system clock changes [pass] +library/clock.spec.ts › setInterval › is not influenced by forward system clock changes [pass] +library/clock.spec.ts › setInterval › passes setTimeout parameters [pass] +library/clock.spec.ts › setInterval › returns numeric id or object with numeric id [pass] +library/clock.spec.ts › setInterval › returns unique id [pass] +library/clock.spec.ts › setInterval › schedules recurring timeout [pass] +library/clock.spec.ts › setInterval › throws if no arguments [pass] +library/clock.spec.ts › setTimeout › calls correct timeout on recursive tick [pass] +library/clock.spec.ts › setTimeout › does not depend on this [pass] +library/clock.spec.ts › setTimeout › does not throw if |undefined| or |null| is passed as a callback [pass] +library/clock.spec.ts › setTimeout › is not influenced by backward system clock changes [pass] +library/clock.spec.ts › setTimeout › is not influenced by forward system clock changes [pass] +library/clock.spec.ts › setTimeout › parses no-numeric string times [pass] +library/clock.spec.ts › setTimeout › parses numeric string times [pass] +library/clock.spec.ts › setTimeout › passes setTimeout parameters [pass] +library/clock.spec.ts › setTimeout › returns numeric id or object with numeric id [pass] +library/clock.spec.ts › setTimeout › returns unique id [pass] +library/clock.spec.ts › setTimeout › sets timers on instance [pass] +library/clock.spec.ts › setTimeout › starts id from a large number [pass] +library/clock.spec.ts › setTimeout › throws if no arguments [pass] +library/clock.spec.ts › setTimeout › use of eval when not in node › evals non-function callbacks [pass] +library/clock.spec.ts › setTimeout › use of eval when not in node › only evals on global scope [pass] +library/clock.spec.ts › stubTimers › deletes global property on uninstall if it was inherited onto the global object [unknown] +library/clock.spec.ts › stubTimers › does not fake methods not provided [pass] +library/clock.spec.ts › stubTimers › fake Date constructor should mirror Date's properties [pass] +library/clock.spec.ts › stubTimers › fakes Date constructor [pass] +library/clock.spec.ts › stubTimers › fakes provided methods [pass] +library/clock.spec.ts › stubTimers › global fake setTimeout should return id [pass] +library/clock.spec.ts › stubTimers › mirrors custom Date properties [pass] +library/clock.spec.ts › stubTimers › replace Event.prototype.timeStamp [pass] +library/clock.spec.ts › stubTimers › replaces global clearInterval [pass] +library/clock.spec.ts › stubTimers › replaces global clearTimeout [pass] +library/clock.spec.ts › stubTimers › replaces global performance.now [pass] +library/clock.spec.ts › stubTimers › replaces global setInterval [pass] +library/clock.spec.ts › stubTimers › replaces global setTimeout [pass] +library/clock.spec.ts › stubTimers › resets faked methods [pass] +library/clock.spec.ts › stubTimers › returns clock object [pass] +library/clock.spec.ts › stubTimers › sets initial timestamp [pass] +library/clock.spec.ts › stubTimers › should let performance.mark still be callable after install() (#136) [unknown] +library/clock.spec.ts › stubTimers › should not alter the global performance properties and methods [unknown] +library/clock.spec.ts › stubTimers › should replace the getEntries, getEntriesByX methods with noops that return [] [unknown] +library/clock.spec.ts › stubTimers › takes an object parameter [pass] +library/clock.spec.ts › stubTimers › uninstalls Date constructor [pass] +library/clock.spec.ts › stubTimers › uninstalls global performance.now [pass] +library/clock.spec.ts › works with concurrent runFor calls [pass] +library/clock.spec.ts › works with slow setTimeout in busy embedder [pass] +library/clock.spec.ts › works with slow setTimeout in busy embedder when not paused [pass] +library/component-parser.spec.ts › should escape [pass] +library/component-parser.spec.ts › should parse [pass] +library/component-parser.spec.ts › should parse all operators [pass] +library/component-parser.spec.ts › should parse bool [pass] +library/component-parser.spec.ts › should parse float values [pass] +library/component-parser.spec.ts › should parse identifiers [pass] +library/component-parser.spec.ts › should parse int values [pass] +library/component-parser.spec.ts › should parse regex [pass] +library/component-parser.spec.ts › should parse short attributes [pass] +library/component-parser.spec.ts › should parse unquoted string [pass] +library/component-parser.spec.ts › should throw on malformed selector [pass] +library/component-parser.spec.ts › should tolerate spacing [pass] +library/css-parser.spec.ts › should parse css [pass] +library/css-parser.spec.ts › should throw on malformed css [pass] +library/debug-controller.spec.ts › should highlight all [fail] +library/debug-controller.spec.ts › should navigate all [fail] +library/debug-controller.spec.ts › should pick element [fail] +library/debug-controller.spec.ts › should record [fail] +library/debug-controller.spec.ts › should record custom data-testid [fail] +library/debug-controller.spec.ts › should report pages [fail] +library/debug-controller.spec.ts › should reset for reuse [fail] +library/debug-controller.spec.ts › should reset routes before reuse [fail] +library/defaultbrowsercontext-1.spec.ts › context.addCookies() should work [pass] +library/defaultbrowsercontext-1.spec.ts › context.clearCookies() should work [pass] +library/defaultbrowsercontext-1.spec.ts › context.cookies() should work @smoke [pass] +library/defaultbrowsercontext-1.spec.ts › should support acceptDownloads option [fail] +library/defaultbrowsercontext-1.spec.ts › should support bypassCSP option [fail] +library/defaultbrowsercontext-1.spec.ts › should support deviceScaleFactor option [pass] +library/defaultbrowsercontext-1.spec.ts › should support httpCredentials option [fail] +library/defaultbrowsercontext-1.spec.ts › should support javascriptEnabled option [fail] +library/defaultbrowsercontext-1.spec.ts › should support offline option [pass] +library/defaultbrowsercontext-1.spec.ts › should support userAgent option [fail] +library/defaultbrowsercontext-1.spec.ts › should support viewport option [pass] +library/defaultbrowsercontext-1.spec.ts › should(not) block third party cookies [pass] +library/defaultbrowsercontext-2.spec.ts › coverage should work [unknown] +library/defaultbrowsercontext-2.spec.ts › should accept userDataDir [pass] +library/defaultbrowsercontext-2.spec.ts › should connect to a browser with the default page [pass] +library/defaultbrowsercontext-2.spec.ts › should create userDataDir if it does not exist [pass] +library/defaultbrowsercontext-2.spec.ts › should fire close event for a persistent context [pass] +library/defaultbrowsercontext-2.spec.ts › should handle exception [timeout] +library/defaultbrowsercontext-2.spec.ts › should handle timeout [pass] +library/defaultbrowsercontext-2.spec.ts › should have default URL when launching browser [pass] +library/defaultbrowsercontext-2.spec.ts › should have passed URL when launching with ignoreDefaultArgs: true [fail] +library/defaultbrowsercontext-2.spec.ts › should respect selectors [fail] +library/defaultbrowsercontext-2.spec.ts › should restore state from userDataDir [pass] +library/defaultbrowsercontext-2.spec.ts › should support colorScheme option [fail] +library/defaultbrowsercontext-2.spec.ts › should support extraHTTPHeaders option [fail] +library/defaultbrowsercontext-2.spec.ts › should support forcedColors option [fail] +library/defaultbrowsercontext-2.spec.ts › should support geolocation and permissions options [timeout] +library/defaultbrowsercontext-2.spec.ts › should support har option [pass] +library/defaultbrowsercontext-2.spec.ts › should support hasTouch option [fail] +library/defaultbrowsercontext-2.spec.ts › should support ignoreHTTPSErrors option [fail] +library/defaultbrowsercontext-2.spec.ts › should support locale option [fail] +library/defaultbrowsercontext-2.spec.ts › should support reducedMotion option [fail] +library/defaultbrowsercontext-2.spec.ts › should support timezoneId option [fail] +library/defaultbrowsercontext-2.spec.ts › should throw if page argument is passed [fail] +library/defaultbrowsercontext-2.spec.ts › should work in persistent context [fail] +library/defaultbrowsercontext-2.spec.ts › user agent is up to date [pass] +library/download.spec.ts › download event › should be able to cancel pending downloads [fail] +library/download.spec.ts › download event › should close the context without awaiting the download [fail] +library/download.spec.ts › download event › should close the context without awaiting the failed download [unknown] +library/download.spec.ts › download event › should create subdirectories when saving to non-existent user-specified path [fail] +library/download.spec.ts › download event › should delete downloads on browser gone [fail] +library/download.spec.ts › download event › should delete downloads on context destruction [fail] +library/download.spec.ts › download event › should delete file [fail] +library/download.spec.ts › download event › should download large binary.zip [fail] +library/download.spec.ts › download event › should emit download event from nested iframes [timeout] +library/download.spec.ts › download event › should error when saving after deletion [fail] +library/download.spec.ts › download event › should error when saving with downloads disabled [fail] +library/download.spec.ts › download event › should expose stream [fail] +library/download.spec.ts › download event › should not fail explicitly to cancel a download even if that is already finished [fail] +library/download.spec.ts › download event › should report alt-click downloads [fail] +library/download.spec.ts › download event › should report download path within page.on('download', …) handler for Blobs [timeout] +library/download.spec.ts › download event › should report download path within page.on('download', …) handler for Files [fail] +library/download.spec.ts › download event › should report download when navigation turns into download @smoke [timeout] +library/download.spec.ts › download event › should report downloads for download attribute [fail] +library/download.spec.ts › download event › should report downloads with acceptDownloads: false [fail] +library/download.spec.ts › download event › should report downloads with acceptDownloads: true [fail] +library/download.spec.ts › download event › should report downloads with interception [fail] +library/download.spec.ts › download event › should report new window downloads [fail] +library/download.spec.ts › download event › should report non-navigation downloads [fail] +library/download.spec.ts › download event › should report proper download url when download is from download attribute [fail] +library/download.spec.ts › download event › should save to overwritten filepath [fail] +library/download.spec.ts › download event › should save to two different paths with multiple saveAs calls [fail] +library/download.spec.ts › download event › should save to user-specified path without updating original path [fail] +library/download.spec.ts › download event › should throw if browser dies [fail] +library/download.spec.ts › download event › should work with Cross-Origin-Opener-Policy [timeout] +library/download.spec.ts › should be able to download a PDF file [fail] +library/download.spec.ts › should be able to download a inline PDF file via navigation [fail] +library/download.spec.ts › should be able to download a inline PDF file via response interception [fail] +library/download.spec.ts › should convert navigation to a resource with unsupported mime type into download [timeout] +library/download.spec.ts › should download even if there is no "attachment" value [fail] +library/download.spec.ts › should download links with data url [fail] +library/download.spec.ts › should download successfully when routing [fail] +library/download.spec.ts › should save to user-specified path [fail] +library/downloads-path.spec.ts › downloads path › should accept downloads in persistent context [fail] +library/downloads-path.spec.ts › downloads path › should delete downloads when context closes [fail] +library/downloads-path.spec.ts › downloads path › should delete downloads when persistent context closes [fail] +library/downloads-path.spec.ts › downloads path › should keep downloadsPath folder [fail] +library/downloads-path.spec.ts › downloads path › should report downloads in downloadsPath folder [fail] +library/downloads-path.spec.ts › downloads path › should report downloads in downloadsPath folder with a relative path [fail] +library/emulation-focus.spec.ts › should change document.activeElement [pass] +library/emulation-focus.spec.ts › should change focused iframe [pass] +library/emulation-focus.spec.ts › should focus popups by default [fail] +library/emulation-focus.spec.ts › should focus with more than one page/context [fail] +library/emulation-focus.spec.ts › should not affect mouse event target page [pass] +library/emulation-focus.spec.ts › should not affect screenshots [fail] +library/emulation-focus.spec.ts › should not fire blur events when interacting with more than one page/context [fail] +library/emulation-focus.spec.ts › should provide target for keyboard events [pass] +library/emulation-focus.spec.ts › should think that all pages are focused @smoke [fail] +library/emulation-focus.spec.ts › should think that it is focused by default [pass] +library/emulation-focus.spec.ts › should trigger hover state concurrently [fail] +library/events/add-listeners.spec.ts › EventEmitter tests › Listener order [pass] +library/events/add-listeners.spec.ts › EventEmitter tests › listener type check [pass] +library/events/add-listeners.spec.ts › EventEmitter tests › set max listeners test [pass] +library/events/add-listeners.spec.ts › EventEmitter tests › should work [pass] +library/events/check-listener-leaks.spec.ts › _maxListeners still has precedence over defaultMaxListeners [pass] +library/events/check-listener-leaks.spec.ts › defaultMaxListeners [pass] +library/events/check-listener-leaks.spec.ts › process-wide [pass] +library/events/events-list.spec.ts › EventEmitter › should maintain event names correctly [pass] +library/events/listener-count.spec.ts › Listener count test [pass] +library/events/listeners-side-effects.spec.ts › listeners empty check [pass] +library/events/listeners.spec.ts › Array copy modification does not modify orig [pass] +library/events/listeners.spec.ts › EventEmitter listeners with one listener [pass] +library/events/listeners.spec.ts › EventEmitter with no members [pass] +library/events/listeners.spec.ts › Modify array copy after multiple adds [pass] +library/events/listeners.spec.ts › listeners and once [pass] +library/events/listeners.spec.ts › listeners on prototype [pass] +library/events/listeners.spec.ts › listeners with conflicting types [pass] +library/events/listeners.spec.ts › raw listeners [pass] +library/events/listeners.spec.ts › raw listeners order [pass] +library/events/max-listeners.spec.ts › emit maxListeners on e [pass] +library/events/method-names.spec.ts › EventEmitter prototype test [pass] +library/events/modify-in-emit.spec.ts › add and remove listeners [pass] +library/events/modify-in-emit.spec.ts › removing callbacks in emit [pass] +library/events/num-args.spec.ts › should work [pass] +library/events/once.spec.ts › once() has different code paths based on the number of arguments being emitted [pass] +library/events/once.spec.ts › should work [pass] +library/events/prepend.spec.ts › EventEmitter functionality [pass] +library/events/prepend.spec.ts › Verify that the listener must be a function [pass] +library/events/remove-all-listeners-wait.spec.ts › should not throw with ignoreErrors [pass] +library/events/remove-all-listeners-wait.spec.ts › should wait [pass] +library/events/remove-all-listeners-wait.spec.ts › should wait all [pass] +library/events/remove-all-listeners-wait.spec.ts › wait should throw [pass] +library/events/remove-all-listeners.spec.ts › listener count after removeAllListeners [pass] +library/events/remove-all-listeners.spec.ts › listeners [pass] +library/events/remove-all-listeners.spec.ts › removeAllListeners on undefined _events [pass] +library/events/remove-all-listeners.spec.ts › removeAllListeners removes all listeners [pass] +library/events/remove-all-listeners.spec.ts › removeAllListeners returns EventEmitter [pass] +library/events/remove-all-listeners.spec.ts › removeAllListeners with no event type [pass] +library/events/remove-listeners.spec.ts › Eighth test [pass] +library/events/remove-listeners.spec.ts › Fifth test [pass] +library/events/remove-listeners.spec.ts › First test [pass] +library/events/remove-listeners.spec.ts › Fourth test [pass] +library/events/remove-listeners.spec.ts › Ninth test [pass] +library/events/remove-listeners.spec.ts › Second test [pass] +library/events/remove-listeners.spec.ts › Seventh test [pass] +library/events/remove-listeners.spec.ts › Sixth test [pass] +library/events/remove-listeners.spec.ts › Tenth test [pass] +library/events/remove-listeners.spec.ts › Third test [pass] +library/events/set-max-listeners-side-effects.spec.ts › set max listeners test [pass] +library/events/special-event-names.spec.ts › should support special event names [pass] +library/events/subclass.spec.ts › MyEE2 instance [pass] +library/events/subclass.spec.ts › myee instance [pass] +library/events/symbols.spec.ts › should support symbols [pass] +library/favicon.spec.ts › should load svg favicon with prefer-color-scheme [unknown] +library/fetch-proxy.spec.ts › context request should pick up proxy credentials [timeout] +library/fetch-proxy.spec.ts › global request should pick up proxy credentials [pass] +library/fetch-proxy.spec.ts › should support proxy.bypass [pass] +library/fetch-proxy.spec.ts › should use socks proxy [pass] +library/fetch-proxy.spec.ts › should work with context level proxy [pass] +library/firefox/launcher.spec.ts › should pass firefox user preferences [fail] +library/firefox/launcher.spec.ts › should pass firefox user preferences in persistent [fail] +library/geolocation.spec.ts › should isolate contexts [timeout] +library/geolocation.spec.ts › should not modify passed default options object [pass] +library/geolocation.spec.ts › should throw when invalid longitude [fail] +library/geolocation.spec.ts › should throw with missing latitude [pass] +library/geolocation.spec.ts › should throw with missing longitude in default options [pass] +library/geolocation.spec.ts › should use context options [timeout] +library/geolocation.spec.ts › should use context options for popup [timeout] +library/geolocation.spec.ts › should work @smoke [timeout] +library/geolocation.spec.ts › watchPosition should be notified [timeout] +library/global-fetch-cookie.spec.ts › should do case-insensitive match of cookie domain [pass] +library/global-fetch-cookie.spec.ts › should do case-insensitive match of request domain [pass] +library/global-fetch-cookie.spec.ts › should export cookies to storage state [pass] +library/global-fetch-cookie.spec.ts › should filter outgoing cookies by domain [pass] +library/global-fetch-cookie.spec.ts › should filter outgoing cookies by path [pass] +library/global-fetch-cookie.spec.ts › should override cookie from Set-Cookie header [pass] +library/global-fetch-cookie.spec.ts › should override cookie from Set-Cookie header even if it expired [pass] +library/global-fetch-cookie.spec.ts › should preserve local storage on import/export of storage state [pass] +library/global-fetch-cookie.spec.ts › should remove cookie with expires far in the past [pass] +library/global-fetch-cookie.spec.ts › should remove cookie with negative max-age [pass] +library/global-fetch-cookie.spec.ts › should remove expired cookies [pass] +library/global-fetch-cookie.spec.ts › should send cookies from storage state [pass] +library/global-fetch-cookie.spec.ts › should send not expired cookies [pass] +library/global-fetch-cookie.spec.ts › should send secure cookie over http for localhost [pass] +library/global-fetch-cookie.spec.ts › should send secure cookie over https [pass] +library/global-fetch-cookie.spec.ts › should store cookie from Set-Cookie header [pass] +library/global-fetch-cookie.spec.ts › should store cookie from Set-Cookie header even if it contains equal signs [pass] +library/global-fetch-cookie.spec.ts › should work with empty storage state [pass] +library/global-fetch-cookie.spec.ts › storage state should round-trip through file [pass] +library/global-fetch.spec.ts › delete should work @smoke [pass] +library/global-fetch.spec.ts › fetch should work @smoke [pass] +library/global-fetch.spec.ts › get should work @smoke [pass] +library/global-fetch.spec.ts › head should work @smoke [pass] +library/global-fetch.spec.ts › patch should work @smoke [pass] +library/global-fetch.spec.ts › post should work @smoke [pass] +library/global-fetch.spec.ts › put should work @smoke [pass] +library/global-fetch.spec.ts › should abort redirected requests when context is disposed [pass] +library/global-fetch.spec.ts › should abort requests when context is disposed [pass] +library/global-fetch.spec.ts › should accept already serialized data as Buffer when content-type is application/json [pass] +library/global-fetch.spec.ts › should be able to construct with context options [pass] +library/global-fetch.spec.ts › should dispose global request [pass] +library/global-fetch.spec.ts › should have nice toString [pass] +library/global-fetch.spec.ts › should json stringify array body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify bool (false) body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify bool body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify literal string undefined body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify null body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify number (falsey) body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify number body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify object body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify string (falsey) body when content-type is application/json [pass] +library/global-fetch.spec.ts › should json stringify string body when content-type is application/json [pass] +library/global-fetch.spec.ts › should keep headers capitalization [pass] +library/global-fetch.spec.ts › should not double stringify array body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify bool (false) body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify bool body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify literal string undefined body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify null body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify number (falsey) body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify number body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify object body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify string (falsey) body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not double stringify string body when content-type is application/json [pass] +library/global-fetch.spec.ts › should not fail on empty body with encoding [pass] +library/global-fetch.spec.ts › should not follow redirects when maxRedirects is set to 0 [pass] +library/global-fetch.spec.ts › should propagate extra http headers with redirects [pass] +library/global-fetch.spec.ts › should propagate ignoreHTTPSErrors on redirects [pass] +library/global-fetch.spec.ts › should remove content-length from redirected post requests [pass] +library/global-fetch.spec.ts › should resolve url relative to global baseURL option [pass] +library/global-fetch.spec.ts › should retry ECONNRESET [pass] +library/global-fetch.spec.ts › should return body for failing requests [pass] +library/global-fetch.spec.ts › should return empty body [pass] +library/global-fetch.spec.ts › should return error with correct credentials and mismatching hostname [pass] +library/global-fetch.spec.ts › should return error with correct credentials and mismatching port [pass] +library/global-fetch.spec.ts › should return error with correct credentials and mismatching scheme [pass] +library/global-fetch.spec.ts › should return error with wrong credentials [pass] +library/global-fetch.spec.ts › should serialize post data on the client [pass] +library/global-fetch.spec.ts › should set playwright as user-agent [pass] +library/global-fetch.spec.ts › should support HTTPCredentials.send [pass] +library/global-fetch.spec.ts › should support WWW-Authenticate: Basic [pass] +library/global-fetch.spec.ts › should support global httpCredentials option [pass] +library/global-fetch.spec.ts › should support global ignoreHTTPSErrors option [pass] +library/global-fetch.spec.ts › should support global timeout option [pass] +library/global-fetch.spec.ts › should support global userAgent option [pass] +library/global-fetch.spec.ts › should throw after dispose [pass] +library/global-fetch.spec.ts › should throw an error when maxRedirects is exceeded [pass] +library/global-fetch.spec.ts › should throw an error when maxRedirects is less than 0 [pass] +library/global-fetch.spec.ts › should work with correct credentials and matching origin [pass] +library/global-fetch.spec.ts › should work with correct credentials and matching origin case insensitive [pass] +library/har.spec.ts › should attach content [fail] +library/har.spec.ts › should calculate time [pass] +library/har.spec.ts › should contain http2 for http2 requests [fail] +library/har.spec.ts › should filter by glob [pass] +library/har.spec.ts › should filter by regexp [pass] +library/har.spec.ts › should filter favicon and favicon redirects [unknown] +library/har.spec.ts › should have -1 _transferSize when its a failed request [pass] +library/har.spec.ts › should have browser [fail] +library/har.spec.ts › should have connection details [fail] +library/har.spec.ts › should have connection details for failed requests [fail] +library/har.spec.ts › should have connection details for redirects [fail] +library/har.spec.ts › should have different hars for concurrent contexts [pass] +library/har.spec.ts › should have pages [pass] +library/har.spec.ts › should have pages in persistent context [pass] +library/har.spec.ts › should have popup requests [fail] +library/har.spec.ts › should have security details [fail] +library/har.spec.ts › should have version and creator [pass] +library/har.spec.ts › should include API request [pass] +library/har.spec.ts › should include binary postData [fail] +library/har.spec.ts › should include content @smoke [fail] +library/har.spec.ts › should include cookies [pass] +library/har.spec.ts › should include form params [fail] +library/har.spec.ts › should include postData [fail] +library/har.spec.ts › should include query params [pass] +library/har.spec.ts › should include redirectURL [pass] +library/har.spec.ts › should include redirects from API request [pass] +library/har.spec.ts › should include request [pass] +library/har.spec.ts › should include response [pass] +library/har.spec.ts › should include secure set-cookies [fail] +library/har.spec.ts › should include set-cookies [fail] +library/har.spec.ts › should include set-cookies with comma [fail] +library/har.spec.ts › should include sizes [fail] +library/har.spec.ts › should not contain internal pages [pass] +library/har.spec.ts › should not hang on resources served from cache [pass] +library/har.spec.ts › should not hang on slow chunked response [fail] +library/har.spec.ts › should omit content [pass] +library/har.spec.ts › should omit content legacy [pass] +library/har.spec.ts › should record failed request headers [pass] +library/har.spec.ts › should record failed request overrides [timeout] +library/har.spec.ts › should record request overrides [timeout] +library/har.spec.ts › should report the correct _transferSize with PNG files [fail] +library/har.spec.ts › should report the correct request body size [pass] +library/har.spec.ts › should report the correct request body size when the bodySize is 0 [pass] +library/har.spec.ts › should report the correct response body size when the bodySize is 0 [pass] +library/har.spec.ts › should respect minimal mode for API Requests [pass] +library/har.spec.ts › should return receive time [fail] +library/har.spec.ts › should return security details directly from response [fail] +library/har.spec.ts › should return server address directly from response [fail] +library/har.spec.ts › should skip invalid Expires [pass] +library/har.spec.ts › should throw without path [pass] +library/har.spec.ts › should use attach mode for zip extension [fail] +library/har.spec.ts › should work with gzip compression [fail] +library/headful.spec.ts › Page.bringToFront should work [fail] +library/headful.spec.ts › headless and headful should use same default fonts [fail] +library/headful.spec.ts › should click background tab [fail] +library/headful.spec.ts › should click bottom row w/ infobar in OOPIF [timeout] +library/headful.spec.ts › should click in OOPIF [fail] +library/headful.spec.ts › should click when viewport size is larger than screen [fail] +library/headful.spec.ts › should close browser after context menu was triggered [pass] +library/headful.spec.ts › should close browser with beforeunload page [pass] +library/headful.spec.ts › should close browsercontext with pending beforeunload dialog [fail] +library/headful.spec.ts › should dispatch click events to oversized viewports [pass] +library/headful.spec.ts › should have default url when launching browser @smoke [pass] +library/headful.spec.ts › should not block third party SameSite=None cookies [fail] +library/headful.spec.ts › should not crash when creating second context [pass] +library/headful.spec.ts › should not override viewport size when passed null [pass] +library/headful.spec.ts › should(not) block third party cookies [pass] +library/hit-target.spec.ts › should block all events when hit target is wrong [pass] +library/hit-target.spec.ts › should block all events when hit target is wrong and element detaches [pass] +library/hit-target.spec.ts › should block click when mousedown fails [pass] +library/hit-target.spec.ts › should click an element inside closed shadow root [fail] +library/hit-target.spec.ts › should click in custom element [fail] +library/hit-target.spec.ts › should click in iframe with padding [fail] +library/hit-target.spec.ts › should click in iframe with padding 2 [fail] +library/hit-target.spec.ts › should click into frame inside closed shadow root [fail] +library/hit-target.spec.ts › should click the button again after document.write [pass] +library/hit-target.spec.ts › should click when element detaches in mousedown [pass] +library/hit-target.spec.ts › should detect overlaid element in a transformed iframe [fail] +library/hit-target.spec.ts › should detect overlay from another shadow root [fail] +library/hit-target.spec.ts › should not block programmatic events [pass] +library/hit-target.spec.ts › should not click an element overlaying iframe with the target [fail] +library/hit-target.spec.ts › should not click iframe overlaying the target [fail] +library/hit-target.spec.ts › should work with block inside inline [fail] +library/hit-target.spec.ts › should work with block inside inline in shadow dom [fail] +library/hit-target.spec.ts › should work with block-block-block inside inline-inline [fail] +library/hit-target.spec.ts › should work with drag and drop that moves the element under cursor [pass] +library/hit-target.spec.ts › should work with mui select [pass] +library/ignorehttpserrors.spec.ts › serviceWorker should intercept document request [fail] +library/ignorehttpserrors.spec.ts › should fail with WebSocket if not ignored [pass] +library/ignorehttpserrors.spec.ts › should isolate contexts [fail] +library/ignorehttpserrors.spec.ts › should work @smoke [fail] +library/ignorehttpserrors.spec.ts › should work with WebSocket [fail] +library/ignorehttpserrors.spec.ts › should work with mixed content [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should attribute navigation to click [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should await popup [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should check [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should check a radio button [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should check with keyboard [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should click [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should click after same-document navigation [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should click button with nested div [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should double click [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should emit single keyup on ArrowDown [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should fill [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should fill [contentEditable] [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should fill japanese text [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should fill textarea [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should fill textarea with new lines at the end [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should ignore AltGraph [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should ignore programmatic events [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should make a positioned click on a canvas [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should middle click [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should not target selector preview by text regexp [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should not throw csp directive violation errors [pass] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should press [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should record ArrowDown [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should record omnibox navigations after performAction [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should record omnibox navigations after recordAction [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should record slider [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should select [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should select with size attribute [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should uncheck [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should update selected element after pressing Tab [fail] +library/inspector/cli-codegen-1.spec.ts › cli codegen › should work with TrustedTypes [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › click should emit events in order [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should --save-trace [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should check input with chaining id [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should clear files [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should contain close page [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should contain open page [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should contain second page [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should download files [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should fill tricky characters [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should handle dialogs [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should handle history.postData [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should not clash pages [timeout] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should not lead to an error if html gets clicked [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should record navigations after identical pushState [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should record open in a new tab with url [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should reset hover model on action when element detaches [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should save assets via SIGINT [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should update active model on action [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should update hover model on action [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should upload a single file [fail] +library/inspector/cli-codegen-2.spec.ts › cli codegen › should upload multiple files [fail] +library/inspector/cli-codegen-2.spec.ts › should --test-id-attribute [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should assert value [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should assert value on disabled input [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should assert value on disabled select [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should assert visibility [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should click locator.first [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should click locator.nth [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should consume contextmenu events, despite a custom context menu [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should consume pointer events [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate frame locators [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate frame locators with id attribute [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate frame locators with name attribute [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate frame locators with special characters in name attribute [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate frame locators with testId [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate frame locators with title attribute [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate getByAltText [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate getByLabel [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate getByLabel without regex [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate getByPlaceholder [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate getByTestId [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate role locators undef frame locators [fail] +library/inspector/cli-codegen-3.spec.ts › cli codegen › should keep toolbar visible even if webpage erases content in hydration [fail] +library/inspector/cli-codegen-csharp.spec.ts › should not print context options method override in mstest if no options were passed [fail] +library/inspector/cli-codegen-csharp.spec.ts › should not print context options method override in nunit if no options were passed [fail] +library/inspector/cli-codegen-csharp.spec.ts › should print a valid basic program in mstest [fail] +library/inspector/cli-codegen-csharp.spec.ts › should print a valid basic program in nunit [fail] +library/inspector/cli-codegen-csharp.spec.ts › should print context options method override in mstest if options were passed [fail] +library/inspector/cli-codegen-csharp.spec.ts › should print context options method override in nunit if options were passed [fail] +library/inspector/cli-codegen-csharp.spec.ts › should print load/save storageState [fail] +library/inspector/cli-codegen-csharp.spec.ts › should print the correct context options for custom settings [fail] +library/inspector/cli-codegen-csharp.spec.ts › should print the correct context options when using a device [unknown] +library/inspector/cli-codegen-csharp.spec.ts › should print the correct context options when using a device and additional options [unknown] +library/inspector/cli-codegen-csharp.spec.ts › should print the correct imports and context options [fail] +library/inspector/cli-codegen-csharp.spec.ts › should work with --save-har [fail] +library/inspector/cli-codegen-java.spec.ts › should print a valid basic program in junit [fail] +library/inspector/cli-codegen-java.spec.ts › should print load/save storage_state [fail] +library/inspector/cli-codegen-java.spec.ts › should print the correct context options for custom settings [fail] +library/inspector/cli-codegen-java.spec.ts › should print the correct context options when using a device [unknown] +library/inspector/cli-codegen-java.spec.ts › should print the correct context options when using a device and additional options [unknown] +library/inspector/cli-codegen-java.spec.ts › should print the correct imports and context options [fail] +library/inspector/cli-codegen-java.spec.ts › should print the correct imports in junit [fail] +library/inspector/cli-codegen-java.spec.ts › should work with --save-har [fail] +library/inspector/cli-codegen-javascript.spec.ts › should print load/save storageState [fail] +library/inspector/cli-codegen-javascript.spec.ts › should print the correct context options for custom settings [fail] +library/inspector/cli-codegen-javascript.spec.ts › should print the correct context options when using a device [unknown] +library/inspector/cli-codegen-javascript.spec.ts › should print the correct context options when using a device and additional options [unknown] +library/inspector/cli-codegen-javascript.spec.ts › should print the correct imports and context options [fail] +library/inspector/cli-codegen-javascript.spec.ts › should save the codegen output to a file if specified [fail] +library/inspector/cli-codegen-pytest.spec.ts › should print the correct context options when using a device and lang [unknown] +library/inspector/cli-codegen-pytest.spec.ts › should print the correct imports and context options [fail] +library/inspector/cli-codegen-pytest.spec.ts › should save the codegen output to a file if specified [fail] +library/inspector/cli-codegen-python-async.spec.ts › should print load/save storage_state [fail] +library/inspector/cli-codegen-python-async.spec.ts › should print the correct context options for custom settings [fail] +library/inspector/cli-codegen-python-async.spec.ts › should print the correct context options when using a device [unknown] +library/inspector/cli-codegen-python-async.spec.ts › should print the correct context options when using a device and additional options [unknown] +library/inspector/cli-codegen-python-async.spec.ts › should print the correct imports and context options [fail] +library/inspector/cli-codegen-python-async.spec.ts › should save the codegen output to a file if specified [fail] +library/inspector/cli-codegen-python-async.spec.ts › should work with --save-har [fail] +library/inspector/cli-codegen-python.spec.ts › should print load/save storage_state [fail] +library/inspector/cli-codegen-python.spec.ts › should print the correct context options for custom settings [fail] +library/inspector/cli-codegen-python.spec.ts › should print the correct context options when using a device [unknown] +library/inspector/cli-codegen-python.spec.ts › should print the correct context options when using a device and additional options [unknown] +library/inspector/cli-codegen-python.spec.ts › should print the correct imports and context options [fail] +library/inspector/cli-codegen-python.spec.ts › should save the codegen output to a file if specified [fail] +library/inspector/cli-codegen-test.spec.ts › should print load storageState [fail] +library/inspector/cli-codegen-test.spec.ts › should print the correct context options for custom settings [fail] +library/inspector/cli-codegen-test.spec.ts › should print the correct context options when using a device [unknown] +library/inspector/cli-codegen-test.spec.ts › should print the correct context options when using a device and additional options [unknown] +library/inspector/cli-codegen-test.spec.ts › should print the correct imports and context options [fail] +library/inspector/cli-codegen-test.spec.ts › should work with --save-har [fail] +library/inspector/console-api.spec.ts › expected properties on playwright object [pass] +library/inspector/console-api.spec.ts › should support locator.and() [fail] +library/inspector/console-api.spec.ts › should support locator.or() [fail] +library/inspector/console-api.spec.ts › should support playwright.$, playwright.$$ [pass] +library/inspector/console-api.spec.ts › should support playwright.getBy* [fail] +library/inspector/console-api.spec.ts › should support playwright.locator({ has }) [fail] +library/inspector/console-api.spec.ts › should support playwright.locator({ hasNot }) [fail] +library/inspector/console-api.spec.ts › should support playwright.locator.value [fail] +library/inspector/console-api.spec.ts › should support playwright.locator.values [fail] +library/inspector/console-api.spec.ts › should support playwright.selector [pass] +library/inspector/pause.spec.ts › pause › should hide internal calls [pass] +library/inspector/pause.spec.ts › pause › should highlight locators with custom testId [fail] +library/inspector/pause.spec.ts › pause › should highlight on explore [fail] +library/inspector/pause.spec.ts › pause › should highlight on explore (csharp) [fail] +library/inspector/pause.spec.ts › pause › should highlight pointer, only in main frame [fail] +library/inspector/pause.spec.ts › pause › should highlight waitForEvent [fail] +library/inspector/pause.spec.ts › pause › should not prevent key events [fail] +library/inspector/pause.spec.ts › pause › should pause after a navigation [pass] +library/inspector/pause.spec.ts › pause › should pause and resume the script [pass] +library/inspector/pause.spec.ts › pause › should pause and resume the script with keyboard shortcut [pass] +library/inspector/pause.spec.ts › pause › should pause on context close [pass] +library/inspector/pause.spec.ts › pause › should pause on next pause [pass] +library/inspector/pause.spec.ts › pause › should pause on page close [pass] +library/inspector/pause.spec.ts › pause › should populate log [fail] +library/inspector/pause.spec.ts › pause › should populate log with error [fail] +library/inspector/pause.spec.ts › pause › should populate log with error in waitForEvent [fail] +library/inspector/pause.spec.ts › pause › should populate log with waitForEvent [fail] +library/inspector/pause.spec.ts › pause › should resume from console [pass] +library/inspector/pause.spec.ts › pause › should show expect.toHaveText [fail] +library/inspector/pause.spec.ts › pause › should show source [pass] +library/inspector/pause.spec.ts › pause › should skip input when resuming [fail] +library/inspector/pause.spec.ts › pause › should step [fail] +library/inspector/pause.spec.ts › pause › should step with keyboard shortcut [fail] +library/inspector/pause.spec.ts › should not reset timeouts [pass] +library/inspector/pause.spec.ts › should resume when closing inspector [pass] +library/launcher.spec.ts › should have a devices object [pass] +library/launcher.spec.ts › should have an errors object [pass] +library/launcher.spec.ts › should kill browser process on timeout after close [pass] +library/launcher.spec.ts › should throw a friendly error if its headed and there is no xserver on linux running [fail] +library/locator-generator.spec.ts › asLocator internal:and [pass] +library/locator-generator.spec.ts › asLocator internal:chain [pass] +library/locator-generator.spec.ts › asLocator internal:or [pass] +library/locator-generator.spec.ts › asLocator xpath [pass] +library/locator-generator.spec.ts › generate multiple locators [pass] +library/locator-generator.spec.ts › parse locators strictly [pass] +library/locator-generator.spec.ts › parseLocator css [pass] +library/locator-generator.spec.ts › parseLocator quotes [pass] +library/locator-generator.spec.ts › reverse engineer frameLocator [pass] +library/locator-generator.spec.ts › reverse engineer getByRole [pass] +library/locator-generator.spec.ts › reverse engineer has [pass] +library/locator-generator.spec.ts › reverse engineer has + hasText [pass] +library/locator-generator.spec.ts › reverse engineer hasNot [pass] +library/locator-generator.spec.ts › reverse engineer hasNotText [pass] +library/locator-generator.spec.ts › reverse engineer hasText [pass] +library/locator-generator.spec.ts › reverse engineer ignore-case locators [pass] +library/locator-generator.spec.ts › reverse engineer internal:has-text locators [fail] +library/locator-generator.spec.ts › reverse engineer locators [pass] +library/locator-generator.spec.ts › reverse engineer locators with regex [pass] +library/locator-generator.spec.ts › reverse engineer ordered locators [pass] +library/logger.spec.ts › should log @smoke [pass] +library/logger.spec.ts › should log context-level [fail] +library/modernizr.spec.ts › Mobile Safari [unknown] +library/modernizr.spec.ts › Safari Desktop [unknown] +library/page-clock.frozen.spec.ts › clock should be frozen [unknown] +library/page-clock.frozen.spec.ts › clock should be realtime [unknown] +library/page-clock.spec.ts › Date.now › check Date.now is an integer [pass] +library/page-clock.spec.ts › Date.now › check Date.now is an integer (2) [pass] +library/page-clock.spec.ts › fastForward › ignores timers which wouldn't be run [pass] +library/page-clock.spec.ts › fastForward › pushes back execution time for skipped timers [pass] +library/page-clock.spec.ts › fastForward › supports string time arguments [pass] +library/page-clock.spec.ts › popup › should not run time before popup on pause [pass] +library/page-clock.spec.ts › popup › should run time before popup [timeout] +library/page-clock.spec.ts › popup › should tick after popup [timeout] +library/page-clock.spec.ts › popup › should tick before popup [timeout] +library/page-clock.spec.ts › runFor › creates updated Date while ticking [pass] +library/page-clock.spec.ts › runFor › does not trigger without sufficient delay [pass] +library/page-clock.spec.ts › runFor › passes 1 minute [pass] +library/page-clock.spec.ts › runFor › passes 2 hours, 34 minutes and 10 seconds [pass] +library/page-clock.spec.ts › runFor › passes 8 seconds [pass] +library/page-clock.spec.ts › runFor › returns the current now value [pass] +library/page-clock.spec.ts › runFor › throws for invalid format [pass] +library/page-clock.spec.ts › runFor › triggers after sufficient delay [pass] +library/page-clock.spec.ts › runFor › triggers event when some throw [fail] +library/page-clock.spec.ts › runFor › triggers immediately without specified delay [pass] +library/page-clock.spec.ts › runFor › triggers multiple simultaneous timers [pass] +library/page-clock.spec.ts › runFor › triggers simultaneous timers [pass] +library/page-clock.spec.ts › runFor › waits after setTimeout was called [pass] +library/page-clock.spec.ts › setFixedTime › allows installing fake timers after settings time [pass] +library/page-clock.spec.ts › setFixedTime › allows setting time multiple times [pass] +library/page-clock.spec.ts › setFixedTime › does not fake methods [pass] +library/page-clock.spec.ts › setFixedTime › fixed time is not affected by clock manipulation [pass] +library/page-clock.spec.ts › stubTimers › fakes Date constructor [pass] +library/page-clock.spec.ts › stubTimers › global fake setTimeout should return id [pass] +library/page-clock.spec.ts › stubTimers › replaces global clearInterval [pass] +library/page-clock.spec.ts › stubTimers › replaces global clearTimeout [pass] +library/page-clock.spec.ts › stubTimers › replaces global performance.now [pass] +library/page-clock.spec.ts › stubTimers › replaces global performance.timeOrigin [pass] +library/page-clock.spec.ts › stubTimers › replaces global setInterval [pass] +library/page-clock.spec.ts › stubTimers › replaces global setTimeout [pass] +library/page-clock.spec.ts › stubTimers › sets initial timestamp [pass] +library/page-clock.spec.ts › stubTimers › should throw for invalid date [pass] +library/page-clock.spec.ts › while on pause › fastForward should not run nested immediate [pass] +library/page-clock.spec.ts › while on pause › runFor should not run nested immediate [pass] +library/page-clock.spec.ts › while on pause › runFor should not run nested immediate from microtask [pass] +library/page-clock.spec.ts › while running › should fastForward [pass] +library/page-clock.spec.ts › while running › should fastForwardTo [pass] +library/page-clock.spec.ts › while running › should pause [pass] +library/page-clock.spec.ts › while running › should pause and fastForward [pass] +library/page-clock.spec.ts › while running › should progress time [pass] +library/page-clock.spec.ts › while running › should runFor [pass] +library/page-clock.spec.ts › while running › should set system time on pause [pass] +library/page-event-crash.spec.ts › should be able to close context when page crashes [fail] +library/page-event-crash.spec.ts › should cancel navigation when page crashes [fail] +library/page-event-crash.spec.ts › should cancel waitForEvent when page crashes [fail] +library/page-event-crash.spec.ts › should emit crash event when page crashes [fail] +library/page-event-crash.spec.ts › should throw on any action after page crashes [fail] +library/pdf.spec.ts › should be able to generate outline [unknown] +library/pdf.spec.ts › should be able to save file [unknown] +library/permissions.spec.ts › permissions › should accumulate when adding [fail] +library/permissions.spec.ts › permissions › should be prompt by default [pass] +library/permissions.spec.ts › permissions › should clear permissions [fail] +library/permissions.spec.ts › permissions › should deny permission when not listed [fail] +library/permissions.spec.ts › permissions › should fail when bad permission is given [fail] +library/permissions.spec.ts › permissions › should grant geolocation permission when origin is listed [fail] +library/permissions.spec.ts › permissions › should grant notifications permission when listed [fail] +library/permissions.spec.ts › permissions › should grant permission when creating context [fail] +library/permissions.spec.ts › permissions › should grant permission when listed for all domains [fail] +library/permissions.spec.ts › permissions › should isolate permissions between browser contexts [fail] +library/permissions.spec.ts › permissions › should prompt for geolocation permission when origin is not listed [pass] +library/permissions.spec.ts › permissions › should reset permissions [fail] +library/permissions.spec.ts › permissions › should trigger permission onchange [fail] +library/permissions.spec.ts › should support clipboard read [fail] +library/permissions.spec.ts › storage access [unknown] +library/popup.spec.ts › BrowserContext.addInitScript should apply to a cross-process popup [pass] +library/popup.spec.ts › BrowserContext.addInitScript should apply to an in-process popup [fail] +library/popup.spec.ts › should expose function from browser context [fail] +library/popup.spec.ts › should inherit extra headers from browser context [fail] +library/popup.spec.ts › should inherit http credentials from browser context [pass] +library/popup.spec.ts › should inherit offline from browser context [fail] +library/popup.spec.ts › should inherit touch support from browser context [fail] +library/popup.spec.ts › should inherit user agent from browser context @smoke [fail] +library/popup.spec.ts › should inherit viewport size from browser context [fail] +library/popup.spec.ts › should not dispatch binding on a closed page [fail] +library/popup.spec.ts › should not throttle rAF in the opener page [timeout] +library/popup.spec.ts › should not throw when click closes popup [timeout] +library/popup.spec.ts › should respect routes from browser context [fail] +library/popup.spec.ts › should respect routes from browser context when using window.open [pass] +library/popup.spec.ts › should use viewport size from window features [timeout] +library/proxy-pattern.spec.ts › socks proxy patter matcher [pass] +library/proxy.spec.ts › does launch without a port [pass] +library/proxy.spec.ts › should authenticate [fail] +library/proxy.spec.ts › should exclude patterns [pass] +library/proxy.spec.ts › should proxy local network requests › by default › link-local [pass] +library/proxy.spec.ts › should proxy local network requests › by default › localhost [fail] +library/proxy.spec.ts › should proxy local network requests › by default › loopback address [fail] +library/proxy.spec.ts › should proxy local network requests › with other bypasses › link-local [pass] +library/proxy.spec.ts › should proxy local network requests › with other bypasses › localhost [fail] +library/proxy.spec.ts › should proxy local network requests › with other bypasses › loopback address [fail] +library/proxy.spec.ts › should throw for bad server value [pass] +library/proxy.spec.ts › should use SOCKS proxy for websocket requests [pass] +library/proxy.spec.ts › should use proxy @smoke [fail] +library/proxy.spec.ts › should use proxy for second page [fail] +library/proxy.spec.ts › should use proxy with emulated user agent [unknown] +library/proxy.spec.ts › should use socks proxy [fail] +library/proxy.spec.ts › should use socks proxy in second page [fail] +library/proxy.spec.ts › should work with IP:PORT notion [fail] +library/proxy.spec.ts › should work with authenticate followed by redirect [fail] +library/resource-timing.spec.ts › should work @smoke [pass] +library/resource-timing.spec.ts › should work for SSL [fail] +library/resource-timing.spec.ts › should work for redirect [pass] +library/resource-timing.spec.ts › should work for subresource [pass] +library/resource-timing.spec.ts › should work when serving from memory cache [pass] +library/role-utils.spec.ts › accessible name nested treeitem [fail] +library/role-utils.spec.ts › accessible name with slots [fail] +library/role-utils.spec.ts › axe-core accessible-text [fail] +library/role-utils.spec.ts › axe-core implicit-role [fail] +library/role-utils.spec.ts › control embedded in a label [fail] +library/role-utils.spec.ts › control embedded in a target element [fail] +library/role-utils.spec.ts › display:contents should be visible when contents are visible [fail] +library/role-utils.spec.ts › label/labelled-by aria-hidden with descendants [fail] +library/role-utils.spec.ts › native controls [fail] +library/role-utils.spec.ts › native controls labelled-by [fail] +library/role-utils.spec.ts › own aria-label concatenated with aria-labelledby [fail] +library/role-utils.spec.ts › should ignore stylesheet from hidden aria-labelledby subtree [fail] +library/role-utils.spec.ts › should not include hidden pseudo into accessible name [fail] +library/role-utils.spec.ts › should work with form and tricky input names [fail] +library/role-utils.spec.ts › svg role=presentation [fail] +library/role-utils.spec.ts › svg title [fail] +library/role-utils.spec.ts › wpt accname #0 [pass] +library/role-utils.spec.ts › wpt accname #1 [pass] +library/role-utils.spec.ts › wpt accname #2 [fail] +library/role-utils.spec.ts › wpt accname #3 [pass] +library/role-utils.spec.ts › wpt accname non-manual [pass] +library/route-web-socket.spec.ts › no-match › should work when connection errors out [pass] +library/route-web-socket.spec.ts › no-match › should work with binaryType=arraybuffer [pass] +library/route-web-socket.spec.ts › no-match › should work with binaryType=blob [pass] +library/route-web-socket.spec.ts › no-match › should work with client-side close [pass] +library/route-web-socket.spec.ts › no-match › should work with error after successful open [fail] +library/route-web-socket.spec.ts › no-match › should work with text message [pass] +library/route-web-socket.spec.ts › no-mock › should work when connection errors out [pass] +library/route-web-socket.spec.ts › no-mock › should work with binaryType=arraybuffer [pass] +library/route-web-socket.spec.ts › no-mock › should work with binaryType=blob [pass] +library/route-web-socket.spec.ts › no-mock › should work with client-side close [pass] +library/route-web-socket.spec.ts › no-mock › should work with error after successful open [fail] +library/route-web-socket.spec.ts › no-mock › should work with text message [pass] +library/route-web-socket.spec.ts › pass-through › should work when connection errors out [pass] +library/route-web-socket.spec.ts › pass-through › should work with binaryType=arraybuffer [pass] +library/route-web-socket.spec.ts › pass-through › should work with binaryType=blob [pass] +library/route-web-socket.spec.ts › pass-through › should work with client-side close [pass] +library/route-web-socket.spec.ts › pass-through › should work with error after successful open [fail] +library/route-web-socket.spec.ts › pass-through › should work with text message [pass] +library/route-web-socket.spec.ts › should emit close upon frame detach [pass] +library/route-web-socket.spec.ts › should emit close upon frame navigation [pass] +library/route-web-socket.spec.ts › should not throw after page closure [pass] +library/route-web-socket.spec.ts › should pattern match [pass] +library/route-web-socket.spec.ts › should route on context [pass] +library/route-web-socket.spec.ts › should work with server [pass] +library/route-web-socket.spec.ts › should work with ws.close [pass] +library/route-web-socket.spec.ts › should work without server [pass] +library/screenshot.spec.ts › element screenshot › element screenshot should work with a mobile viewport [fail] +library/screenshot.spec.ts › element screenshot › element screenshot should work with device scale factor [fail] +library/screenshot.spec.ts › element screenshot › element screenshots should handle vh units [pass] +library/screenshot.spec.ts › element screenshot › page screenshot should capture css transform with device pixels [fail] +library/screenshot.spec.ts › element screenshot › should capture full element when larger than viewport with device scale factor [fail] +library/screenshot.spec.ts › element screenshot › should capture full element when larger than viewport with device scale factor and scale:css [fail] +library/screenshot.spec.ts › element screenshot › should restore default viewport after fullPage screenshot [pass] +library/screenshot.spec.ts › element screenshot › should restore viewport after element screenshot and exception [fail] +library/screenshot.spec.ts › element screenshot › should restore viewport after page screenshot and exception [pass] +library/screenshot.spec.ts › element screenshot › should restore viewport after page screenshot and timeout [pass] +library/screenshot.spec.ts › element screenshot › should take element screenshot when default viewport is null and restore back [fail] +library/screenshot.spec.ts › element screenshot › should take fullPage screenshots when default viewport is null [pass] +library/screenshot.spec.ts › element screenshot › should take screenshots when default viewport is null [fail] +library/screenshot.spec.ts › element screenshot › should work if the main resource hangs [fail] +library/screenshot.spec.ts › page screenshot › should handle vh units [pass] +library/screenshot.spec.ts › page screenshot › should run in parallel in multiple pages [fail] +library/screenshot.spec.ts › page screenshot › should throw if screenshot size is too large with device scale factor [fail] +library/screenshot.spec.ts › page screenshot › should work with a mobile viewport [fail] +library/screenshot.spec.ts › page screenshot › should work with a mobile viewport and clip [fail] +library/screenshot.spec.ts › page screenshot › should work with a mobile viewport and fullPage [fail] +library/screenshot.spec.ts › page screenshot › should work with device scale factor [fail] +library/screenshot.spec.ts › page screenshot › should work with device scale factor and clip [fail] +library/screenshot.spec.ts › page screenshot › should work with device scale factor and scale:css [fail] +library/screenshot.spec.ts › page screenshot › should work with device scale factor, clip and scale:css [fail] +library/screenshot.spec.ts › page screenshot › should work with large size [pass] +library/selector-generator.spec.ts › selector generator › should accept valid aria-label for candidate consideration [fail] +library/selector-generator.spec.ts › selector generator › should accept valid data-test-id for candidate consideration [fail] +library/selector-generator.spec.ts › selector generator › should chain text after parent [fail] +library/selector-generator.spec.ts › selector generator › should escape text with quote [fail] +library/selector-generator.spec.ts › selector generator › should escape text with slash [fail] +library/selector-generator.spec.ts › selector generator › should find text in shadow dom [fail] +library/selector-generator.spec.ts › selector generator › should generate exact label when necessary [fail] +library/selector-generator.spec.ts › selector generator › should generate exact placeholder when necessary [fail] +library/selector-generator.spec.ts › selector generator › should generate exact role when necessary [fail] +library/selector-generator.spec.ts › selector generator › should generate exact text when necessary [fail] +library/selector-generator.spec.ts › selector generator › should generate exact title when necessary [fail] +library/selector-generator.spec.ts › selector generator › should generate label selector [fail] +library/selector-generator.spec.ts › selector generator › should generate multiple: noId [fail] +library/selector-generator.spec.ts › selector generator › should generate multiple: noId noText [fail] +library/selector-generator.spec.ts › selector generator › should generate multiple: noText in role [fail] +library/selector-generator.spec.ts › selector generator › should generate multiple: noText in text [fail] +library/selector-generator.spec.ts › selector generator › should generate relative selector [fail] +library/selector-generator.spec.ts › selector generator › should generate text and normalize whitespace [fail] +library/selector-generator.spec.ts › selector generator › should generate text for <input type=button> [fail] +library/selector-generator.spec.ts › selector generator › should generate title selector [fail] +library/selector-generator.spec.ts › selector generator › should handle first non-unique data-testid [fail] +library/selector-generator.spec.ts › selector generator › should handle second non-unique data-testid [fail] +library/selector-generator.spec.ts › selector generator › should ignore empty aria-label for candidate consideration [fail] +library/selector-generator.spec.ts › selector generator › should ignore empty data-test-id for candidate consideration [fail] +library/selector-generator.spec.ts › selector generator › should ignore empty role for candidate consideration [fail] +library/selector-generator.spec.ts › selector generator › should match in deep shadow dom [fail] +library/selector-generator.spec.ts › selector generator › should match in shadow dom [fail] +library/selector-generator.spec.ts › selector generator › should not accept invalid role for candidate consideration [fail] +library/selector-generator.spec.ts › selector generator › should not escape spaces inside named attr selectors [fail] +library/selector-generator.spec.ts › selector generator › should not escape text with >> [fail] +library/selector-generator.spec.ts › selector generator › should not improve guid text [fail] +library/selector-generator.spec.ts › selector generator › should not prefer zero-sized button over inner span [fail] +library/selector-generator.spec.ts › selector generator › should not use generated id [fail] +library/selector-generator.spec.ts › selector generator › should not use input[value] [fail] +library/selector-generator.spec.ts › selector generator › should not use text for select [fail] +library/selector-generator.spec.ts › selector generator › should prefer button over inner span [fail] +library/selector-generator.spec.ts › selector generator › should prefer data-testid [fail] +library/selector-generator.spec.ts › selector generator › should prefer role other input[type] [fail] +library/selector-generator.spec.ts › selector generator › should prefer role=button over inner span [fail] +library/selector-generator.spec.ts › selector generator › should prioritise attributes correctly › name [fail] +library/selector-generator.spec.ts › selector generator › should prioritise attributes correctly › placeholder [fail] +library/selector-generator.spec.ts › selector generator › should prioritise attributes correctly › role [fail] +library/selector-generator.spec.ts › selector generator › should prioritise attributes correctly › type [fail] +library/selector-generator.spec.ts › selector generator › should properly join child selectors under nested ordinals [fail] +library/selector-generator.spec.ts › selector generator › should separate selectors by >> [fail] +library/selector-generator.spec.ts › selector generator › should trim long text [fail] +library/selector-generator.spec.ts › selector generator › should trim text [fail] +library/selector-generator.spec.ts › selector generator › should try to improve label text by shortening [fail] +library/selector-generator.spec.ts › selector generator › should try to improve role name [fail] +library/selector-generator.spec.ts › selector generator › should try to improve text [fail] +library/selector-generator.spec.ts › selector generator › should try to improve text by shortening [fail] +library/selector-generator.spec.ts › selector generator › should use data-testid in strict errors [fail] +library/selector-generator.spec.ts › selector generator › should use internal:has-text [fail] +library/selector-generator.spec.ts › selector generator › should use internal:has-text with regexp [fail] +library/selector-generator.spec.ts › selector generator › should use internal:has-text with regexp with a quote [fail] +library/selector-generator.spec.ts › selector generator › should use nested ordinals [fail] +library/selector-generator.spec.ts › selector generator › should use ordinal for identical nodes [fail] +library/selector-generator.spec.ts › selector generator › should use parent text [fail] +library/selector-generator.spec.ts › selector generator › should use readable id [fail] +library/selector-generator.spec.ts › selector generator › should use the name attributes for elements that can have it [fail] +library/selector-generator.spec.ts › selector generator › should work in dynamic iframes without navigation [fail] +library/selector-generator.spec.ts › selector generator › should work with tricky attributes [fail] +library/selector-generator.spec.ts › selector generator › should work without CSS.escape [fail] +library/selectors-register.spec.ts › should handle errors [pass] +library/selectors-register.spec.ts › should not rely on engines working from the root [fail] +library/selectors-register.spec.ts › should throw a nice error if the selector returns a bad value [pass] +library/selectors-register.spec.ts › should work [fail] +library/selectors-register.spec.ts › should work in main and isolated world [fail] +library/selectors-register.spec.ts › should work when registered on global [fail] +library/selectors-register.spec.ts › should work with path [fail] +library/shared-worker.spec.ts › should survive shared worker restart [pass] +library/signals.spec.ts › should close the browser when the node process closes [timeout] +library/signals.spec.ts › should remove temp dir on process.exit [timeout] +library/signals.spec.ts › signals › should close the browser on SIGHUP [timeout] +library/signals.spec.ts › signals › should close the browser on SIGINT [timeout] +library/signals.spec.ts › signals › should close the browser on SIGTERM [timeout] +library/signals.spec.ts › signals › should kill the browser on SIGINT + SIGTERM [timeout] +library/signals.spec.ts › signals › should kill the browser on SIGTERM + SIGINT [timeout] +library/signals.spec.ts › signals › should kill the browser on double SIGINT and remove temp dir [timeout] +library/signals.spec.ts › signals › should not prevent default SIGTERM handling after browser close [timeout] +library/signals.spec.ts › signals › should report browser close signal 2 [timeout] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo check [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo click [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo dblclick [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo dispatchEvent [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo fill [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo focus [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo hover [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo press [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo selectOption [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo setInputFiles [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo type [fail] +library/slowmo.spec.ts › slowMo › ElementHandle SlowMo uncheck [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo check [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo click [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo dblclick [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo dispatchEvent [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo fill [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo focus [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo goto [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo hover [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo press [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo selectOption [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo setInputFiles [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo type [fail] +library/slowmo.spec.ts › slowMo › Frame SlowMo uncheck [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo check [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo click [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo dblclick [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo dispatchEvent [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo fill [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo focus [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo goto [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo hover [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo press [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo reload [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo selectOption [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo setInputFiles [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo type [fail] +library/slowmo.spec.ts › slowMo › Page SlowMo uncheck [fail] +library/snapshotter.spec.ts › snapshots › empty adopted style sheets should not prevent node refs [fail] +library/snapshotter.spec.ts › snapshots › should capture frame [pass] +library/snapshotter.spec.ts › snapshots › should capture iframe [pass] +library/snapshotter.spec.ts › snapshots › should capture iframe with srcdoc [pass] +library/snapshotter.spec.ts › snapshots › should capture resources [fail] +library/snapshotter.spec.ts › snapshots › should capture snapshot target [fail] +library/snapshotter.spec.ts › snapshots › should collect multiple [fail] +library/snapshotter.spec.ts › snapshots › should collect on attribute change [fail] +library/snapshotter.spec.ts › snapshots › should collect snapshot [fail] +library/snapshotter.spec.ts › snapshots › should have a custom doctype [fail] +library/snapshotter.spec.ts › snapshots › should not navigate on anchor clicks [fail] +library/snapshotter.spec.ts › snapshots › should preserve BASE and other content on reset [pass] +library/snapshotter.spec.ts › snapshots › should replace meta charset attr that specifies charset [fail] +library/snapshotter.spec.ts › snapshots › should replace meta content attr that specifies charset [fail] +library/snapshotter.spec.ts › snapshots › should respect CSSOM change through CSSGroupingRule [fail] +library/snapshotter.spec.ts › snapshots › should respect attr removal [fail] +library/snapshotter.spec.ts › snapshots › should respect inline CSSOM change [fail] +library/snapshotter.spec.ts › snapshots › should respect node removal [fail] +library/snapshotter.spec.ts › snapshots › should respect subresource CSSOM change [fail] +library/tap.spec.ts › locators › should send all of the correct events [fail] +library/tap.spec.ts › should not send mouse events touchstart is canceled [fail] +library/tap.spec.ts › should not send mouse events when touchend is canceled [fail] +library/tap.spec.ts › should not wait for a navigation caused by a tap [fail] +library/tap.spec.ts › should send all of the correct events @smoke [fail] +library/tap.spec.ts › should send well formed touch points [fail] +library/tap.spec.ts › should wait until an element is visible to tap it [fail] +library/tap.spec.ts › should work with modifiers [fail] +library/tap.spec.ts › trial run should not tap [fail] +library/trace-viewer.spec.ts › should allow hiding route actions [unknown] +library/trace-viewer.spec.ts › should allow showing screenshots instead of snapshots [unknown] +library/trace-viewer.spec.ts › should capture data-url svg iframe [unknown] +library/trace-viewer.spec.ts › should capture iframe with sandbox attribute [unknown] +library/trace-viewer.spec.ts › should complain about newer version of trace in old viewer [unknown] +library/trace-viewer.spec.ts › should contain action info [fail] +library/trace-viewer.spec.ts › should contain adopted style sheets [fail] +library/trace-viewer.spec.ts › should display language-specific locators [unknown] +library/trace-viewer.spec.ts › should display waitForLoadState even if did not wait for it [unknown] +library/trace-viewer.spec.ts › should filter network requests by resource type [fail] +library/trace-viewer.spec.ts › should filter network requests by url [unknown] +library/trace-viewer.spec.ts › should follow redirects [unknown] +library/trace-viewer.spec.ts › should handle case where neither snapshots nor screenshots exist [fail] +library/trace-viewer.spec.ts › should handle file URIs [fail] +library/trace-viewer.spec.ts › should handle multiple headers [unknown] +library/trace-viewer.spec.ts › should handle src=blob [unknown] +library/trace-viewer.spec.ts › should have correct snapshot size [fail] +library/trace-viewer.spec.ts › should have correct stack trace [unknown] +library/trace-viewer.spec.ts › should have network request overrides [fail] +library/trace-viewer.spec.ts › should have network request overrides 2 [unknown] +library/trace-viewer.spec.ts › should have network requests [unknown] +library/trace-viewer.spec.ts › should highlight expect failure [unknown] +library/trace-viewer.spec.ts › should highlight locator in iframe while typing [unknown] +library/trace-viewer.spec.ts › should highlight target element in shadow dom [unknown] +library/trace-viewer.spec.ts › should highlight target elements [fail] +library/trace-viewer.spec.ts › should ignore 304 responses [unknown] +library/trace-viewer.spec.ts › should include metainfo [unknown] +library/trace-viewer.spec.ts › should include requestUrl in route.abort [unknown] +library/trace-viewer.spec.ts › should include requestUrl in route.continue [unknown] +library/trace-viewer.spec.ts › should include requestUrl in route.fulfill [unknown] +library/trace-viewer.spec.ts › should not crash with broken locator [fail] +library/trace-viewer.spec.ts › should open console errors on click [fail] +library/trace-viewer.spec.ts › should open simple trace viewer [fail] +library/trace-viewer.spec.ts › should open snapshot in new browser context [unknown] +library/trace-viewer.spec.ts › should open trace viewer on specific host [unknown] +library/trace-viewer.spec.ts › should open trace-1.31 [unknown] +library/trace-viewer.spec.ts › should open trace-1.37 [fail] +library/trace-viewer.spec.ts › should open two trace files [fail] +library/trace-viewer.spec.ts › should open two trace files of the same test [unknown] +library/trace-viewer.spec.ts › should open two trace viewers [unknown] +library/trace-viewer.spec.ts › should pick locator [fail] +library/trace-viewer.spec.ts › should pick locator in iframe [fail] +library/trace-viewer.spec.ts › should popup snapshot [fail] +library/trace-viewer.spec.ts › should prefer later resource request with the same method [unknown] +library/trace-viewer.spec.ts › should preserve currentSrc [unknown] +library/trace-viewer.spec.ts › should preserve noscript when javascript is disabled [unknown] +library/trace-viewer.spec.ts › should properly synchronize local and remote time [unknown] +library/trace-viewer.spec.ts › should register custom elements [unknown] +library/trace-viewer.spec.ts › should remove noscript by default [fail] +library/trace-viewer.spec.ts › should remove noscript when javaScriptEnabled is set to true [unknown] +library/trace-viewer.spec.ts › should render console [unknown] +library/trace-viewer.spec.ts › should render network bars [unknown] +library/trace-viewer.spec.ts › should restore control values [unknown] +library/trace-viewer.spec.ts › should restore scroll positions [unknown] +library/trace-viewer.spec.ts › should serve css without content-type [unknown] +library/trace-viewer.spec.ts › should serve overridden request [fail] +library/trace-viewer.spec.ts › should show action source [fail] +library/trace-viewer.spec.ts › should show baseURL in metadata pane [fail] +library/trace-viewer.spec.ts › should show correct request start time [unknown] +library/trace-viewer.spec.ts › should show empty trace viewer [fail] +library/trace-viewer.spec.ts › should show font preview [unknown] +library/trace-viewer.spec.ts › should show null as a param [unknown] +library/trace-viewer.spec.ts › should show only one pointer with multilevel iframes [unknown] +library/trace-viewer.spec.ts › should show params and return value [unknown] +library/trace-viewer.spec.ts › should show similar actions from library-only trace [fail] +library/trace-viewer.spec.ts › should show snapshot URL [unknown] +library/trace-viewer.spec.ts › should update highlight when typing [unknown] +library/trace-viewer.spec.ts › should work with adopted style sheets and all: unset [unknown] +library/trace-viewer.spec.ts › should work with adopted style sheets and replace/replaceSync [unknown] +library/trace-viewer.spec.ts › should work with meta CSP [fail] +library/trace-viewer.spec.ts › should work with nesting CSS selectors [fail] +library/tracing.spec.ts › should collect sources [fail] +library/tracing.spec.ts › should collect trace with resources, but no js [fail] +library/tracing.spec.ts › should collect two traces [fail] +library/tracing.spec.ts › should exclude internal pages [pass] +library/tracing.spec.ts › should export trace concurrently to second navigation [pass] +library/tracing.spec.ts › should flush console events on tracing stop [pass] +library/tracing.spec.ts › should hide internal stack frames [fail] +library/tracing.spec.ts › should hide internal stack frames in expect [fail] +library/tracing.spec.ts › should ignore iframes in head [pass] +library/tracing.spec.ts › should include context API requests [pass] +library/tracing.spec.ts › should include interrupted actions [fail] +library/tracing.spec.ts › should not collect snapshots by default [fail] +library/tracing.spec.ts › should not crash when browser closes mid-trace [pass] +library/tracing.spec.ts › should not emit after w/o before [pass] +library/tracing.spec.ts › should not flush console events [pass] +library/tracing.spec.ts › should not hang for clicks that open dialogs [fail] +library/tracing.spec.ts › should not include buffers in the trace [pass] +library/tracing.spec.ts › should not include trace resources from the previous chunks [fail] +library/tracing.spec.ts › should not stall on dialogs [fail] +library/tracing.spec.ts › should not throw when stopping without start but not exporting [pass] +library/tracing.spec.ts › should overwrite existing file [fail] +library/tracing.spec.ts › should produce screencast frames crop [fail] +library/tracing.spec.ts › should produce screencast frames fit [fail] +library/tracing.spec.ts › should produce screencast frames scale [fail] +library/tracing.spec.ts › should record global request trace [pass] +library/tracing.spec.ts › should record network failures [pass] +library/tracing.spec.ts › should respect tracesDir and name [fail] +library/tracing.spec.ts › should store global request traces separately [pass] +library/tracing.spec.ts › should store postData for global request [pass] +library/tracing.spec.ts › should survive browser.close with auto-created traces dir [pass] +library/tracing.spec.ts › should throw when starting with different options [pass] +library/tracing.spec.ts › should throw when stopping without start [pass] +library/tracing.spec.ts › should use the correct apiName for event driven callbacks [pass] +library/tracing.spec.ts › should work with multiple chunks [fail] +library/unroute-behavior.spec.ts › context.close should not wait for active route handlers on the owned pages [pass] +library/unroute-behavior.spec.ts › context.unroute should not wait for pending handlers to complete [timeout] +library/unroute-behavior.spec.ts › context.unrouteAll removes all handlers [pass] +library/unroute-behavior.spec.ts › context.unrouteAll should not wait for pending handlers to complete if behavior is ignoreErrors [timeout] +library/unroute-behavior.spec.ts › context.unrouteAll should wait for pending handlers to complete [timeout] +library/unroute-behavior.spec.ts › page.close does not wait for active route handlers [pass] +library/unroute-behavior.spec.ts › page.close should not wait for active route handlers on the owning context [pass] +library/unroute-behavior.spec.ts › page.unroute should not wait for pending handlers to complete [pass] +library/unroute-behavior.spec.ts › page.unrouteAll removes all routes [pass] +library/unroute-behavior.spec.ts › page.unrouteAll should not wait for pending handlers to complete if behavior is ignoreErrors [pass] +library/unroute-behavior.spec.ts › page.unrouteAll should wait for pending handlers to complete [pass] +library/unroute-behavior.spec.ts › route.continue should not throw if page has been closed [pass] +library/unroute-behavior.spec.ts › route.fallback should not throw if page has been closed [pass] +library/unroute-behavior.spec.ts › route.fulfill should not throw if page has been closed [pass] +library/video.spec.ts › screencast › saveAs should throw when no video frames [timeout] +library/video.spec.ts › screencast › should be 800x450 by default [timeout] +library/video.spec.ts › screencast › should be 800x600 with null viewport [timeout] +library/video.spec.ts › screencast › should capture css transformation [timeout] +library/video.spec.ts › screencast › should capture full viewport [fail] +library/video.spec.ts › screencast › should capture full viewport on hidpi [fail] +library/video.spec.ts › screencast › should capture navigation [timeout] +library/video.spec.ts › screencast › should capture static page [timeout] +library/video.spec.ts › screencast › should capture static page in persistent context @smoke [fail] +library/video.spec.ts › screencast › should continue recording main page after popup closes [fail] +library/video.spec.ts › screencast › should delete video [timeout] +library/video.spec.ts › screencast › should emulate an iphone [timeout] +library/video.spec.ts › screencast › should expose video path [fail] +library/video.spec.ts › screencast › should expose video path blank page [timeout] +library/video.spec.ts › screencast › should expose video path blank popup [fail] +library/video.spec.ts › screencast › should not create video for internal pages [unknown] +library/video.spec.ts › screencast › should scale frames down to the requested size [timeout] +library/video.spec.ts › screencast › should throw if browser dies [fail] +library/video.spec.ts › screencast › should throw on browser close [fail] +library/video.spec.ts › screencast › should throw without recordVideo.dir [pass] +library/video.spec.ts › screencast › should use viewport scaled down to fit into 800x800 as default size [timeout] +library/video.spec.ts › screencast › should wait for video to finish if page was closed [fail] +library/video.spec.ts › screencast › should work for popups [fail] +library/video.spec.ts › screencast › should work with old options [timeout] +library/video.spec.ts › screencast › should work with relative path for recordVideo.dir [timeout] +library/video.spec.ts › screencast › should work with video+trace [timeout] +library/video.spec.ts › screencast › should work with weird screen resolution [timeout] +library/video.spec.ts › screencast › videoSize should require videosPath [pass] +library/video.spec.ts › should saveAs video [timeout] +library/web-socket.spec.ts › should emit binary frame events [timeout] +library/web-socket.spec.ts › should emit close events [timeout] +library/web-socket.spec.ts › should emit error [timeout] +library/web-socket.spec.ts › should emit frame events [timeout] +library/web-socket.spec.ts › should filter out the close events when the server closes with a message [timeout] +library/web-socket.spec.ts › should not have stray error events [timeout] +library/web-socket.spec.ts › should pass self as argument to close event [timeout] +library/web-socket.spec.ts › should reject waitForEvent on page close [timeout] +library/web-socket.spec.ts › should reject waitForEvent on socket close [timeout] +library/web-socket.spec.ts › should turn off when offline [unknown] +library/web-socket.spec.ts › should work @smoke [pass] \ No newline at end of file diff --git a/tests/bidi/expectations/bidi-firefox-nightly-page.txt b/tests/bidi/expectations/bidi-firefox-nightly-page.txt new file mode 100644 index 0000000000..366d8fdf36 --- /dev/null +++ b/tests/bidi/expectations/bidi-firefox-nightly-page.txt @@ -0,0 +1,1968 @@ +page/elementhandle-bounding-box.spec.ts › should force a layout [fail] +page/elementhandle-bounding-box.spec.ts › should get frame box [fail] +page/elementhandle-bounding-box.spec.ts › should handle nested frames [fail] +page/elementhandle-bounding-box.spec.ts › should handle scroll offset and click [fail] +page/elementhandle-bounding-box.spec.ts › should return null for invisible elements [fail] +page/elementhandle-bounding-box.spec.ts › should work [fail] +page/elementhandle-bounding-box.spec.ts › should work when inline box child is outside of viewport [fail] +page/elementhandle-bounding-box.spec.ts › should work with SVG nodes [fail] +page/elementhandle-click.spec.ts › should double click the button [fail] +page/elementhandle-click.spec.ts › should throw for <br> elements with force [fail] +page/elementhandle-click.spec.ts › should throw for detached nodes [pass] +page/elementhandle-click.spec.ts › should throw for hidden nodes with force [pass] +page/elementhandle-click.spec.ts › should throw for recursively hidden nodes with force [pass] +page/elementhandle-click.spec.ts › should work @smoke [pass] +page/elementhandle-click.spec.ts › should work for Shadow DOM v1 [pass] +page/elementhandle-click.spec.ts › should work for TextNodes [fail] +page/elementhandle-click.spec.ts › should work with Node removed [pass] +page/elementhandle-content-frame.spec.ts › should return null for document.documentElement [pass] +page/elementhandle-content-frame.spec.ts › should return null for non-iframes [pass] +page/elementhandle-content-frame.spec.ts › should work [pass] +page/elementhandle-content-frame.spec.ts › should work for cross-frame evaluations [fail] +page/elementhandle-content-frame.spec.ts › should work for cross-process iframes [pass] +page/elementhandle-convenience.spec.ts › getAttribute should work [pass] +page/elementhandle-convenience.spec.ts › innerHTML should work [pass] +page/elementhandle-convenience.spec.ts › innerText should throw [fail] +page/elementhandle-convenience.spec.ts › innerText should work [pass] +page/elementhandle-convenience.spec.ts › inputValue should work [pass] +page/elementhandle-convenience.spec.ts › isChecked should work [fail] +page/elementhandle-convenience.spec.ts › isEditable should work [fail] +page/elementhandle-convenience.spec.ts › isEnabled and isDisabled should work [fail] +page/elementhandle-convenience.spec.ts › isEnabled and isDisabled should work with <select/> option/optgroup correctly [fail] +page/elementhandle-convenience.spec.ts › isVisible and isHidden should work [fail] +page/elementhandle-convenience.spec.ts › isVisible should not throw when the DOM element is not connected [fail] +page/elementhandle-convenience.spec.ts › should have a nice preview [pass] +page/elementhandle-convenience.spec.ts › should have a nice preview for non-ascii attributes/children [fail] +page/elementhandle-convenience.spec.ts › textContent should work [pass] +page/elementhandle-convenience.spec.ts › textContent should work on ShadowRoot [fail] +page/elementhandle-eval-on-selector.spec.ts › should not throw in case of missing selector for all [fail] +page/elementhandle-eval-on-selector.spec.ts › should retrieve content from subtree [fail] +page/elementhandle-eval-on-selector.spec.ts › should retrieve content from subtree for all [fail] +page/elementhandle-eval-on-selector.spec.ts › should throw in case of missing selector [fail] +page/elementhandle-eval-on-selector.spec.ts › should work [fail] +page/elementhandle-eval-on-selector.spec.ts › should work for all [fail] +page/elementhandle-misc.spec.ts › should allow disposing twice [fail] +page/elementhandle-misc.spec.ts › should check the box [fail] +page/elementhandle-misc.spec.ts › should check the box using setChecked [fail] +page/elementhandle-misc.spec.ts › should fill input [pass] +page/elementhandle-misc.spec.ts › should fill input when Node is removed [pass] +page/elementhandle-misc.spec.ts › should focus a button [pass] +page/elementhandle-misc.spec.ts › should hover [pass] +page/elementhandle-misc.spec.ts › should hover when Node is removed [pass] +page/elementhandle-misc.spec.ts › should select single option [pass] +page/elementhandle-misc.spec.ts › should uncheck the box [fail] +page/elementhandle-owner-frame.spec.ts › should work [fail] +page/elementhandle-owner-frame.spec.ts › should work for adopted elements [fail] +page/elementhandle-owner-frame.spec.ts › should work for cross-frame evaluations [fail] +page/elementhandle-owner-frame.spec.ts › should work for cross-process iframes [fail] +page/elementhandle-owner-frame.spec.ts › should work for detached elements [fail] +page/elementhandle-owner-frame.spec.ts › should work for document [fail] +page/elementhandle-owner-frame.spec.ts › should work for iframe elements [fail] +page/elementhandle-press.spec.ts › should not modify selection when focused [fail] +page/elementhandle-press.spec.ts › should not select existing value [fail] +page/elementhandle-press.spec.ts › should reset selection when not focused [fail] +page/elementhandle-press.spec.ts › should work [fail] +page/elementhandle-press.spec.ts › should work with number input [fail] +page/elementhandle-query-selector.spec.ts › should query existing element [fail] +page/elementhandle-query-selector.spec.ts › should query existing elements [fail] +page/elementhandle-query-selector.spec.ts › should return empty array for non-existing elements [fail] +page/elementhandle-query-selector.spec.ts › should return null for non-existing element [fail] +page/elementhandle-query-selector.spec.ts › should work for adopted elements [timeout] +page/elementhandle-query-selector.spec.ts › xpath should query existing element [fail] +page/elementhandle-query-selector.spec.ts › xpath should return null for non-existing element [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › path option should create subdirectories [pass] +page/elementhandle-screenshot.spec.ts › element screenshot › should capture full element when larger than viewport [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should capture full element when larger than viewport in parallel [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should fail to screenshot a detached element [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should not issue resize event [pass] +page/elementhandle-screenshot.spec.ts › element screenshot › should prefer type over extension [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should scroll 15000px into view [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should scroll element into view [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should take into account padding and border [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should take screenshot of disabled button [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should timeout waiting for visible [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should wait for element to stop moving [pass] +page/elementhandle-screenshot.spec.ts › element screenshot › should wait for visible [pass] +page/elementhandle-screenshot.spec.ts › element screenshot › should work [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should work for an element with an offset [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should work for an element with fractional dimensions [fail] +page/elementhandle-screenshot.spec.ts › element screenshot › should work when main world busts JSON.stringify [pass] +page/elementhandle-screenshot.spec.ts › element screenshot › should work with a rotated element [fail] +page/elementhandle-scroll-into-view.spec.ts › should scroll display:contents into view [fail] +page/elementhandle-scroll-into-view.spec.ts › should throw for detached element [fail] +page/elementhandle-scroll-into-view.spec.ts › should timeout waiting for visible [fail] +page/elementhandle-scroll-into-view.spec.ts › should wait for display:none to become visible [fail] +page/elementhandle-scroll-into-view.spec.ts › should wait for element to stop moving [fail] +page/elementhandle-scroll-into-view.spec.ts › should wait for nested display:none to become visible [fail] +page/elementhandle-scroll-into-view.spec.ts › should work @smoke [pass] +page/elementhandle-scroll-into-view.spec.ts › should work for visibility:hidden element [fail] +page/elementhandle-scroll-into-view.spec.ts › should work for zero-sized element [fail] +page/elementhandle-select-text.spec.ts › should select input [fail] +page/elementhandle-select-text.spec.ts › should select plain div [pass] +page/elementhandle-select-text.spec.ts › should select textarea [fail] +page/elementhandle-select-text.spec.ts › should timeout waiting for invisible element [pass] +page/elementhandle-select-text.spec.ts › should wait for visible [pass] +page/elementhandle-type.spec.ts › should not modify selection when focused [fail] +page/elementhandle-type.spec.ts › should not select existing value [fail] +page/elementhandle-type.spec.ts › should reset selection when not focused [fail] +page/elementhandle-type.spec.ts › should work [fail] +page/elementhandle-type.spec.ts › should work with number input [fail] +page/elementhandle-wait-for-element-state.spec.ts › should throw waiting for enabled when detached [fail] +page/elementhandle-wait-for-element-state.spec.ts › should throw waiting for visible when detached [fail] +page/elementhandle-wait-for-element-state.spec.ts › should timeout waiting for visible [fail] +page/elementhandle-wait-for-element-state.spec.ts › should wait for already hidden [fail] +page/elementhandle-wait-for-element-state.spec.ts › should wait for already visible [fail] +page/elementhandle-wait-for-element-state.spec.ts › should wait for aria enabled button [fail] +page/elementhandle-wait-for-element-state.spec.ts › should wait for button with an aria-disabled parent [fail] +page/elementhandle-wait-for-element-state.spec.ts › should wait for editable input [fail] +page/elementhandle-wait-for-element-state.spec.ts › should wait for hidden [fail] +page/elementhandle-wait-for-element-state.spec.ts › should wait for hidden when detached [fail] +page/elementhandle-wait-for-element-state.spec.ts › should wait for stable position [pass] +page/elementhandle-wait-for-element-state.spec.ts › should wait for visible [fail] +page/eval-on-selector-all.spec.ts › should auto-detect css selector [fail] +page/eval-on-selector-all.spec.ts › should return complex values [fail] +page/eval-on-selector-all.spec.ts › should support * capture [fail] +page/eval-on-selector-all.spec.ts › should support * capture when multiple paths match [fail] +page/eval-on-selector-all.spec.ts › should support >> syntax [fail] +page/eval-on-selector-all.spec.ts › should work with bogus Array.from [fail] +page/eval-on-selector-all.spec.ts › should work with css selector [fail] +page/eval-on-selector-all.spec.ts › should work with text selector [fail] +page/eval-on-selector-all.spec.ts › should work with xpath selector [fail] +page/eval-on-selector.spec.ts › should accept ElementHandles as arguments [fail] +page/eval-on-selector.spec.ts › should accept arguments [fail] +page/eval-on-selector.spec.ts › should auto-detect css selector [fail] +page/eval-on-selector.spec.ts › should auto-detect css selector with attributes [fail] +page/eval-on-selector.spec.ts › should auto-detect nested selectors [fail] +page/eval-on-selector.spec.ts › should not stop at first failure with >> syntax [fail] +page/eval-on-selector.spec.ts › should return complex values [fail] +page/eval-on-selector.spec.ts › should support * capture [fail] +page/eval-on-selector.spec.ts › should support >> syntax [fail] +page/eval-on-selector.spec.ts › should support >> syntax with different engines [fail] +page/eval-on-selector.spec.ts › should support spaces with >> syntax [pass] +page/eval-on-selector.spec.ts › should throw error if no element is found [pass] +page/eval-on-selector.spec.ts › should throw on malformed * capture [pass] +page/eval-on-selector.spec.ts › should throw on multiple * captures [pass] +page/eval-on-selector.spec.ts › should work with css selector [fail] +page/eval-on-selector.spec.ts › should work with data-test selector [fail] +page/eval-on-selector.spec.ts › should work with data-test-id selector [fail] +page/eval-on-selector.spec.ts › should work with data-testid selector [fail] +page/eval-on-selector.spec.ts › should work with id selector [fail] +page/eval-on-selector.spec.ts › should work with quotes in css attributes [fail] +page/eval-on-selector.spec.ts › should work with quotes in css attributes when missing [fail] +page/eval-on-selector.spec.ts › should work with spaces in css attributes [fail] +page/eval-on-selector.spec.ts › should work with spaces in css attributes when missing [fail] +page/eval-on-selector.spec.ts › should work with text selector [fail] +page/eval-on-selector.spec.ts › should work with text selector in quotes [fail] +page/eval-on-selector.spec.ts › should work with xpath selector [fail] +page/expect-boolean.spec.ts › not.toBeDisabled div [fail] +page/expect-boolean.spec.ts › not.toBeEmpty [fail] +page/expect-boolean.spec.ts › not.toBeOK [pass] +page/expect-boolean.spec.ts › should print selector syntax error [pass] +page/expect-boolean.spec.ts › should print unknown engine error [pass] +page/expect-boolean.spec.ts › toBeAttached › default [fail] +page/expect-boolean.spec.ts › toBeAttached › eventually [fail] +page/expect-boolean.spec.ts › toBeAttached › eventually with not [fail] +page/expect-boolean.spec.ts › toBeAttached › fail [fail] +page/expect-boolean.spec.ts › toBeAttached › fail with not [fail] +page/expect-boolean.spec.ts › toBeAttached › over navigation [pass] +page/expect-boolean.spec.ts › toBeAttached › with attached:false [fail] +page/expect-boolean.spec.ts › toBeAttached › with attached:true [fail] +page/expect-boolean.spec.ts › toBeAttached › with frameLocator [fail] +page/expect-boolean.spec.ts › toBeAttached › with hidden element [fail] +page/expect-boolean.spec.ts › toBeAttached › with impossible timeout [fail] +page/expect-boolean.spec.ts › toBeAttached › with impossible timeout .not [fail] +page/expect-boolean.spec.ts › toBeAttached › with not [fail] +page/expect-boolean.spec.ts › toBeAttached › with not and attached:false [fail] +page/expect-boolean.spec.ts › toBeChecked with value [fail] +page/expect-boolean.spec.ts › toBeChecked › default [fail] +page/expect-boolean.spec.ts › toBeChecked › fail [fail] +page/expect-boolean.spec.ts › toBeChecked › fail missing [fail] +page/expect-boolean.spec.ts › toBeChecked › fail with checked:false [fail] +page/expect-boolean.spec.ts › toBeChecked › fail with not [fail] +page/expect-boolean.spec.ts › toBeChecked › friendly log [fail] +page/expect-boolean.spec.ts › toBeChecked › with checked:false [fail] +page/expect-boolean.spec.ts › toBeChecked › with checked:true [fail] +page/expect-boolean.spec.ts › toBeChecked › with impossible timeout [fail] +page/expect-boolean.spec.ts › toBeChecked › with impossible timeout .not [fail] +page/expect-boolean.spec.ts › toBeChecked › with not [fail] +page/expect-boolean.spec.ts › toBeChecked › with not and checked:false [fail] +page/expect-boolean.spec.ts › toBeChecked › with role [fail] +page/expect-boolean.spec.ts › toBeDisabled with value [fail] +page/expect-boolean.spec.ts › toBeEditable › default [fail] +page/expect-boolean.spec.ts › toBeEditable › with editable:false [fail] +page/expect-boolean.spec.ts › toBeEditable › with editable:true [fail] +page/expect-boolean.spec.ts › toBeEditable › with not [fail] +page/expect-boolean.spec.ts › toBeEditable › with not and editable:false [fail] +page/expect-boolean.spec.ts › toBeEmpty div [fail] +page/expect-boolean.spec.ts › toBeEmpty input [fail] +page/expect-boolean.spec.ts › toBeEnabled › default [fail] +page/expect-boolean.spec.ts › toBeEnabled › eventually [fail] +page/expect-boolean.spec.ts › toBeEnabled › eventually with not [fail] +page/expect-boolean.spec.ts › toBeEnabled › failed [fail] +page/expect-boolean.spec.ts › toBeEnabled › toBeDisabled [fail] +page/expect-boolean.spec.ts › toBeEnabled › with enabled:false [fail] +page/expect-boolean.spec.ts › toBeEnabled › with enabled:true [fail] +page/expect-boolean.spec.ts › toBeEnabled › with not and enabled:false [fail] +page/expect-boolean.spec.ts › toBeFocused [fail] +page/expect-boolean.spec.ts › toBeFocused with shadow elements [fail] +page/expect-boolean.spec.ts › toBeHidden with value [fail] +page/expect-boolean.spec.ts › toBeHidden › default [fail] +page/expect-boolean.spec.ts › toBeHidden › eventually [fail] +page/expect-boolean.spec.ts › toBeHidden › eventually with not [fail] +page/expect-boolean.spec.ts › toBeHidden › fail [fail] +page/expect-boolean.spec.ts › toBeHidden › fail with not [fail] +page/expect-boolean.spec.ts › toBeHidden › fail with not when nothing matching [fail] +page/expect-boolean.spec.ts › toBeHidden › when nothing matches [fail] +page/expect-boolean.spec.ts › toBeHidden › with impossible timeout [fail] +page/expect-boolean.spec.ts › toBeHidden › with impossible timeout .not [fail] +page/expect-boolean.spec.ts › toBeHidden › with not [fail] +page/expect-boolean.spec.ts › toBeOK [pass] +page/expect-boolean.spec.ts › toBeOK fail with invalid argument [pass] +page/expect-boolean.spec.ts › toBeOK fail with promise [pass] +page/expect-boolean.spec.ts › toBeOK should print response with text content type when fails › image content type [pass] +page/expect-boolean.spec.ts › toBeOK should print response with text content type when fails › no content type [pass] +page/expect-boolean.spec.ts › toBeOK should print response with text content type when fails › text content type [pass] +page/expect-boolean.spec.ts › toBeVisible › default [fail] +page/expect-boolean.spec.ts › toBeVisible › eventually [fail] +page/expect-boolean.spec.ts › toBeVisible › eventually with not [fail] +page/expect-boolean.spec.ts › toBeVisible › fail [fail] +page/expect-boolean.spec.ts › toBeVisible › fail with not [fail] +page/expect-boolean.spec.ts › toBeVisible › over navigation [pass] +page/expect-boolean.spec.ts › toBeVisible › with frameLocator [fail] +page/expect-boolean.spec.ts › toBeVisible › with frameLocator 2 [fail] +page/expect-boolean.spec.ts › toBeVisible › with impossible timeout [fail] +page/expect-boolean.spec.ts › toBeVisible › with impossible timeout .not [fail] +page/expect-boolean.spec.ts › toBeVisible › with not [fail] +page/expect-boolean.spec.ts › toBeVisible › with not and visible:false [fail] +page/expect-boolean.spec.ts › toBeVisible › with visible:false [fail] +page/expect-boolean.spec.ts › toBeVisible › with visible:true [fail] +page/expect-matcher-result.spec.ts › toBeChecked({ checked: false }) should have expected: false [fail] +page/expect-matcher-result.spec.ts › toBeTruthy-based assertions should have matcher result [fail] +page/expect-matcher-result.spec.ts › toEqual-based assertions should have matcher result [fail] +page/expect-matcher-result.spec.ts › toHaveScreenshot should populate matcherResult [fail] +page/expect-matcher-result.spec.ts › toMatchText-based assertions should have matcher result [fail] +page/expect-misc.spec.ts › toBeInViewport › should have good stack [pass] +page/expect-misc.spec.ts › toBeInViewport › should report intersection even if fully covered by other element [fail] +page/expect-misc.spec.ts › toBeInViewport › should respect ratio option [fail] +page/expect-misc.spec.ts › toBeInViewport › should work [fail] +page/expect-misc.spec.ts › toHaveAccessibleDescription [fail] +page/expect-misc.spec.ts › toHaveAccessibleName [fail] +page/expect-misc.spec.ts › toHaveAttribute › pass [fail] +page/expect-misc.spec.ts › toHaveAttribute › should match attribute without value [fail] +page/expect-misc.spec.ts › toHaveAttribute › should match boolean attribute [fail] +page/expect-misc.spec.ts › toHaveAttribute › should not match missing attribute [fail] +page/expect-misc.spec.ts › toHaveAttribute › should support boolean attribute with options [fail] +page/expect-misc.spec.ts › toHaveAttribute › support ignoreCase [fail] +page/expect-misc.spec.ts › toHaveCSS › custom css properties [fail] +page/expect-misc.spec.ts › toHaveCSS › pass [fail] +page/expect-misc.spec.ts › toHaveClass › fail [fail] +page/expect-misc.spec.ts › toHaveClass › fail with array [fail] +page/expect-misc.spec.ts › toHaveClass › pass [fail] +page/expect-misc.spec.ts › toHaveClass › pass with SVGs [fail] +page/expect-misc.spec.ts › toHaveClass › pass with array [fail] +page/expect-misc.spec.ts › toHaveCount should not produce logs twice [fail] +page/expect-misc.spec.ts › toHaveCount › eventually pass non-zero [fail] +page/expect-misc.spec.ts › toHaveCount › eventually pass not non-zero [fail] +page/expect-misc.spec.ts › toHaveCount › eventually pass zero [fail] +page/expect-misc.spec.ts › toHaveCount › fail zero [fail] +page/expect-misc.spec.ts › toHaveCount › fail zero 2 [fail] +page/expect-misc.spec.ts › toHaveCount › pass zero [fail] +page/expect-misc.spec.ts › toHaveCount › toHaveCount pass [fail] +page/expect-misc.spec.ts › toHaveId › pass [fail] +page/expect-misc.spec.ts › toHaveJSProperty › fail [fail] +page/expect-misc.spec.ts › toHaveJSProperty › fail boolean [fail] +page/expect-misc.spec.ts › toHaveJSProperty › fail boolean 2 [fail] +page/expect-misc.spec.ts › toHaveJSProperty › fail nested [fail] +page/expect-misc.spec.ts › toHaveJSProperty › fail number [fail] +page/expect-misc.spec.ts › toHaveJSProperty › fail string [fail] +page/expect-misc.spec.ts › toHaveJSProperty › pass [fail] +page/expect-misc.spec.ts › toHaveJSProperty › pass boolean [fail] +page/expect-misc.spec.ts › toHaveJSProperty › pass boolean 2 [fail] +page/expect-misc.spec.ts › toHaveJSProperty › pass nested [fail] +page/expect-misc.spec.ts › toHaveJSProperty › pass null [fail] +page/expect-misc.spec.ts › toHaveJSProperty › pass number [fail] +page/expect-misc.spec.ts › toHaveJSProperty › pass string [fail] +page/expect-misc.spec.ts › toHaveJSProperty › pass undefined [fail] +page/expect-misc.spec.ts › toHaveRole [fail] +page/expect-misc.spec.ts › toHaveText should not produce logs twice [fail] +page/expect-misc.spec.ts › toHaveText that does not match should not produce logs twice [fail] +page/expect-misc.spec.ts › toHaveTitle › fail [fail] +page/expect-misc.spec.ts › toHaveTitle › pass [fail] +page/expect-misc.spec.ts › toHaveURL › fail [pass] +page/expect-misc.spec.ts › toHaveURL › pass [pass] +page/expect-misc.spec.ts › toHaveURL › support ignoreCase [pass] +page/expect-timeout.spec.ts › should have timeout error name [pass] +page/expect-timeout.spec.ts › should not print timed out error message when page closes [fail] +page/expect-timeout.spec.ts › should not throw when navigating during first locator handler check [fail] +page/expect-timeout.spec.ts › should not throw when navigating during one-shot check [fail] +page/expect-timeout.spec.ts › should print timed out error message [fail] +page/expect-timeout.spec.ts › should print timed out error message when value does not match [fail] +page/expect-timeout.spec.ts › should print timed out error message when value does not match with impossible timeout [fail] +page/expect-timeout.spec.ts › should print timed out error message with impossible timeout [fail] +page/expect-timeout.spec.ts › should timeout during first locator handler check [fail] +page/expect-to-have-text.spec.ts › not.toHaveText › fail [fail] +page/expect-to-have-text.spec.ts › not.toHaveText › pass [fail] +page/expect-to-have-text.spec.ts › not.toHaveText › should work when selector does not match [fail] +page/expect-to-have-text.spec.ts › toContainText with array › fail [fail] +page/expect-to-have-text.spec.ts › toContainText with array › pass [fail] +page/expect-to-have-text.spec.ts › toContainText with regex › fail [fail] +page/expect-to-have-text.spec.ts › toContainText with regex › pass [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › fail [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › fail on not+empty [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › fail on repeating array matchers [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › pass [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › pass empty [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › pass eventually empty [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › pass lazy [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › pass not empty [fail] +page/expect-to-have-text.spec.ts › toHaveText with array › pass on empty [fail] +page/expect-to-have-text.spec.ts › toHaveText with regex › fail [fail] +page/expect-to-have-text.spec.ts › toHaveText with regex › pass [fail] +page/expect-to-have-text.spec.ts › toHaveText with text › fail [fail] +page/expect-to-have-text.spec.ts › toHaveText with text › fail with impossible timeout [fail] +page/expect-to-have-text.spec.ts › toHaveText with text › in shadow dom [fail] +page/expect-to-have-text.spec.ts › toHaveText with text › pass [fail] +page/expect-to-have-text.spec.ts › toHaveText with text › pass contain [fail] +page/expect-to-have-text.spec.ts › toHaveText with text › pass eventually [fail] +page/expect-to-have-text.spec.ts › toHaveText with text › with userInnerText [fail] +page/expect-to-have-value.spec.ts › should support failure [fail] +page/expect-to-have-value.spec.ts › should work [fail] +page/expect-to-have-value.spec.ts › should work with label [fail] +page/expect-to-have-value.spec.ts › should work with regex [fail] +page/expect-to-have-value.spec.ts › toHaveValues with multi-select › exact match with text failure [fail] +page/expect-to-have-value.spec.ts › toHaveValues with multi-select › fails when items not selected [fail] +page/expect-to-have-value.spec.ts › toHaveValues with multi-select › fails when multiple not specified [fail] +page/expect-to-have-value.spec.ts › toHaveValues with multi-select › fails when not a select element [fail] +page/expect-to-have-value.spec.ts › toHaveValues with multi-select › follows labels [fail] +page/expect-to-have-value.spec.ts › toHaveValues with multi-select › works with regex [fail] +page/expect-to-have-value.spec.ts › toHaveValues with multi-select › works with text [fail] +page/frame-evaluate.spec.ts › evaluateHandle should work [pass] +page/frame-evaluate.spec.ts › should allow cross-frame element handles [fail] +page/frame-evaluate.spec.ts › should be isolated between frames [pass] +page/frame-evaluate.spec.ts › should dispose context on cross-origin navigation [fail] +page/frame-evaluate.spec.ts › should dispose context on navigation [fail] +page/frame-evaluate.spec.ts › should execute after cross-site navigation [pass] +page/frame-evaluate.spec.ts › should have correct execution contexts @smoke [pass] +page/frame-evaluate.spec.ts › should have different execution contexts [pass] +page/frame-evaluate.spec.ts › should not allow cross-frame element handles when frames do not script each other [fail] +page/frame-evaluate.spec.ts › should not allow cross-frame js handles [pass] +page/frame-evaluate.spec.ts › should throw for detached frames [pass] +page/frame-evaluate.spec.ts › should work in iframes that failed initial navigation [fail] +page/frame-evaluate.spec.ts › should work in iframes that interrupted initial javascript url navigation [pass] +page/frame-frame-element.spec.ts › should throw when detached [pass] +page/frame-frame-element.spec.ts › should work @smoke [pass] +page/frame-frame-element.spec.ts › should work inside closed shadow root [fail] +page/frame-frame-element.spec.ts › should work inside declarative shadow root [fail] +page/frame-frame-element.spec.ts › should work with contentFrame [pass] +page/frame-frame-element.spec.ts › should work with frameset [pass] +page/frame-goto.spec.ts › should continue after client redirect [pass] +page/frame-goto.spec.ts › should navigate subframes @smoke [pass] +page/frame-goto.spec.ts › should reject when frame detaches [pass] +page/frame-goto.spec.ts › should return matching responses [fail] +page/frame-hierarchy.spec.ts › should detach child frames on navigation [pass] +page/frame-hierarchy.spec.ts › should handle nested frames @smoke [fail] +page/frame-hierarchy.spec.ts › should not send attach/detach events for main frame [pass] +page/frame-hierarchy.spec.ts › should persist mainFrame on cross-process navigation [pass] +page/frame-hierarchy.spec.ts › should refuse to display x-frame-options:deny iframe [fail] +page/frame-hierarchy.spec.ts › should report different frame instance when frame re-attaches [pass] +page/frame-hierarchy.spec.ts › should report frame from-inside shadow DOM [pass] +page/frame-hierarchy.spec.ts › should report frame.name() [fail] +page/frame-hierarchy.spec.ts › should report frame.parent() [pass] +page/frame-hierarchy.spec.ts › should return frame.page() [pass] +page/frame-hierarchy.spec.ts › should send "framenavigated" when navigating on anchor URLs [timeout] +page/frame-hierarchy.spec.ts › should send events when frames are manipulated dynamically [pass] +page/frame-hierarchy.spec.ts › should support framesets [pass] +page/interception.spec.ts › should disable memory cache when intercepting [timeout] +page/interception.spec.ts › should intercept after a service worker [pass] +page/interception.spec.ts › should intercept blob url requests [fail] +page/interception.spec.ts › should intercept network activity from worker [pass] +page/interception.spec.ts › should intercept network activity from worker 2 [pass] +page/interception.spec.ts › should intercept worker requests when enabled after worker creation [timeout] +page/interception.spec.ts › should not break remote worker importScripts [pass] +page/interception.spec.ts › should work with glob [pass] +page/interception.spec.ts › should work with navigation @smoke [pass] +page/interception.spec.ts › should work with regular expression passed from a different context [fail] +page/jshandle-as-element.spec.ts › should return ElementHandle for TextNodes [fail] +page/jshandle-as-element.spec.ts › should return null for non-elements [pass] +page/jshandle-as-element.spec.ts › should work @smoke [pass] +page/jshandle-as-element.spec.ts › should work with nullified Node [fail] +page/jshandle-evaluate.spec.ts › should work with expression [fail] +page/jshandle-evaluate.spec.ts › should work with function @smoke [pass] +page/jshandle-json-value.spec.ts › should handle circular objects [pass] +page/jshandle-json-value.spec.ts › should work @smoke [pass] +page/jshandle-json-value.spec.ts › should work with dates [fail] +page/jshandle-properties.spec.ts › getProperties should return empty map for non-objects [pass] +page/jshandle-properties.spec.ts › getProperties should return even non-own properties [fail] +page/jshandle-properties.spec.ts › getProperties should work [fail] +page/jshandle-properties.spec.ts › getProperties should work with elements [fail] +page/jshandle-properties.spec.ts › should work @smoke [fail] +page/jshandle-properties.spec.ts › should work with undefined, null, and empty [fail] +page/jshandle-properties.spec.ts › should work with unserializable values [fail] +page/jshandle-to-string.spec.ts › should beautifully render sparse arrays [fail] +page/jshandle-to-string.spec.ts › should work for complicated objects [fail] +page/jshandle-to-string.spec.ts › should work for primitives [pass] +page/jshandle-to-string.spec.ts › should work for promises [fail] +page/jshandle-to-string.spec.ts › should work with different subtypes @smoke [fail] +page/jshandle-to-string.spec.ts › should work with previewable subtypes [fail] +page/locator-click.spec.ts › should click if the target element is removed in pointerdown event [fail] +page/locator-click.spec.ts › should click if the target element is removed in pointerup event [fail] +page/locator-click.spec.ts › should double click the button [fail] +page/locator-click.spec.ts › should work @smoke [pass] +page/locator-click.spec.ts › should work for TextNodes [fail] +page/locator-click.spec.ts › should work with Node removed [pass] +page/locator-convenience.spec.ts › allInnerTexts should work [fail] +page/locator-convenience.spec.ts › allTextContents should work [fail] +page/locator-convenience.spec.ts › getAttribute should work [pass] +page/locator-convenience.spec.ts › innerHTML should work [pass] +page/locator-convenience.spec.ts › innerText should produce log [fail] +page/locator-convenience.spec.ts › innerText should throw [fail] +page/locator-convenience.spec.ts › innerText should work [pass] +page/locator-convenience.spec.ts › inputValue should work [pass] +page/locator-convenience.spec.ts › isChecked should work [fail] +page/locator-convenience.spec.ts › isChecked should work for indeterminate input [fail] +page/locator-convenience.spec.ts › isEditable should work [fail] +page/locator-convenience.spec.ts › isEnabled and isDisabled should work [fail] +page/locator-convenience.spec.ts › should have a nice preview [pass] +page/locator-convenience.spec.ts › should return page [fail] +page/locator-convenience.spec.ts › textContent should work [pass] +page/locator-element-handle.spec.ts › should query existing element @smoke [fail] +page/locator-element-handle.spec.ts › should query existing elements [fail] +page/locator-element-handle.spec.ts › should return empty array for non-existing elements [fail] +page/locator-element-handle.spec.ts › xpath should query existing element [fail] +page/locator-element-handle.spec.ts › xpath should return null for non-existing element [fail] +page/locator-evaluate.spec.ts › should not throw in case of missing selector for all [fail] +page/locator-evaluate.spec.ts › should retrieve content from subtree [fail] +page/locator-evaluate.spec.ts › should retrieve content from subtree for all [fail] +page/locator-evaluate.spec.ts › should work @smoke [fail] +page/locator-evaluate.spec.ts › should work for all [fail] +page/locator-frame.spec.ts › click should survive frame reattach [pass] +page/locator-frame.spec.ts › click should survive iframe navigation [pass] +page/locator-frame.spec.ts › frameLocator.owner should work [pass] +page/locator-frame.spec.ts › getBy coverage [pass] +page/locator-frame.spec.ts › locator.contentFrame should work [pass] +page/locator-frame.spec.ts › locator.frameLocator should not throw on first/last/nth [fail] +page/locator-frame.spec.ts › locator.frameLocator should throw on ambiguity [pass] +page/locator-frame.spec.ts › locator.frameLocator should work for iframe [pass] +page/locator-frame.spec.ts › should click in lazy iframe [pass] +page/locator-frame.spec.ts › should non work for non-frame [fail] +page/locator-frame.spec.ts › should not wait for frame [pass] +page/locator-frame.spec.ts › should not wait for frame 2 [pass] +page/locator-frame.spec.ts › should not wait for frame 3 [pass] +page/locator-frame.spec.ts › should wait for frame [pass] +page/locator-frame.spec.ts › should wait for frame 2 [pass] +page/locator-frame.spec.ts › should wait for frame to go [pass] +page/locator-frame.spec.ts › should work for $ and $$ [pass] +page/locator-frame.spec.ts › should work for iframe @smoke [pass] +page/locator-frame.spec.ts › should work for nested iframe [pass] +page/locator-frame.spec.ts › should work with COEP/COOP/CORP isolated iframe [fail] +page/locator-frame.spec.ts › wait for hidden should succeed when frame is not in dom [pass] +page/locator-frame.spec.ts › waitFor should survive frame reattach [pass] +page/locator-highlight.spec.ts › should highlight locator [fail] +page/locator-is-visible.spec.ts › isVisible and isHidden should work [fail] +page/locator-is-visible.spec.ts › isVisible and isHidden should work with details [fail] +page/locator-is-visible.spec.ts › isVisible during navigation should not throw [fail] +page/locator-is-visible.spec.ts › isVisible inside a button [fail] +page/locator-is-visible.spec.ts › isVisible inside a role=button [fail] +page/locator-is-visible.spec.ts › isVisible should be true for element outside view [fail] +page/locator-is-visible.spec.ts › isVisible should be true for opacity:0 [fail] +page/locator-is-visible.spec.ts › isVisible with invalid selector should throw [pass] +page/locator-list.spec.ts › locator.all should work [fail] +page/locator-misc-1.spec.ts › focus should respect strictness [fail] +page/locator-misc-1.spec.ts › should check the box [fail] +page/locator-misc-1.spec.ts › should check the box using setChecked [fail] +page/locator-misc-1.spec.ts › should clear input [pass] +page/locator-misc-1.spec.ts › should dispatch click event via ElementHandles [pass] +page/locator-misc-1.spec.ts › should fill input [pass] +page/locator-misc-1.spec.ts › should fill input when Node is removed [fail] +page/locator-misc-1.spec.ts › should focus and blur a button [pass] +page/locator-misc-1.spec.ts › should hover @smoke [pass] +page/locator-misc-1.spec.ts › should hover when Node is removed [pass] +page/locator-misc-1.spec.ts › should select single option [pass] +page/locator-misc-1.spec.ts › should uncheck the box [fail] +page/locator-misc-1.spec.ts › should upload the file [fail] +page/locator-misc-2.spec.ts › Locator.locator() and FrameLocator.locator() should accept locator [fail] +page/locator-misc-2.spec.ts › locator.count should work with deleted Map in main world [pass] +page/locator-misc-2.spec.ts › should combine visible with other selectors [fail] +page/locator-misc-2.spec.ts › should press @smoke [fail] +page/locator-misc-2.spec.ts › should pressSequentially [fail] +page/locator-misc-2.spec.ts › should return bounding box [fail] +page/locator-misc-2.spec.ts › should scroll into view [pass] +page/locator-misc-2.spec.ts › should scroll zero-sized element into view [fail] +page/locator-misc-2.spec.ts › should select textarea [fail] +page/locator-misc-2.spec.ts › should take screenshot [fail] +page/locator-misc-2.spec.ts › should type [fail] +page/locator-misc-2.spec.ts › should waitFor [fail] +page/locator-misc-2.spec.ts › should waitFor hidden [fail] +page/locator-query.spec.ts › alias methods coverage [fail] +page/locator-query.spec.ts › should allow some, but not all nested frameLocators [fail] +page/locator-query.spec.ts › should enforce same frame for has/leftOf/rightOf/above/below/near [pass] +page/locator-query.spec.ts › should filter by case-insensitive regex in a child [fail] +page/locator-query.spec.ts › should filter by case-insensitive regex in multiple children [fail] +page/locator-query.spec.ts › should filter by regex [fail] +page/locator-query.spec.ts › should filter by regex and regexp flags [fail] +page/locator-query.spec.ts › should filter by regex with a single quote [fail] +page/locator-query.spec.ts › should filter by regex with quotes [fail] +page/locator-query.spec.ts › should filter by regex with special symbols [fail] +page/locator-query.spec.ts › should filter by text [fail] +page/locator-query.spec.ts › should filter by text 2 [fail] +page/locator-query.spec.ts › should filter by text with quotes [fail] +page/locator-query.spec.ts › should respect first() and last() @smoke [fail] +page/locator-query.spec.ts › should respect nth() [fail] +page/locator-query.spec.ts › should support has:locator [fail] +page/locator-query.spec.ts › should support locator.and [fail] +page/locator-query.spec.ts › should support locator.filter [fail] +page/locator-query.spec.ts › should support locator.locator with and/or [fail] +page/locator-query.spec.ts › should support locator.or [fail] +page/locator-query.spec.ts › should throw on capture w/ nth() [fail] +page/locator-query.spec.ts › should throw on due to strictness [fail] +page/locator-query.spec.ts › should throw on due to strictness 2 [fail] +page/matchers.misc.spec.ts › should outlive frame navigation [pass] +page/matchers.misc.spec.ts › should print no-locator-resolved error when locator matcher did not resolve to any element [pass] +page/network-post-data.spec.ts › should get post data for file/blob [fail] +page/network-post-data.spec.ts › should get post data for navigator.sendBeacon api calls [fail] +page/network-post-data.spec.ts › should return correct postData buffer for utf-8 body [fail] +page/network-post-data.spec.ts › should return post data for PUT requests [fail] +page/network-post-data.spec.ts › should return post data w/o content-type @smoke [fail] +page/network-post-data.spec.ts › should throw on invalid JSON in post data [fail] +page/page-accessibility.spec.ts › autocomplete [fail] +page/page-accessibility.spec.ts › checkbox with and tabIndex and label should not have children [fail] +page/page-accessibility.spec.ts › checkbox without label should not have children [fail] +page/page-accessibility.spec.ts › keyshortcuts [fail] +page/page-accessibility.spec.ts › multiselectable [fail] +page/page-accessibility.spec.ts › non editable textbox with role and tabIndex and label should not have children [fail] +page/page-accessibility.spec.ts › orientation [fail] +page/page-accessibility.spec.ts › rich text editable fields should have children [fail] +page/page-accessibility.spec.ts › rich text editable fields with role should have children [fail] +page/page-accessibility.spec.ts › roledescription [fail] +page/page-accessibility.spec.ts › should not report text nodes inside controls [fail] +page/page-accessibility.spec.ts › should return null when the element is no longer in DOM [fail] +page/page-accessibility.spec.ts › should show uninteresting nodes [fail] +page/page-accessibility.spec.ts › should work @smoke [fail] +page/page-accessibility.spec.ts › should work a button [fail] +page/page-accessibility.spec.ts › should work an input [fail] +page/page-accessibility.spec.ts › should work on a menu [fail] +page/page-accessibility.spec.ts › should work when there is a title [fail] +page/page-accessibility.spec.ts › should work with aria-invalid accessibility tree [fail] +page/page-accessibility.spec.ts › should work with regular text [fail] +page/page-add-init-script.spec.ts › init script should run only once in iframe [pass] +page/page-add-init-script.spec.ts › init script should run only once in popup [timeout] +page/page-add-init-script.spec.ts › should evaluate before anything else on the page [pass] +page/page-add-init-script.spec.ts › should support multiple scripts [pass] +page/page-add-init-script.spec.ts › should throw without path and content [pass] +page/page-add-init-script.spec.ts › should work after a cross origin navigation [pass] +page/page-add-init-script.spec.ts › should work with CSP [fail] +page/page-add-init-script.spec.ts › should work with a path [pass] +page/page-add-init-script.spec.ts › should work with content @smoke [pass] +page/page-add-init-script.spec.ts › should work with trailing comments [pass] +page/page-add-locator-handler.spec.ts › should not work with force:true [pass] +page/page-add-locator-handler.spec.ts › should removeLocatorHandler [pass] +page/page-add-locator-handler.spec.ts › should throw when handler times out [pass] +page/page-add-locator-handler.spec.ts › should throw when page closes [pass] +page/page-add-locator-handler.spec.ts › should wait for hidden by default [pass] +page/page-add-locator-handler.spec.ts › should wait for hidden by default 2 [pass] +page/page-add-locator-handler.spec.ts › should work [pass] +page/page-add-locator-handler.spec.ts › should work when owner frame detaches [pass] +page/page-add-locator-handler.spec.ts › should work with a custom check [pass] +page/page-add-locator-handler.spec.ts › should work with locator.hover() [pass] +page/page-add-locator-handler.spec.ts › should work with locator.waitFor [pass] +page/page-add-locator-handler.spec.ts › should work with noWaitAfter [pass] +page/page-add-locator-handler.spec.ts › should work with times: option [pass] +page/page-add-locator-handler.spec.ts › should work with toBeVisible [pass] +page/page-add-locator-handler.spec.ts › should work with toHaveScreenshot [fail] +page/page-add-script-tag.spec.ts › should include sourceURL when path is provided [pass] +page/page-add-script-tag.spec.ts › should throw a nice error when the request fails [pass] +page/page-add-script-tag.spec.ts › should throw an error if loading from url fail [pass] +page/page-add-script-tag.spec.ts › should throw an error if no options are provided [pass] +page/page-add-script-tag.spec.ts › should throw when added with URL to the CSP page [pass] +page/page-add-script-tag.spec.ts › should throw when added with content to the CSP page [pass] +page/page-add-script-tag.spec.ts › should work with a content and type=module [pass] +page/page-add-script-tag.spec.ts › should work with a path [pass] +page/page-add-script-tag.spec.ts › should work with a path and type=module [pass] +page/page-add-script-tag.spec.ts › should work with a url [pass] +page/page-add-script-tag.spec.ts › should work with a url and type=module [pass] +page/page-add-script-tag.spec.ts › should work with content [pass] +page/page-add-style-tag.spec.ts › should include sourceURL when path is provided [pass] +page/page-add-style-tag.spec.ts › should throw an error if loading from url fail [pass] +page/page-add-style-tag.spec.ts › should throw an error if no options are provided [pass] +page/page-add-style-tag.spec.ts › should throw when added with URL to the CSP page [pass] +page/page-add-style-tag.spec.ts › should throw when added with content to the CSP page [pass] +page/page-add-style-tag.spec.ts › should work with a path [fail] +page/page-add-style-tag.spec.ts › should work with a url @smoke [pass] +page/page-add-style-tag.spec.ts › should work with content [pass] +page/page-autowaiting-basic.spec.ts › should await cross-process navigation when clicking anchor [fail] +page/page-autowaiting-basic.spec.ts › should await form-get on click [fail] +page/page-autowaiting-basic.spec.ts › should await form-post on click [fail] +page/page-autowaiting-basic.spec.ts › should await navigation when clicking anchor [fail] +page/page-autowaiting-basic.spec.ts › should not stall on JS navigation link [fail] +page/page-autowaiting-basic.spec.ts › should report navigation in the log when clicking anchor [fail] +page/page-autowaiting-basic.spec.ts › should work with dblclick without noWaitAfter when navigation is stalled [fail] +page/page-autowaiting-basic.spec.ts › should work with goto following click [fail] +page/page-autowaiting-basic.spec.ts › should work with noWaitAfter: true [fail] +page/page-autowaiting-basic.spec.ts › should work with waitForLoadState(load) [fail] +page/page-autowaiting-no-hang.spec.ts › assigning location to about:blank [pass] +page/page-autowaiting-no-hang.spec.ts › assigning location to about:blank after non-about:blank [pass] +page/page-autowaiting-no-hang.spec.ts › calling window.open and window.close [fail] +page/page-autowaiting-no-hang.spec.ts › calling window.stop async [pass] +page/page-autowaiting-no-hang.spec.ts › calling window.stop sync [pass] +page/page-autowaiting-no-hang.spec.ts › clicking on links which do not commit navigation [fail] +page/page-autowaiting-no-hang.spec.ts › opening a popup [pass] +page/page-basic.spec.ts › async stacks should work [pass] +page/page-basic.spec.ts › frame.press should work [fail] +page/page-basic.spec.ts › has navigator.webdriver set to true [fail] +page/page-basic.spec.ts › page.close should work with page.close [pass] +page/page-basic.spec.ts › page.close should work with window.close [timeout] +page/page-basic.spec.ts › page.frame should respect name [fail] +page/page-basic.spec.ts › page.frame should respect url [fail] +page/page-basic.spec.ts › page.press should work [pass] +page/page-basic.spec.ts › page.press should work for Enter [fail] +page/page-basic.spec.ts › page.title should return the page title [pass] +page/page-basic.spec.ts › page.url should include hashes [pass] +page/page-basic.spec.ts › page.url should work [pass] +page/page-basic.spec.ts › should be callable twice [pass] +page/page-basic.spec.ts › should fail with error upon disconnect [pass] +page/page-basic.spec.ts › should fire domcontentloaded when expected [pass] +page/page-basic.spec.ts › should fire load when expected [fail] +page/page-basic.spec.ts › should have sane user agent [fail] +page/page-basic.spec.ts › should iterate over page properties [pass] +page/page-basic.spec.ts › should pass page to close event [pass] +page/page-basic.spec.ts › should pass self as argument to domcontentloaded event [pass] +page/page-basic.spec.ts › should pass self as argument to load event [pass] +page/page-basic.spec.ts › should provide access to the opener page [timeout] +page/page-basic.spec.ts › should reject all promises when page is closed [pass] +page/page-basic.spec.ts › should return null if parent page has been closed [timeout] +page/page-basic.spec.ts › should set the page close state [pass] +page/page-basic.spec.ts › should terminate network waiters [pass] +page/page-check.spec.ts › should check radio [fail] +page/page-check.spec.ts › should check radio by aria role [fail] +page/page-check.spec.ts › should check the box @smoke [fail] +page/page-check.spec.ts › should check the box by aria role [fail] +page/page-check.spec.ts › should check the box inside a button [fail] +page/page-check.spec.ts › should check the box using setChecked [fail] +page/page-check.spec.ts › should check the label with position [fail] +page/page-check.spec.ts › should not check the checked box [fail] +page/page-check.spec.ts › should not uncheck the unchecked box [fail] +page/page-check.spec.ts › should throw when not a checkbox [fail] +page/page-check.spec.ts › should throw when not a checkbox 2 [fail] +page/page-check.spec.ts › should uncheck radio by aria role [fail] +page/page-check.spec.ts › should uncheck the box [fail] +page/page-check.spec.ts › should uncheck the box by aria role [fail] +page/page-check.spec.ts › trial run should not check [fail] +page/page-check.spec.ts › trial run should not uncheck [fail] +page/page-click-during-navigation.spec.ts › should not fail with internal error upon navigation [pass] +page/page-click-react.spec.ts › should not retarget the handle when element is recycled [unknown] +page/page-click-react.spec.ts › should not retarget when element changes on hover [pass] +page/page-click-react.spec.ts › should not retarget when element is recycled on hover [pass] +page/page-click-react.spec.ts › should report that selector does not match anymore [unknown] +page/page-click-react.spec.ts › should retarget when element is recycled before enabled check [unknown] +page/page-click-react.spec.ts › should retarget when element is recycled during hit testing [unknown] +page/page-click-react.spec.ts › should timeout when click opens alert [fail] +page/page-click-scroll.spec.ts › should not crash when force-clicking hidden input [fail] +page/page-click-scroll.spec.ts › should not hit scroll bar [fail] +page/page-click-scroll.spec.ts › should scroll into view display:contents [fail] +page/page-click-scroll.spec.ts › should scroll into view display:contents with a child [fail] +page/page-click-scroll.spec.ts › should scroll into view display:contents with position [fail] +page/page-click-scroll.spec.ts › should scroll into view element in iframe [fail] +page/page-click-scroll.spec.ts › should scroll into view span element [fail] +page/page-click-timeout-1.spec.ts › should avoid side effects after timeout [pass] +page/page-click-timeout-1.spec.ts › should timeout waiting for button to be enabled [fail] +page/page-click-timeout-2.spec.ts › should timeout waiting for display:none to be gone [pass] +page/page-click-timeout-2.spec.ts › should timeout waiting for visibility:hidden to be gone [pass] +page/page-click-timeout-3.spec.ts › should fail when element jumps during hit testing [fail] +page/page-click-timeout-3.spec.ts › should report wrong hit target subtree [pass] +page/page-click-timeout-3.spec.ts › should still click when force but hit target is obscured [pass] +page/page-click-timeout-3.spec.ts › should timeout waiting for hit target [pass] +page/page-click-timeout-4.spec.ts › should click for the second time after first timeout [pass] +page/page-click-timeout-4.spec.ts › should timeout waiting for stable position [pass] +page/page-click.spec.ts › ensure events are dispatched in the individual tasks [fail] +page/page-click.spec.ts › should click a button in scrolling container with offset [pass] +page/page-click.spec.ts › should click a button that is overlaid by a permission popup [fail] +page/page-click.spec.ts › should click a partially obscured button [pass] +page/page-click.spec.ts › should click a rotated button [pass] +page/page-click.spec.ts › should click a very large button with offset [fail] +page/page-click.spec.ts › should click an offscreen element when scroll-behavior is smooth [fail] +page/page-click.spec.ts › should click button inside frameset [pass] +page/page-click.spec.ts › should click disabled div [fail] +page/page-click.spec.ts › should click if opened select covers the button [fail] +page/page-click.spec.ts › should click in a nested transformed iframe [fail] +page/page-click.spec.ts › should click in a transformed iframe [fail] +page/page-click.spec.ts › should click in a transformed iframe with force [fail] +page/page-click.spec.ts › should click in an iframe with border [fail] +page/page-click.spec.ts › should click in an iframe with border 2 [fail] +page/page-click.spec.ts › should click links which cause navigation [fail] +page/page-click.spec.ts › should click offscreen buttons [pass] +page/page-click.spec.ts › should click on a span with an inline element inside [fail] +page/page-click.spec.ts › should click on checkbox input and toggle [pass] +page/page-click.spec.ts › should click on checkbox label and toggle [pass] +page/page-click.spec.ts › should click svg [fail] +page/page-click.spec.ts › should click the 1x1 div [fail] +page/page-click.spec.ts › should click the button @smoke [pass] +page/page-click.spec.ts › should click the button after a cross origin navigation [pass] +page/page-click.spec.ts › should click the button after navigation [pass] +page/page-click.spec.ts › should click the button behind sticky header [fail] +page/page-click.spec.ts › should click the button if window.Node is removed [pass] +page/page-click.spec.ts › should click the button inside an iframe [fail] +page/page-click.spec.ts › should click the button when window.innerWidth is corrupted [pass] +page/page-click.spec.ts › should click the button with em border with offset [pass] +page/page-click.spec.ts › should click the button with fixed position inside an iframe [fail] +page/page-click.spec.ts › should click the button with px border with offset [pass] +page/page-click.spec.ts › should click when one of inline box children is outside of viewport [fail] +page/page-click.spec.ts › should click wrapped links [pass] +page/page-click.spec.ts › should click zero-sized input by label [fail] +page/page-click.spec.ts › should climb dom for inner label with pointer-events:none [fail] +page/page-click.spec.ts › should climb up to [role=button] [fail] +page/page-click.spec.ts › should climb up to a [role=link] [fail] +page/page-click.spec.ts › should climb up to a anchor [fail] +page/page-click.spec.ts › should dispatch microtasks in order [fail] +page/page-click.spec.ts › should double click the button [fail] +page/page-click.spec.ts › should fail when element detaches after animation [pass] +page/page-click.spec.ts › should fail when element is animating from outside the viewport with force [fail] +page/page-click.spec.ts › should fail when obscured and not waiting for hit target [pass] +page/page-click.spec.ts › should fire contextmenu event on right click [pass] +page/page-click.spec.ts › should fire contextmenu event on right click in correct order [fail] +page/page-click.spec.ts › should issue clicks in parallel in page and popup [pass] +page/page-click.spec.ts › should not hang when frame is detached [pass] +page/page-click.spec.ts › should not throw UnhandledPromiseRejection when page closes [pass] +page/page-click.spec.ts › should not throw protocol error when navigating during the click [pass] +page/page-click.spec.ts › should not wait with force [pass] +page/page-click.spec.ts › should report nice error when element is detached and force-clicked [pass] +page/page-click.spec.ts › should retry when element detaches after animation [pass] +page/page-click.spec.ts › should retry when element is animating from outside the viewport [fail] +page/page-click.spec.ts › should retry when navigating during the click [pass] +page/page-click.spec.ts › should scroll and click the button [pass] +page/page-click.spec.ts › should scroll and click the button with smooth scroll behavior [pass] +page/page-click.spec.ts › should select the text by triple clicking [fail] +page/page-click.spec.ts › should update modifiers correctly [pass] +page/page-click.spec.ts › should wait for BUTTON to be clickable when it has pointer-events:none [fail] +page/page-click.spec.ts › should wait for LABEL to be clickable when it has pointer-events:none [fail] +page/page-click.spec.ts › should wait for becoming hit target [pass] +page/page-click.spec.ts › should wait for becoming hit target with trial run [pass] +page/page-click.spec.ts › should wait for button to be enabled [fail] +page/page-click.spec.ts › should wait for input to be enabled [fail] +page/page-click.spec.ts › should wait for select to be enabled [fail] +page/page-click.spec.ts › should wait for stable position [pass] +page/page-click.spec.ts › should waitFor display:none to be gone [pass] +page/page-click.spec.ts › should waitFor visibility:hidden to be gone [pass] +page/page-click.spec.ts › should waitFor visible when already visible [pass] +page/page-click.spec.ts › should waitFor visible when parent is hidden [pass] +page/page-click.spec.ts › trial run should not click [pass] +page/page-click.spec.ts › trial run should not double click [pass] +page/page-click.spec.ts › trial run should work with short timeout [pass] +page/page-close.spec.ts › should close page with active dialog [fail] +page/page-close.spec.ts › should not accept dialog after close [fail] +page/page-dialog.spec.ts › should accept the confirm prompt [pass] +page/page-dialog.spec.ts › should allow accepting prompts @smoke [pass] +page/page-dialog.spec.ts › should auto-dismiss the alert without listeners [fail] +page/page-dialog.spec.ts › should auto-dismiss the prompt without listeners [pass] +page/page-dialog.spec.ts › should be able to close context with open alert [pass] +page/page-dialog.spec.ts › should dismiss the confirm prompt [pass] +page/page-dialog.spec.ts › should dismiss the prompt [pass] +page/page-dialog.spec.ts › should fire [pass] +page/page-dialog.spec.ts › should handle multiple alerts [fail] +page/page-dialog.spec.ts › should handle multiple confirms [fail] +page/page-dispatchevent.spec.ts › should be atomic [fail] +page/page-dispatchevent.spec.ts › should dispatch absolute device orientation event [pass] +page/page-dispatchevent.spec.ts › should dispatch click after a cross origin navigation [pass] +page/page-dispatchevent.spec.ts › should dispatch click after navigation [pass] +page/page-dispatchevent.spec.ts › should dispatch click event @smoke [pass] +page/page-dispatchevent.spec.ts › should dispatch click event properties [pass] +page/page-dispatchevent.spec.ts › should dispatch click event via ElementHandles [pass] +page/page-dispatchevent.spec.ts › should dispatch click on a span with an inline element inside [fail] +page/page-dispatchevent.spec.ts › should dispatch click svg [fail] +page/page-dispatchevent.spec.ts › should dispatch click when node is added in shadow dom [pass] +page/page-dispatchevent.spec.ts › should dispatch device motion event [pass] +page/page-dispatchevent.spec.ts › should dispatch device orientation event [pass] +page/page-dispatchevent.spec.ts › should dispatch drag drop events [pass] +page/page-dispatchevent.spec.ts › should dispatch drag drop events via ElementHandles [pass] +page/page-dispatchevent.spec.ts › should dispatch wheel event [pass] +page/page-dispatchevent.spec.ts › should not fail when element is blocked on hover [fail] +page/page-dispatchevent.spec.ts › should throw if argument is from different frame [pass] +page/page-drag.spec.ts › Drag and drop › iframe › should drag into an iframe [unknown] +page/page-drag.spec.ts › Drag and drop › iframe › should drag out of an iframe [unknown] +page/page-drag.spec.ts › Drag and drop › should allow specifying the position [fail] +page/page-drag.spec.ts › Drag and drop › should be able to drag the mouse in a frame [pass] +page/page-drag.spec.ts › Drag and drop › should cancel on escape [fail] +page/page-drag.spec.ts › Drag and drop › should not send dragover on the first mousemove [unknown] +page/page-drag.spec.ts › Drag and drop › should respect the drop effect [fail] +page/page-drag.spec.ts › Drag and drop › should send the right events [fail] +page/page-drag.spec.ts › Drag and drop › should work @smoke [fail] +page/page-drag.spec.ts › Drag and drop › should work if a frame is stalled [fail] +page/page-drag.spec.ts › Drag and drop › should work if the drag event is captured but not canceled [fail] +page/page-drag.spec.ts › Drag and drop › should work if the drag is canceled [pass] +page/page-drag.spec.ts › Drag and drop › should work inside iframe [fail] +page/page-drag.spec.ts › Drag and drop › should work with locators [fail] +page/page-drag.spec.ts › Drag and drop › should work with the helper method [fail] +page/page-drag.spec.ts › should handle custom dataTransfer [fail] +page/page-drag.spec.ts › should report event.buttons [pass] +page/page-drag.spec.ts › should work if not doing a drag [pass] +page/page-drag.spec.ts › what happens when dragging element is destroyed [fail] +page/page-emulate-media.spec.ts › should change the actual colors in css [fail] +page/page-emulate-media.spec.ts › should default to light [fail] +page/page-emulate-media.spec.ts › should emulate colorScheme should work @smoke [fail] +page/page-emulate-media.spec.ts › should emulate forcedColors [fail] +page/page-emulate-media.spec.ts › should emulate reduced motion [fail] +page/page-emulate-media.spec.ts › should emulate type @smoke [fail] +page/page-emulate-media.spec.ts › should keep reduced motion and color emulation after reload [fail] +page/page-emulate-media.spec.ts › should throw in case of bad colorScheme argument [pass] +page/page-emulate-media.spec.ts › should throw in case of bad media argument [pass] +page/page-emulate-media.spec.ts › should work during navigation [fail] +page/page-evaluate-handle.spec.ts › should accept multiple nested handles [pass] +page/page-evaluate-handle.spec.ts › should accept nested handle [pass] +page/page-evaluate-handle.spec.ts › should accept nested window handle [pass] +page/page-evaluate-handle.spec.ts › should accept object handle as an argument [pass] +page/page-evaluate-handle.spec.ts › should accept object handle to primitive types [pass] +page/page-evaluate-handle.spec.ts › should accept object handle to unserializable value [pass] +page/page-evaluate-handle.spec.ts › should accept same handle multiple times [pass] +page/page-evaluate-handle.spec.ts › should accept same nested object multiple times [pass] +page/page-evaluate-handle.spec.ts › should pass configurable args [pass] +page/page-evaluate-handle.spec.ts › should work [pass] +page/page-evaluate-handle.spec.ts › should work with primitives [pass] +page/page-evaluate-no-stall.spec.ts › should throw when no main execution context [fail] +page/page-evaluate-no-stall.spec.ts › should throw while pending navigation [pass] +page/page-evaluate-no-stall.spec.ts › should work [pass] +page/page-evaluate.spec.ts › should accept "undefined" as one of multiple parameters [pass] +page/page-evaluate.spec.ts › should accept a string [pass] +page/page-evaluate.spec.ts › should accept a string with comments [pass] +page/page-evaluate.spec.ts › should accept a string with semi colons [pass] +page/page-evaluate.spec.ts › should accept element handle as an argument [fail] +page/page-evaluate.spec.ts › should alias Window, Document and Node [pass] +page/page-evaluate.spec.ts › should await promise [pass] +page/page-evaluate.spec.ts › should await promise from popup [pass] +page/page-evaluate.spec.ts › should be able to throw a tricky error [pass] +page/page-evaluate.spec.ts › should evaluate date [pass] +page/page-evaluate.spec.ts › should evaluate exception [pass] +page/page-evaluate.spec.ts › should evaluate exception with a function on the stack [pass] +page/page-evaluate.spec.ts › should evaluate in the page context [pass] +page/page-evaluate.spec.ts › should evaluate url [pass] +page/page-evaluate.spec.ts › should ignore buggy toJSON [pass] +page/page-evaluate.spec.ts › should jsonValue() date [pass] +page/page-evaluate.spec.ts › should jsonValue() url [pass] +page/page-evaluate.spec.ts › should modify global environment [pass] +page/page-evaluate.spec.ts › should not add a toJSON property to newly created Arrays after evaluation [pass] +page/page-evaluate.spec.ts › should not expose the injected script export [pass] +page/page-evaluate.spec.ts › should not leak handles [pass] +page/page-evaluate.spec.ts › should not leak utility script [pass] +page/page-evaluate.spec.ts › should not throw an error when evaluation does a navigation [pass] +page/page-evaluate.spec.ts › should not throw an error when evaluation does a synchronous navigation and returns an object [pass] +page/page-evaluate.spec.ts › should not throw an error when evaluation does a synchronous navigation and returns undefined [pass] +page/page-evaluate.spec.ts › should not use Array.prototype.toJSON when evaluating [pass] +page/page-evaluate.spec.ts › should not use toJSON in jsonValue [pass] +page/page-evaluate.spec.ts › should not use toJSON when evaluating [pass] +page/page-evaluate.spec.ts › should pass exception argument [pass] +page/page-evaluate.spec.ts › should properly serialize PerformanceMeasure object [pass] +page/page-evaluate.spec.ts › should properly serialize null arguments [pass] +page/page-evaluate.spec.ts › should properly serialize null fields [pass] +page/page-evaluate.spec.ts › should properly serialize undefined arguments [pass] +page/page-evaluate.spec.ts › should properly serialize undefined fields [pass] +page/page-evaluate.spec.ts › should properly serialize window.performance object [pass] +page/page-evaluate.spec.ts › should reject promise with exception [pass] +page/page-evaluate.spec.ts › should respect use strict expression [pass] +page/page-evaluate.spec.ts › should return -0 [pass] +page/page-evaluate.spec.ts › should return -Infinity [pass] +page/page-evaluate.spec.ts › should return Infinity [pass] +page/page-evaluate.spec.ts › should return NaN [pass] +page/page-evaluate.spec.ts › should return complex objects [pass] +page/page-evaluate.spec.ts › should return undefined for non-serializable objects [pass] +page/page-evaluate.spec.ts › should return undefined for objects with symbols [pass] +page/page-evaluate.spec.ts › should return undefined properties [pass] +page/page-evaluate.spec.ts › should roundtrip date [pass] +page/page-evaluate.spec.ts › should roundtrip promise to unserializable values [pass] +page/page-evaluate.spec.ts › should roundtrip promise to value [pass] +page/page-evaluate.spec.ts › should roundtrip regex [pass] +page/page-evaluate.spec.ts › should roundtrip unserializable values [pass] +page/page-evaluate.spec.ts › should roundtrip url [pass] +page/page-evaluate.spec.ts › should simulate a user gesture [pass] +page/page-evaluate.spec.ts › should support thrown numbers as error messages [pass] +page/page-evaluate.spec.ts › should support thrown strings as error messages [pass] +page/page-evaluate.spec.ts › should throw a nice error after a navigation [pass] +page/page-evaluate.spec.ts › should throw error with detailed information on exception inside promise [pass] +page/page-evaluate.spec.ts › should throw if underlying element was disposed [fail] +page/page-evaluate.spec.ts › should throw when evaluation triggers reload [pass] +page/page-evaluate.spec.ts › should throw when frame is detached [pass] +page/page-evaluate.spec.ts › should throw when passed more than one parameter [pass] +page/page-evaluate.spec.ts › should transfer -0 [pass] +page/page-evaluate.spec.ts › should transfer -Infinity [pass] +page/page-evaluate.spec.ts › should transfer 100Mb of data from page to node.js [fail] +page/page-evaluate.spec.ts › should transfer Infinity [fail] +page/page-evaluate.spec.ts › should transfer NaN [pass] +page/page-evaluate.spec.ts › should transfer arrays [pass] +page/page-evaluate.spec.ts › should transfer arrays as arrays, not objects [pass] +page/page-evaluate.spec.ts › should transfer bigint [pass] +page/page-evaluate.spec.ts › should transfer maps as empty objects [pass] +page/page-evaluate.spec.ts › should work @smoke [pass] +page/page-evaluate.spec.ts › should work even when JSON is set to null [pass] +page/page-evaluate.spec.ts › should work for circular object [pass] +page/page-evaluate.spec.ts › should work from-inside an exposed function [pass] +page/page-evaluate.spec.ts › should work right after a cross-origin navigation [fail] +page/page-evaluate.spec.ts › should work right after framenavigated [fail] +page/page-evaluate.spec.ts › should work with Array.from/map [pass] +page/page-evaluate.spec.ts › should work with CSP [fail] +page/page-evaluate.spec.ts › should work with busted Array.prototype.map/push [pass] +page/page-evaluate.spec.ts › should work with function shorthands [pass] +page/page-evaluate.spec.ts › should work with large strings [pass] +page/page-evaluate.spec.ts › should work with large unicode strings [pass] +page/page-evaluate.spec.ts › should work with new Function() and CSP [fail] +page/page-evaluate.spec.ts › should work with non-strict expressions [pass] +page/page-evaluate.spec.ts › should work with overridden Object.defineProperty [pass] +page/page-evaluate.spec.ts › should work with overridden URL/Date/RegExp [pass] +page/page-evaluate.spec.ts › should work with overridden globalThis.Window/Document/Node [pass] +page/page-evaluate.spec.ts › should work with overwritten Promise [pass] +page/page-evaluate.spec.ts › should work with unicode chars [pass] +page/page-event-console.spec.ts › do not update console count on unhandled rejections [pass] +page/page-event-console.spec.ts › should emit same log twice [pass] +page/page-event-console.spec.ts › should format the message correctly with time/timeLog/timeEnd [fail] +page/page-event-console.spec.ts › should have location for console API calls [fail] +page/page-event-console.spec.ts › should not fail for window object [fail] +page/page-event-console.spec.ts › should not throw when there are console messages in detached iframes [pass] +page/page-event-console.spec.ts › should trigger correct Log [timeout] +page/page-event-console.spec.ts › should use object previews for arrays and objects [fail] +page/page-event-console.spec.ts › should use object previews for errors [pass] +page/page-event-console.spec.ts › should use text() for inspection [pass] +page/page-event-console.spec.ts › should work @smoke [fail] +page/page-event-console.spec.ts › should work for different console API calls [fail] +page/page-event-load.spec.ts › should fire once [pass] +page/page-event-load.spec.ts › should fire once with iframe navigation [pass] +page/page-event-network.spec.ts › Page.Events.Request @smoke [fail] +page/page-event-network.spec.ts › Page.Events.RequestFailed @smoke [fail] +page/page-event-network.spec.ts › Page.Events.RequestFinished @smoke [pass] +page/page-event-network.spec.ts › Page.Events.Response @smoke [pass] +page/page-event-network.spec.ts › interrupt request.response() and request.allHeaders() on page.close [fail] +page/page-event-network.spec.ts › should fire events in proper order [pass] +page/page-event-network.spec.ts › should resolve responses after a navigation [pass] +page/page-event-network.spec.ts › should support redirects [pass] +page/page-event-pageerror.spec.ts › should contain sourceURL [timeout] +page/page-event-pageerror.spec.ts › should contain the Error.name property [timeout] +page/page-event-pageerror.spec.ts › should emit error from unhandled rejects [fail] +page/page-event-pageerror.spec.ts › should fire [timeout] +page/page-event-pageerror.spec.ts › should handle object [timeout] +page/page-event-pageerror.spec.ts › should handle odd values [timeout] +page/page-event-pageerror.spec.ts › should handle window [timeout] +page/page-event-pageerror.spec.ts › should not receive console message for pageError [timeout] +page/page-event-pageerror.spec.ts › should remove a listener of a non-existing event handler [pass] +page/page-event-pageerror.spec.ts › should support an empty Error.name property [timeout] +page/page-event-popup.spec.ts › should be able to capture alert [timeout] +page/page-event-popup.spec.ts › should emit for immediately closed popups [fail] +page/page-event-popup.spec.ts › should emit for immediately closed popups 2 [fail] +page/page-event-popup.spec.ts › should not treat navigations as new popups [fail] +page/page-event-popup.spec.ts › should report popup opened from iframes [fail] +page/page-event-popup.spec.ts › should work @smoke [timeout] +page/page-event-popup.spec.ts › should work with clicking target=_blank [fail] +page/page-event-popup.spec.ts › should work with clicking target=_blank and rel=noopener [fail] +page/page-event-popup.spec.ts › should work with empty url [pass] +page/page-event-popup.spec.ts › should work with fake-clicking target=_blank and rel=noopener [fail] +page/page-event-popup.spec.ts › should work with noopener and about:blank [pass] +page/page-event-popup.spec.ts › should work with noopener and no url [fail] +page/page-event-popup.spec.ts › should work with noopener and url [pass] +page/page-event-popup.spec.ts › should work with window features [pass] +page/page-event-request.spec.ts › main resource xhr should have type xhr [fail] +page/page-event-request.spec.ts › should fire for fetches [pass] +page/page-event-request.spec.ts › should fire for iframes [pass] +page/page-event-request.spec.ts › should fire for navigation requests [pass] +page/page-event-request.spec.ts › should fire requestfailed when intercepting race [unknown] +page/page-event-request.spec.ts › should report navigation requests and responses handled by service worker [timeout] +page/page-event-request.spec.ts › should report navigation requests and responses handled by service worker with routing [timeout] +page/page-event-request.spec.ts › should report requests and responses handled by service worker [fail] +page/page-event-request.spec.ts › should report requests and responses handled by service worker with routing [fail] +page/page-event-request.spec.ts › should return response body when Cross-Origin-Opener-Policy is set [fail] +page/page-expose-function.spec.ts › exposeBinding should work @smoke [pass] +page/page-expose-function.spec.ts › exposeBinding(handle) should work with element handles [fail] +page/page-expose-function.spec.ts › exposeBindingHandle should not throw during navigation [pass] +page/page-expose-function.spec.ts › exposeBindingHandle should throw for multiple arguments [pass] +page/page-expose-function.spec.ts › exposeBindingHandle should work [pass] +page/page-expose-function.spec.ts › should alias Window, Document and Node [pass] +page/page-expose-function.spec.ts › should await returned promise [pass] +page/page-expose-function.spec.ts › should be callable from-inside addInitScript [pass] +page/page-expose-function.spec.ts › should fail with busted Array.prototype.toJSON [pass] +page/page-expose-function.spec.ts › should not result in unhandled rejection [pass] +page/page-expose-function.spec.ts › should serialize cycles [pass] +page/page-expose-function.spec.ts › should support throwing "null" [pass] +page/page-expose-function.spec.ts › should survive navigation [pass] +page/page-expose-function.spec.ts › should throw exception in page context [pass] +page/page-expose-function.spec.ts › should throw for duplicate registrations [pass] +page/page-expose-function.spec.ts › should work [pass] +page/page-expose-function.spec.ts › should work after cross origin navigation [pass] +page/page-expose-function.spec.ts › should work on frames [pass] +page/page-expose-function.spec.ts › should work on frames before navigation [pass] +page/page-expose-function.spec.ts › should work with busted Array.prototype.map/push [pass] +page/page-expose-function.spec.ts › should work with complex objects [pass] +page/page-expose-function.spec.ts › should work with handles and complex objects [pass] +page/page-expose-function.spec.ts › should work with overridden console object [pass] +page/page-expose-function.spec.ts › should work with setContent [fail] +page/page-fill.spec.ts › fill back to back [fail] +page/page-fill.spec.ts › input event.composed should be true and cross shadow dom boundary - color [fail] +page/page-fill.spec.ts › input event.composed should be true and cross shadow dom boundary - date [fail] +page/page-fill.spec.ts › input event.composed should be true and cross shadow dom boundary - datetime-local [fail] +page/page-fill.spec.ts › input event.composed should be true and cross shadow dom boundary - month [unknown] +page/page-fill.spec.ts › input event.composed should be true and cross shadow dom boundary - range [fail] +page/page-fill.spec.ts › input event.composed should be true and cross shadow dom boundary - time [fail] +page/page-fill.spec.ts › input event.composed should be true and cross shadow dom boundary - week [unknown] +page/page-fill.spec.ts › should be able to clear using fill() [pass] +page/page-fill.spec.ts › should be able to fill exponent into the input[type=number] [fail] +page/page-fill.spec.ts › should be able to fill input[type=number] with empty string [fail] +page/page-fill.spec.ts › should be able to fill the body [fail] +page/page-fill.spec.ts › should be able to fill the input[type=number] [fail] +page/page-fill.spec.ts › should be able to fill when focus is in the wrong frame [fail] +page/page-fill.spec.ts › should fill color input [fail] +page/page-fill.spec.ts › should fill contenteditable [pass] +page/page-fill.spec.ts › should fill contenteditable with new lines [fail] +page/page-fill.spec.ts › should fill date input after clicking [fail] +page/page-fill.spec.ts › should fill datetime-local input [fail] +page/page-fill.spec.ts › should fill different input types [pass] +page/page-fill.spec.ts › should fill elements with existing value and selection [pass] +page/page-fill.spec.ts › should fill fixed position input [fail] +page/page-fill.spec.ts › should fill input [pass] +page/page-fill.spec.ts › should fill month input [fail] +page/page-fill.spec.ts › should fill range input [fail] +page/page-fill.spec.ts › should fill textarea @smoke [pass] +page/page-fill.spec.ts › should fill time input [fail] +page/page-fill.spec.ts › should fill week input [fail] +page/page-fill.spec.ts › should not be able to fill text into the input[type=number] [fail] +page/page-fill.spec.ts › should not throw when fill causes navigation [fail] +page/page-fill.spec.ts › should retry on disabled element [pass] +page/page-fill.spec.ts › should retry on invisible element [pass] +page/page-fill.spec.ts › should retry on readonly element [pass] +page/page-fill.spec.ts › should throw if passed a non-string value [pass] +page/page-fill.spec.ts › should throw nice error without injected script stack when element is not an <input> [fail] +page/page-fill.spec.ts › should throw on incorrect color value [fail] +page/page-fill.spec.ts › should throw on incorrect date [fail] +page/page-fill.spec.ts › should throw on incorrect datetime-local [unknown] +page/page-fill.spec.ts › should throw on incorrect month [unknown] +page/page-fill.spec.ts › should throw on incorrect range value [fail] +page/page-fill.spec.ts › should throw on incorrect time [fail] +page/page-fill.spec.ts › should throw on incorrect week [unknown] +page/page-fill.spec.ts › should throw on unsupported inputs [pass] +page/page-focus.spec.ts › clicking checkbox should activate it [unknown] +page/page-focus.spec.ts › keeps focus on element when attempting to focus a non-focusable element [fail] +page/page-focus.spec.ts › should emit blur event [fail] +page/page-focus.spec.ts › should emit focus event [fail] +page/page-focus.spec.ts › should traverse focus [fail] +page/page-focus.spec.ts › should traverse focus in all directions [fail] +page/page-focus.spec.ts › should traverse only form elements [unknown] +page/page-focus.spec.ts › should work @smoke [fail] +page/page-force-gc.spec.ts › should work [fail] +page/page-goto.spec.ts › js redirect overrides url bar navigation [pass] +page/page-goto.spec.ts › should be able to navigate to a page controlled by service worker [pass] +page/page-goto.spec.ts › should capture cross-process iframe navigation request [pass] +page/page-goto.spec.ts › should capture iframe navigation request [pass] +page/page-goto.spec.ts › should disable timeout when its set to 0 [pass] +page/page-goto.spec.ts › should fail when canceled by another navigation [pass] +page/page-goto.spec.ts › should fail when exceeding browser context navigation timeout [pass] +page/page-goto.spec.ts › should fail when exceeding browser context timeout [pass] +page/page-goto.spec.ts › should fail when exceeding default maximum navigation timeout [pass] +page/page-goto.spec.ts › should fail when exceeding default maximum timeout [pass] +page/page-goto.spec.ts › should fail when exceeding maximum navigation timeout [pass] +page/page-goto.spec.ts › should fail when main resources failed to load [pass] +page/page-goto.spec.ts › should fail when navigating and show the url at the error message [pass] +page/page-goto.spec.ts › should fail when navigating to bad SSL [fail] +page/page-goto.spec.ts › should fail when navigating to bad SSL after redirects [fail] +page/page-goto.spec.ts › should fail when navigating to bad url [fail] +page/page-goto.spec.ts › should fail when replaced by another navigation [pass] +page/page-goto.spec.ts › should fail when server returns 204 [timeout] +page/page-goto.spec.ts › should navigate to URL with hash and fire requests without hash [pass] +page/page-goto.spec.ts › should navigate to about:blank [pass] +page/page-goto.spec.ts › should navigate to dataURL and not fire dataURL requests [pass] +page/page-goto.spec.ts › should navigate to empty page with domcontentloaded [pass] +page/page-goto.spec.ts › should not crash when RTCPeerConnection is used [pass] +page/page-goto.spec.ts › should not crash when navigating to bad SSL after a cross origin navigation [pass] +page/page-goto.spec.ts › should not leak listeners during 20 waitForNavigation [pass] +page/page-goto.spec.ts › should not leak listeners during bad navigation [pass] +page/page-goto.spec.ts › should not leak listeners during navigation [pass] +page/page-goto.spec.ts › should not resolve goto upon window.stop() [pass] +page/page-goto.spec.ts › should not throw if networkidle0 is passed as an option [pass] +page/page-goto.spec.ts › should not throw unhandled rejections on invalid url [pass] +page/page-goto.spec.ts › should override referrer-policy [fail] +page/page-goto.spec.ts › should prioritize default navigation timeout over default timeout [pass] +page/page-goto.spec.ts › should properly wait for load [fail] +page/page-goto.spec.ts › should reject referer option when setExtraHTTPHeaders provides referer [pass] +page/page-goto.spec.ts › should report raw buffer for main resource [fail] +page/page-goto.spec.ts › should return from goto if new navigation is started [pass] +page/page-goto.spec.ts › should return last response in redirect chain [pass] +page/page-goto.spec.ts › should return response when page changes its URL after load [pass] +page/page-goto.spec.ts › should return url with basic auth info [pass] +page/page-goto.spec.ts › should return when navigation is committed if commit is specified [pass] +page/page-goto.spec.ts › should send referer [fail] +page/page-goto.spec.ts › should send referer of cross-origin URL [fail] +page/page-goto.spec.ts › should succeed on url bar navigation when there is pending navigation [pass] +page/page-goto.spec.ts › should throw if networkidle2 is passed as an option [fail] +page/page-goto.spec.ts › should use http for no protocol [pass] +page/page-goto.spec.ts › should wait for load when iframe attaches and detaches [pass] +page/page-goto.spec.ts › should work @smoke [pass] +page/page-goto.spec.ts › should work cross-process [pass] +page/page-goto.spec.ts › should work when navigating to 404 [pass] +page/page-goto.spec.ts › should work when navigating to data url [pass] +page/page-goto.spec.ts › should work when navigating to valid url [pass] +page/page-goto.spec.ts › should work when page calls history API in beforeunload [fail] +page/page-goto.spec.ts › should work with Cross-Origin-Opener-Policy [pass] +page/page-goto.spec.ts › should work with Cross-Origin-Opener-Policy after redirect [pass] +page/page-goto.spec.ts › should work with Cross-Origin-Opener-Policy and interception [pass] +page/page-goto.spec.ts › should work with anchor navigation [timeout] +page/page-goto.spec.ts › should work with cross-process that fails before committing [pass] +page/page-goto.spec.ts › should work with file URL [pass] +page/page-goto.spec.ts › should work with file URL with subframes [pass] +page/page-goto.spec.ts › should work with lazy loading iframes [fail] +page/page-goto.spec.ts › should work with redirects [pass] +page/page-goto.spec.ts › should work with self requesting page [pass] +page/page-goto.spec.ts › should work with subframes return 204 [pass] +page/page-goto.spec.ts › should work with subframes return 204 with domcontentloaded [pass] +page/page-history.spec.ts › goBack/goForward should work with bfcache-able pages [fail] +page/page-history.spec.ts › page.goBack during renderer-initiated navigation [fail] +page/page-history.spec.ts › page.goBack should work @smoke [pass] +page/page-history.spec.ts › page.goBack should work for file urls [fail] +page/page-history.spec.ts › page.goBack should work with HistoryAPI [fail] +page/page-history.spec.ts › page.goForward during renderer-initiated navigation [fail] +page/page-history.spec.ts › page.reload during renderer-initiated navigation [fail] +page/page-history.spec.ts › page.reload should not resolve with same-document navigation [fail] +page/page-history.spec.ts › page.reload should work [pass] +page/page-history.spec.ts › page.reload should work on a page with a hash [pass] +page/page-history.spec.ts › page.reload should work on a page with a hash at the end [pass] +page/page-history.spec.ts › page.reload should work with cross-origin redirect [pass] +page/page-history.spec.ts › page.reload should work with data url [pass] +page/page-history.spec.ts › page.reload should work with same origin redirect [pass] +page/page-history.spec.ts › regression test for issue 20791 [pass] +page/page-history.spec.ts › should reload proper page [timeout] +page/page-keyboard.spec.ts › insertText should only emit input event [fail] +page/page-keyboard.spec.ts › pressing Meta should not result in any text insertion on any platform [fail] +page/page-keyboard.spec.ts › should be able to prevent selectAll [pass] +page/page-keyboard.spec.ts › should dispatch a click event on a button when Enter gets pressed [fail] +page/page-keyboard.spec.ts › should dispatch a click event on a button when Space gets pressed [fail] +page/page-keyboard.spec.ts › should dispatch insertText after context menu was opened [pass] +page/page-keyboard.spec.ts › should expose keyIdentifier in webkit [unknown] +page/page-keyboard.spec.ts › should handle selectAll [pass] +page/page-keyboard.spec.ts › should have correct Keydown/Keyup order when pressing Escape key [pass] +page/page-keyboard.spec.ts › should move around the selection in a contenteditable [fail] +page/page-keyboard.spec.ts › should move to the start of the document [unknown] +page/page-keyboard.spec.ts › should move with the arrow keys [pass] +page/page-keyboard.spec.ts › should not type canceled events [pass] +page/page-keyboard.spec.ts › should press Enter [fail] +page/page-keyboard.spec.ts › should press plus [fail] +page/page-keyboard.spec.ts › should press shift plus [fail] +page/page-keyboard.spec.ts › should press the meta key [pass] +page/page-keyboard.spec.ts › should report multiple modifiers [fail] +page/page-keyboard.spec.ts › should report shiftKey [pass] +page/page-keyboard.spec.ts › should scroll with PageDown [pass] +page/page-keyboard.spec.ts › should send a character with ElementHandle.press [pass] +page/page-keyboard.spec.ts › should send a character with insertText [fail] +page/page-keyboard.spec.ts › should send proper codes while typing [pass] +page/page-keyboard.spec.ts › should send proper codes while typing with shift [pass] +page/page-keyboard.spec.ts › should shift raw codes [pass] +page/page-keyboard.spec.ts › should specify location [fail] +page/page-keyboard.spec.ts › should specify repeat property [pass] +page/page-keyboard.spec.ts › should support MacOS shortcuts [unknown] +page/page-keyboard.spec.ts › should support multiple plus-separated modifiers [pass] +page/page-keyboard.spec.ts › should support plus-separated modifiers [pass] +page/page-keyboard.spec.ts › should support simple copy-pasting [fail] +page/page-keyboard.spec.ts › should support simple cut-pasting [fail] +page/page-keyboard.spec.ts › should support undo-redo [fail] +page/page-keyboard.spec.ts › should throw on unknown keys [pass] +page/page-keyboard.spec.ts › should type after context menu was opened [pass] +page/page-keyboard.spec.ts › should type all kinds of characters [pass] +page/page-keyboard.spec.ts › should type emoji [pass] +page/page-keyboard.spec.ts › should type emoji into an iframe [pass] +page/page-keyboard.spec.ts › should type into a textarea @smoke [pass] +page/page-keyboard.spec.ts › should type repeatedly in contenteditable in shadow dom [fail] +page/page-keyboard.spec.ts › should type repeatedly in contenteditable in shadow dom with nested elements [fail] +page/page-keyboard.spec.ts › should type repeatedly in input in shadow dom [fail] +page/page-keyboard.spec.ts › should work after a cross origin navigation [pass] +page/page-keyboard.spec.ts › should work with keyboard events with empty.html [pass] +page/page-keyboard.spec.ts › type to non-focusable element should maintain old focus [fail] +page/page-leaks.spec.ts › click should not leak [fail] +page/page-leaks.spec.ts › expect should not leak [fail] +page/page-leaks.spec.ts › fill should not leak [fail] +page/page-leaks.spec.ts › waitFor should not leak [fail] +page/page-listeners.spec.ts › should not throw with ignoreErrors [pass] +page/page-listeners.spec.ts › should wait [pass] +page/page-listeners.spec.ts › wait should throw [pass] +page/page-mouse.spec.ts › down and up should generate click [pass] +page/page-mouse.spec.ts › should always round down [fail] +page/page-mouse.spec.ts › should click the document @smoke [pass] +page/page-mouse.spec.ts › should dblclick the div [fail] +page/page-mouse.spec.ts › should dispatch mouse move after context menu was opened [pass] +page/page-mouse.spec.ts › should not crash on mouse drag with any button [pass] +page/page-mouse.spec.ts › should pointerdown the div with a custom button [fail] +page/page-mouse.spec.ts › should report correct buttons property [pass] +page/page-mouse.spec.ts › should select the text with mouse [pass] +page/page-mouse.spec.ts › should set modifier keys on click [pass] +page/page-mouse.spec.ts › should trigger hover state [pass] +page/page-mouse.spec.ts › should trigger hover state on disabled button [pass] +page/page-mouse.spec.ts › should trigger hover state with removed window.Node [pass] +page/page-mouse.spec.ts › should tween mouse movement [pass] +page/page-navigation.spec.ts › should work with _blank target [pass] +page/page-navigation.spec.ts › should work with _blank target in form [fail] +page/page-navigation.spec.ts › should work with cross-process _blank target [pass] +page/page-network-idle.spec.ts › should navigate to empty page with networkidle [pass] +page/page-network-idle.spec.ts › should wait for networkidle from the child frame [pass] +page/page-network-idle.spec.ts › should wait for networkidle from the popup [fail] +page/page-network-idle.spec.ts › should wait for networkidle in setContent [fail] +page/page-network-idle.spec.ts › should wait for networkidle in setContent from the child frame [fail] +page/page-network-idle.spec.ts › should wait for networkidle in setContent with request from previous navigation [fail] +page/page-network-idle.spec.ts › should wait for networkidle in waitForNavigation [pass] +page/page-network-idle.spec.ts › should wait for networkidle to succeed navigation [pass] +page/page-network-idle.spec.ts › should wait for networkidle to succeed navigation with request from previous navigation [fail] +page/page-network-idle.spec.ts › should wait for networkidle when iframe attaches and detaches [fail] +page/page-network-idle.spec.ts › should wait for networkidle when navigating iframe [pass] +page/page-network-idle.spec.ts › should work after repeated navigations in the same page [pass] +page/page-network-request.spec.ts › page.reload return 304 status code [pass] +page/page-network-request.spec.ts › should get the same headers as the server [fail] +page/page-network-request.spec.ts › should get the same headers as the server CORS [fail] +page/page-network-request.spec.ts › should get |undefined| with postData() when there is no post data [pass] +page/page-network-request.spec.ts › should get |undefined| with postDataJSON() when there is no post data [pass] +page/page-network-request.spec.ts › should handle mixed-content blocked requests [unknown] +page/page-network-request.spec.ts › should not allow to access frame on popup main request [fail] +page/page-network-request.spec.ts › should not get preflight CORS requests when intercepting [fail] +page/page-network-request.spec.ts › should not return allHeaders() until they are available [fail] +page/page-network-request.spec.ts › should not work for a redirect and interception [pass] +page/page-network-request.spec.ts › should override post data content type [pass] +page/page-network-request.spec.ts › should parse the data if content-type is application/x-www-form-urlencoded [fail] +page/page-network-request.spec.ts › should parse the data if content-type is application/x-www-form-urlencoded; charset=UTF-8 [fail] +page/page-network-request.spec.ts › should parse the json post data [fail] +page/page-network-request.spec.ts › should report all cookies in one header [pass] +page/page-network-request.spec.ts › should report raw headers [fail] +page/page-network-request.spec.ts › should report raw response headers in redirects [pass] +page/page-network-request.spec.ts › should return event source [fail] +page/page-network-request.spec.ts › should return headers [pass] +page/page-network-request.spec.ts › should return multipart/form-data [fail] +page/page-network-request.spec.ts › should return navigation bit [pass] +page/page-network-request.spec.ts › should return navigation bit when navigating to image [pass] +page/page-network-request.spec.ts › should return postData [fail] +page/page-network-request.spec.ts › should work for a redirect [pass] +page/page-network-request.spec.ts › should work for fetch requests @smoke [pass] +page/page-network-request.spec.ts › should work for main frame navigation request [pass] +page/page-network-request.spec.ts › should work for subframe navigation request [pass] +page/page-network-request.spec.ts › should work with binary post data [fail] +page/page-network-request.spec.ts › should work with binary post data and interception [fail] +page/page-network-response.spec.ts › should behave the same way for headers and allHeaders [pass] +page/page-network-response.spec.ts › should bypass disk cache when context interception is enabled [fail] +page/page-network-response.spec.ts › should bypass disk cache when page interception is enabled [pass] +page/page-network-response.spec.ts › should provide a Response with a file URL [fail] +page/page-network-response.spec.ts › should reject response.finished if context closes [timeout] +page/page-network-response.spec.ts › should reject response.finished if page closes [pass] +page/page-network-response.spec.ts › should report all headers [fail] +page/page-network-response.spec.ts › should report if request was fromServiceWorker [fail] +page/page-network-response.spec.ts › should report multiple set-cookie headers [fail] +page/page-network-response.spec.ts › should return body [fail] +page/page-network-response.spec.ts › should return body for prefetch script [fail] +page/page-network-response.spec.ts › should return body with compression [fail] +page/page-network-response.spec.ts › should return headers after route.fulfill [pass] +page/page-network-response.spec.ts › should return json [fail] +page/page-network-response.spec.ts › should return multiple header value [fail] +page/page-network-response.spec.ts › should return set-cookie header after route.fulfill [pass] +page/page-network-response.spec.ts › should return status text [fail] +page/page-network-response.spec.ts › should return text [fail] +page/page-network-response.spec.ts › should return uncompressed text [fail] +page/page-network-response.spec.ts › should throw when requesting body of redirected response [pass] +page/page-network-response.spec.ts › should wait until response completes [fail] +page/page-network-response.spec.ts › should work @smoke [pass] +page/page-network-sizes.spec.ts › should handle redirects [pass] +page/page-network-sizes.spec.ts › should have correct responseBodySize for 404 with content [pass] +page/page-network-sizes.spec.ts › should have the correct responseBodySize [pass] +page/page-network-sizes.spec.ts › should have the correct responseBodySize for chunked request [fail] +page/page-network-sizes.spec.ts › should have the correct responseBodySize with gzip compression [pass] +page/page-network-sizes.spec.ts › should return sizes without hanging [fail] +page/page-network-sizes.spec.ts › should set bodySize and headersSize [pass] +page/page-network-sizes.spec.ts › should set bodySize to 0 if there was no body [pass] +page/page-network-sizes.spec.ts › should set bodySize to 0 when there was no response body [pass] +page/page-network-sizes.spec.ts › should set bodySize, headersSize, and transferSize [pass] +page/page-network-sizes.spec.ts › should throw for failed requests [pass] +page/page-network-sizes.spec.ts › should work with 200 status code [pass] +page/page-network-sizes.spec.ts › should work with 401 status code [pass] +page/page-network-sizes.spec.ts › should work with 404 status code [pass] +page/page-network-sizes.spec.ts › should work with 500 status code [pass] +page/page-object-count.spec.ts › should count objects [unknown] +page/page-request-continue.spec.ts › continue should delete headers on redirects [fail] +page/page-request-continue.spec.ts › continue should not change multipart/form-data body [pass] +page/page-request-continue.spec.ts › continue should propagate headers to redirects [fail] +page/page-request-continue.spec.ts › post data › should amend binary post data [fail] +page/page-request-continue.spec.ts › post data › should amend longer post data [pass] +page/page-request-continue.spec.ts › post data › should amend method and post data [pass] +page/page-request-continue.spec.ts › post data › should amend post data [pass] +page/page-request-continue.spec.ts › post data › should amend utf8 post data [fail] +page/page-request-continue.spec.ts › post data › should compute content-length from post data [pass] +page/page-request-continue.spec.ts › post data › should use content-type from original request [pass] +page/page-request-continue.spec.ts › redirected requests should report overridden headers [fail] +page/page-request-continue.spec.ts › should amend HTTP headers [pass] +page/page-request-continue.spec.ts › should amend method [pass] +page/page-request-continue.spec.ts › should amend method on main request [fail] +page/page-request-continue.spec.ts › should continue preload link requests [pass] +page/page-request-continue.spec.ts › should delete header with undefined value [pass] +page/page-request-continue.spec.ts › should delete the origin header [pass] +page/page-request-continue.spec.ts › should intercept css variable with background url [fail] +page/page-request-continue.spec.ts › should not allow changing protocol when overriding url [pass] +page/page-request-continue.spec.ts › should not throw if request was cancelled by the page [timeout] +page/page-request-continue.spec.ts › should not throw when continuing after page is closed [fail] +page/page-request-continue.spec.ts › should not throw when continuing while page is closing [fail] +page/page-request-continue.spec.ts › should override method along with url [timeout] +page/page-request-continue.spec.ts › should override request url [timeout] +page/page-request-continue.spec.ts › should work [fail] +page/page-request-continue.spec.ts › should work with Cross-Origin-Opener-Policy [pass] +page/page-request-fallback.spec.ts › post data › should amend binary post data [pass] +page/page-request-fallback.spec.ts › post data › should amend json post data [pass] +page/page-request-fallback.spec.ts › post data › should amend post data [pass] +page/page-request-fallback.spec.ts › should amend HTTP headers [pass] +page/page-request-fallback.spec.ts › should amend method [fail] +page/page-request-fallback.spec.ts › should chain once [fail] +page/page-request-fallback.spec.ts › should delete header with undefined value [pass] +page/page-request-fallback.spec.ts › should fall back [fail] +page/page-request-fallback.spec.ts › should fall back after exception [pass] +page/page-request-fallback.spec.ts › should fall back async [pass] +page/page-request-fallback.spec.ts › should not chain abort [pass] +page/page-request-fallback.spec.ts › should not chain fulfill [fail] +page/page-request-fallback.spec.ts › should override request url [fail] +page/page-request-fallback.spec.ts › should work [pass] +page/page-request-fulfill.spec.ts › headerValue should return set-cookie from intercepted response [pass] +page/page-request-fulfill.spec.ts › should allow mocking binary responses [fail] +page/page-request-fulfill.spec.ts › should allow mocking svg with charset [fail] +page/page-request-fulfill.spec.ts › should fetch original request and fulfill [fail] +page/page-request-fulfill.spec.ts › should fulfill json [pass] +page/page-request-fulfill.spec.ts › should fulfill preload link requests [fail] +page/page-request-fulfill.spec.ts › should fulfill with fetch response that has multiple set-cookie [fail] +page/page-request-fulfill.spec.ts › should fulfill with fetch result [fail] +page/page-request-fulfill.spec.ts › should fulfill with fetch result and overrides [fail] +page/page-request-fulfill.spec.ts › should fulfill with global fetch result [fail] +page/page-request-fulfill.spec.ts › should fulfill with gzip and readback [fail] +page/page-request-fulfill.spec.ts › should fulfill with har response [pass] +page/page-request-fulfill.spec.ts › should fulfill with multiple set-cookie [pass] +page/page-request-fulfill.spec.ts › should fulfill with unuassigned status codes [pass] +page/page-request-fulfill.spec.ts › should include the origin header [pass] +page/page-request-fulfill.spec.ts › should not go to the network for fulfilled requests body [fail] +page/page-request-fulfill.spec.ts › should not modify the headers sent to the server [fail] +page/page-request-fulfill.spec.ts › should not throw if request was cancelled by the page [fail] +page/page-request-fulfill.spec.ts › should stringify intercepted request response headers [pass] +page/page-request-fulfill.spec.ts › should work [pass] +page/page-request-fulfill.spec.ts › should work with buffer as body [fail] +page/page-request-fulfill.spec.ts › should work with file path [pass] +page/page-request-fulfill.spec.ts › should work with status code 422 [pass] +page/page-request-intercept.spec.ts › request.postData is not null when fetching FormData with a Blob [fail] +page/page-request-intercept.spec.ts › should fulfill intercepted response [pass] +page/page-request-intercept.spec.ts › should fulfill intercepted response using alias [pass] +page/page-request-intercept.spec.ts › should fulfill popup main request using alias [fail] +page/page-request-intercept.spec.ts › should fulfill response with empty body [fail] +page/page-request-intercept.spec.ts › should fulfill with any response [fail] +page/page-request-intercept.spec.ts › should give access to the intercepted response [pass] +page/page-request-intercept.spec.ts › should give access to the intercepted response body [fail] +page/page-request-intercept.spec.ts › should intercept multipart/form-data request body [unknown] +page/page-request-intercept.spec.ts › should intercept with post data override [pass] +page/page-request-intercept.spec.ts › should intercept with url override [fail] +page/page-request-intercept.spec.ts › should not follow redirects when maxRedirects is set to 0 in route.fetch [fail] +page/page-request-intercept.spec.ts › should override with defaults when intercepted response not provided [fail] +page/page-request-intercept.spec.ts › should support fulfill after intercept [fail] +page/page-request-intercept.spec.ts › should support timeout option in route.fetch [pass] +page/page-route.spec.ts › route.abort should throw if called twice [pass] +page/page-route.spec.ts › route.continue should throw if called twice [pass] +page/page-route.spec.ts › route.fallback should throw if called twice [pass] +page/page-route.spec.ts › route.fulfill should throw if called twice [fail] +page/page-route.spec.ts › should add Access-Control-Allow-Origin by default when fulfill [fail] +page/page-route.spec.ts › should allow null origin for about:blank [fail] +page/page-route.spec.ts › should be able to fetch dataURL and not fire dataURL requests [fail] +page/page-route.spec.ts › should be able to remove headers [pass] +page/page-route.spec.ts › should be abortable [pass] +page/page-route.spec.ts › should be abortable with custom error codes [fail] +page/page-route.spec.ts › should chain fallback w/ dynamic URL [fail] +page/page-route.spec.ts › should contain raw request header [pass] +page/page-route.spec.ts › should contain raw response header [pass] +page/page-route.spec.ts › should contain raw response header after fulfill [pass] +page/page-route.spec.ts › should contain referer header [fail] +page/page-route.spec.ts › should fail navigation when aborting main resource [fail] +page/page-route.spec.ts › should fulfill with redirect status [fail] +page/page-route.spec.ts › should intercept @smoke [fail] +page/page-route.spec.ts › should intercept main resource during cross-process navigation [pass] +page/page-route.spec.ts › should intercept when postData is more than 1MB [fail] +page/page-route.spec.ts › should navigate to URL with hash and and fire requests without hash [pass] +page/page-route.spec.ts › should navigate to dataURL and not fire dataURL requests [pass] +page/page-route.spec.ts › should not auto-intercept non-preflight OPTIONS [fail] +page/page-route.spec.ts › should not fulfill with redirect status [unknown] +page/page-route.spec.ts › should not throw "Invalid Interception Id" if the request was cancelled [fail] +page/page-route.spec.ts › should not throw if request was cancelled by the page [timeout] +page/page-route.spec.ts › should not work with redirects [fail] +page/page-route.spec.ts › should override cookie header [fail] +page/page-route.spec.ts › should pause intercepted XHR until continue [fail] +page/page-route.spec.ts › should pause intercepted fetch request until continue [pass] +page/page-route.spec.ts › should properly return navigation response when URL has cookies [pass] +page/page-route.spec.ts › should reject cors with disallowed credentials [fail] +page/page-route.spec.ts › should respect cors overrides [fail] +page/page-route.spec.ts › should send referer [fail] +page/page-route.spec.ts › should show custom HTTP headers [fail] +page/page-route.spec.ts › should support ? in glob pattern [fail] +page/page-route.spec.ts › should support async handler w/ times [pass] +page/page-route.spec.ts › should support cors for different methods [fail] +page/page-route.spec.ts › should support cors with GET [pass] +page/page-route.spec.ts › should support cors with POST [fail] +page/page-route.spec.ts › should support cors with credentials [fail] +page/page-route.spec.ts › should support the times parameter with route matching [fail] +page/page-route.spec.ts › should unroute [pass] +page/page-route.spec.ts › should work if handler with times parameter was removed from another handler [pass] +page/page-route.spec.ts › should work when POST is redirected with 302 [fail] +page/page-route.spec.ts › should work when header manipulation headers with redirect [fail] +page/page-route.spec.ts › should work with badly encoded server [pass] +page/page-route.spec.ts › should work with custom referer headers [fail] +page/page-route.spec.ts › should work with encoded server [fail] +page/page-route.spec.ts › should work with encoded server - 2 [fail] +page/page-route.spec.ts › should work with equal requests [fail] +page/page-route.spec.ts › should work with redirect inside sync XHR [pass] +page/page-route.spec.ts › should work with redirects for subresources [fail] +page/page-screenshot.spec.ts › page screenshot animations › should capture screenshots after layoutchanges in transitionend event [pass] +page/page-screenshot.spec.ts › page screenshot animations › should fire transitionend for finite transitions [pass] +page/page-screenshot.spec.ts › page screenshot animations › should not capture css animations in shadow DOM [fail] +page/page-screenshot.spec.ts › page screenshot animations › should not capture infinite css animation [fail] +page/page-screenshot.spec.ts › page screenshot animations › should not capture infinite web animations [fail] +page/page-screenshot.spec.ts › page screenshot animations › should not capture pseudo element css animation [fail] +page/page-screenshot.spec.ts › page screenshot animations › should not change animation with playbackRate equal to 0 [pass] +page/page-screenshot.spec.ts › page screenshot animations › should resume infinite animations [pass] +page/page-screenshot.spec.ts › page screenshot animations › should stop animations that happen right before screenshot [pass] +page/page-screenshot.spec.ts › page screenshot animations › should trigger particular events for INfinite css animation [pass] +page/page-screenshot.spec.ts › page screenshot animations › should trigger particular events for css transitions [pass] +page/page-screenshot.spec.ts › page screenshot animations › should trigger particular events for finite css animation [pass] +page/page-screenshot.spec.ts › page screenshot animations › should wait for fonts to load [fail] +page/page-screenshot.spec.ts › page screenshot should capture css transform [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should hide elements based on attr [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should mask in parallel [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should mask inside iframe [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should mask multiple elements [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should remove elements based on attr [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should remove mask after screenshot [pass] +page/page-screenshot.spec.ts › page screenshot › mask option › should work [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should work when mask color is not pink #F0F [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should work when subframe has stalled navigation [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should work when subframe used document.open after a weird url [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should work with elementhandle [fail] +page/page-screenshot.spec.ts › page screenshot › mask option › should work with locator [fail] +page/page-screenshot.spec.ts › page screenshot › path option should create subdirectories [pass] +page/page-screenshot.spec.ts › page screenshot › path option should detect jpeg [fail] +page/page-screenshot.spec.ts › page screenshot › path option should throw for unsupported mime type [pass] +page/page-screenshot.spec.ts › page screenshot › path option should work [fail] +page/page-screenshot.spec.ts › page screenshot › quality option should throw for png [pass] +page/page-screenshot.spec.ts › page screenshot › should allow transparency [fail] +page/page-screenshot.spec.ts › page screenshot › should capture blinking caret if explicitly asked for [fail] +page/page-screenshot.spec.ts › page screenshot › should capture blinking caret in shadow dom [pass] +page/page-screenshot.spec.ts › page screenshot › should capture canvas changes [fail] +page/page-screenshot.spec.ts › page screenshot › should clip elements to the viewport [fail] +page/page-screenshot.spec.ts › page screenshot › should clip rect [fail] +page/page-screenshot.spec.ts › page screenshot › should clip rect with fullPage [fail] +page/page-screenshot.spec.ts › page screenshot › should not capture blinking caret by default [fail] +page/page-screenshot.spec.ts › page screenshot › should not issue resize event [pass] +page/page-screenshot.spec.ts › page screenshot › should prefer type over extension [fail] +page/page-screenshot.spec.ts › page screenshot › should render white background on jpeg file [fail] +page/page-screenshot.spec.ts › page screenshot › should restore viewport after fullPage screenshot [fail] +page/page-screenshot.spec.ts › page screenshot › should run in parallel [fail] +page/page-screenshot.spec.ts › page screenshot › should take fullPage screenshots [fail] +page/page-screenshot.spec.ts › page screenshot › should take fullPage screenshots and mask elements outside of it [fail] +page/page-screenshot.spec.ts › page screenshot › should take fullPage screenshots during navigation [pass] +page/page-screenshot.spec.ts › page screenshot › should throw on clip outside the viewport [pass] +page/page-screenshot.spec.ts › page screenshot › should work @smoke [fail] +page/page-screenshot.spec.ts › page screenshot › should work for canvas [fail] +page/page-screenshot.spec.ts › page screenshot › should work for translateZ [fail] +page/page-screenshot.spec.ts › page screenshot › should work for webgl [fail] +page/page-screenshot.spec.ts › page screenshot › should work while navigating [fail] +page/page-screenshot.spec.ts › page screenshot › should work with Array deleted [fail] +page/page-screenshot.spec.ts › page screenshot › should work with iframe in shadow [fail] +page/page-screenshot.spec.ts › page screenshot › should work with odd clip size on Retina displays [fail] +page/page-screenshot.spec.ts › page screenshot › zero quality option should throw for png [fail] +page/page-screenshot.spec.ts › should capture css box-shadow [fail] +page/page-screenshot.spec.ts › should throw if screenshot size is too large [fail] +page/page-select-option.spec.ts › input event.composed should be true and cross shadow dom boundary [fail] +page/page-select-option.spec.ts › should deselect all options when passed no values for a multiple select [pass] +page/page-select-option.spec.ts › should deselect all options when passed no values for a select without multiple [pass] +page/page-select-option.spec.ts › should fall back to selecting by label [pass] +page/page-select-option.spec.ts › should not allow null items [pass] +page/page-select-option.spec.ts › should not select single option when some attributes do not match [pass] +page/page-select-option.spec.ts › should not throw when select causes navigation [pass] +page/page-select-option.spec.ts › should respect event bubbling [pass] +page/page-select-option.spec.ts › should return [] on no matched values [pass] +page/page-select-option.spec.ts › should return [] on no values [pass] +page/page-select-option.spec.ts › should return an array of matched values [fail] +page/page-select-option.spec.ts › should return an array of one element when multiple is not set [pass] +page/page-select-option.spec.ts › should select multiple options [pass] +page/page-select-option.spec.ts › should select multiple options with attributes [pass] +page/page-select-option.spec.ts › should select only first option [pass] +page/page-select-option.spec.ts › should select single option @smoke [pass] +page/page-select-option.spec.ts › should select single option by handle [pass] +page/page-select-option.spec.ts › should select single option by index [pass] +page/page-select-option.spec.ts › should select single option by label [pass] +page/page-select-option.spec.ts › should select single option by multiple attributes [pass] +page/page-select-option.spec.ts › should select single option by value [pass] +page/page-select-option.spec.ts › should throw if passed wrong types [fail] +page/page-select-option.spec.ts › should throw when element is not a <select> [pass] +page/page-select-option.spec.ts › should unselect with null [pass] +page/page-select-option.spec.ts › should wait for multiple options to be present [pass] +page/page-select-option.spec.ts › should wait for option index to be present [pass] +page/page-select-option.spec.ts › should wait for option to be present [pass] +page/page-select-option.spec.ts › should work when re-defining top-level Event class [pass] +page/page-set-content.spec.ts › content() should throw nice error during navigation [fail] +page/page-set-content.spec.ts › should await resources to load [fail] +page/page-set-content.spec.ts › should respect default navigation timeout [pass] +page/page-set-content.spec.ts › should respect timeout [pass] +page/page-set-content.spec.ts › should return empty content there is no iframe src [unknown] +page/page-set-content.spec.ts › should work @smoke [fail] +page/page-set-content.spec.ts › should work fast enough [fail] +page/page-set-content.spec.ts › should work with HTML 4 doctype [fail] +page/page-set-content.spec.ts › should work with accents [fail] +page/page-set-content.spec.ts › should work with commit [fail] +page/page-set-content.spec.ts › should work with doctype [fail] +page/page-set-content.spec.ts › should work with domcontentloaded [fail] +page/page-set-content.spec.ts › should work with emojis [fail] +page/page-set-content.spec.ts › should work with newline [fail] +page/page-set-content.spec.ts › should work with tricky content [fail] +page/page-set-extra-http-headers.spec.ts › should not duplicate referer header [fail] +page/page-set-extra-http-headers.spec.ts › should throw for non-string header values [pass] +page/page-set-extra-http-headers.spec.ts › should work @smoke [fail] +page/page-set-extra-http-headers.spec.ts › should work with extra headers from browser context [fail] +page/page-set-extra-http-headers.spec.ts › should work with redirects [fail] +page/page-set-input-files.spec.ts › input event.composed should be true and cross shadow dom boundary [fail] +page/page-set-input-files.spec.ts › input should trigger events when files changed second time [fail] +page/page-set-input-files.spec.ts › should accept single file [fail] +page/page-set-input-files.spec.ts › should be able to read selected file [fail] +page/page-set-input-files.spec.ts › should be able to reset selected files with empty file list [fail] +page/page-set-input-files.spec.ts › should detect mime type [fail] +page/page-set-input-files.spec.ts › should emit event addListener/removeListener [fail] +page/page-set-input-files.spec.ts › should emit event after navigation [fail] +page/page-set-input-files.spec.ts › should emit event for iframe [fail] +page/page-set-input-files.spec.ts › should emit event on/off [fail] +page/page-set-input-files.spec.ts › should emit event once [fail] +page/page-set-input-files.spec.ts › should emit event via prepend [fail] +page/page-set-input-files.spec.ts › should emit input and change events [fail] +page/page-set-input-files.spec.ts › should not accept multiple files for single-file input [fail] +page/page-set-input-files.spec.ts › should not throw when filechooser belongs to iframe [fail] +page/page-set-input-files.spec.ts › should not throw when frame is detached immediately [fail] +page/page-set-input-files.spec.ts › should not trim big uploaded files [pass] +page/page-set-input-files.spec.ts › should preserve lastModified timestamp [fail] +page/page-set-input-files.spec.ts › should prioritize exact timeout over default timeout [pass] +page/page-set-input-files.spec.ts › should respect default timeout when there is no custom timeout [pass] +page/page-set-input-files.spec.ts › should respect timeout [pass] +page/page-set-input-files.spec.ts › should return the same file chooser when there are many watchdogs simultaneously [fail] +page/page-set-input-files.spec.ts › should set from memory [fail] +page/page-set-input-files.spec.ts › should throw an error if the file does not exist [fail] +page/page-set-input-files.spec.ts › should throw if a directory and files are passed [fail] +page/page-set-input-files.spec.ts › should throw when uploading a file in a directory upload input [fail] +page/page-set-input-files.spec.ts › should throw when uploading a folder in a normal file upload input [fail] +page/page-set-input-files.spec.ts › should trigger listener added before navigation [fail] +page/page-set-input-files.spec.ts › should upload a file after popup [timeout] +page/page-set-input-files.spec.ts › should upload a folder [fail] +page/page-set-input-files.spec.ts › should upload a folder and throw for multiple directories [fail] +page/page-set-input-files.spec.ts › should upload large file [fail] +page/page-set-input-files.spec.ts › should upload large file with relative path [fail] +page/page-set-input-files.spec.ts › should upload multiple large files [fail] +page/page-set-input-files.spec.ts › should upload the file [fail] +page/page-set-input-files.spec.ts › should upload the file with spaces in name [fail] +page/page-set-input-files.spec.ts › should work @smoke [fail] +page/page-set-input-files.spec.ts › should work for "multiple" [fail] +page/page-set-input-files.spec.ts › should work for "webkitdirectory" [fail] +page/page-set-input-files.spec.ts › should work for single file pick [fail] +page/page-set-input-files.spec.ts › should work when file input is attached to DOM [fail] +page/page-set-input-files.spec.ts › should work when file input is not attached to DOM [fail] +page/page-set-input-files.spec.ts › should work with CSP [fail] +page/page-set-input-files.spec.ts › should work with no timeout [fail] +page/page-strict.spec.ts › should escape class names [fail] +page/page-strict.spec.ts › should escape tag names [fail] +page/page-strict.spec.ts › should fail page.$ in strict mode [fail] +page/page-strict.spec.ts › should fail page.dispatchEvent in strict mode [fail] +page/page-strict.spec.ts › should fail page.fill in strict mode [fail] +page/page-strict.spec.ts › should fail page.getAttribute in strict mode [fail] +page/page-strict.spec.ts › should fail page.textContent in strict mode [fail] +page/page-strict.spec.ts › should fail page.waitForSelector in strict mode [fail] +page/page-strict.spec.ts › should properly format :nth-child() in strict mode message [fail] +page/page-wait-for-function.spec.ts › should accept ElementHandle arguments [fail] +page/page-wait-for-function.spec.ts › should accept a string [pass] +page/page-wait-for-function.spec.ts › should avoid side effects after timeout [pass] +page/page-wait-for-function.spec.ts › should disable timeout when its set to 0 [pass] +page/page-wait-for-function.spec.ts › should fail with ReferenceError on wrong page [timeout] +page/page-wait-for-function.spec.ts › should fail with predicate throwing on first call [timeout] +page/page-wait-for-function.spec.ts › should fail with predicate throwing sometimes [fail] +page/page-wait-for-function.spec.ts › should not be called after finishing successfully [pass] +page/page-wait-for-function.spec.ts › should not be called after finishing unsuccessfully [timeout] +page/page-wait-for-function.spec.ts › should poll on interval [pass] +page/page-wait-for-function.spec.ts › should poll on raf [pass] +page/page-wait-for-function.spec.ts › should respect default timeout [fail] +page/page-wait-for-function.spec.ts › should respect timeout [pass] +page/page-wait-for-function.spec.ts › should return the success value as a JSHandle [pass] +page/page-wait-for-function.spec.ts › should return the window as a success value [pass] +page/page-wait-for-function.spec.ts › should survive cross-process navigation [pass] +page/page-wait-for-function.spec.ts › should survive navigations [pass] +page/page-wait-for-function.spec.ts › should throw negative polling interval [pass] +page/page-wait-for-function.spec.ts › should throw on bad polling value [pass] +page/page-wait-for-function.spec.ts › should throw on polling:mutation [pass] +page/page-wait-for-function.spec.ts › should throw when frame is detached [pass] +page/page-wait-for-function.spec.ts › should timeout [pass] +page/page-wait-for-function.spec.ts › should wait for predicate with arguments [pass] +page/page-wait-for-function.spec.ts › should work when resolved right before execution context disposal [pass] +page/page-wait-for-function.spec.ts › should work with multiline body [pass] +page/page-wait-for-function.spec.ts › should work with strict CSP policy [fail] +page/page-wait-for-load-state.spec.ts › should pick up ongoing navigation [pass] +page/page-wait-for-load-state.spec.ts › should resolve after popup load [pass] +page/page-wait-for-load-state.spec.ts › should resolve immediately if load state matches [pass] +page/page-wait-for-load-state.spec.ts › should resolve immediately if loaded [fail] +page/page-wait-for-load-state.spec.ts › should respect timeout [pass] +page/page-wait-for-load-state.spec.ts › should throw for bad state [pass] +page/page-wait-for-load-state.spec.ts › should wait for load state of about:blank popup [timeout] +page/page-wait-for-load-state.spec.ts › should wait for load state of about:blank popup with noopener [pass] +page/page-wait-for-load-state.spec.ts › should wait for load state of empty url popup [timeout] +page/page-wait-for-load-state.spec.ts › should wait for load state of newPage [pass] +page/page-wait-for-load-state.spec.ts › should wait for load state of popup with network url [pass] +page/page-wait-for-load-state.spec.ts › should wait for load state of popup with network url and noopener [pass] +page/page-wait-for-load-state.spec.ts › should work for frame [pass] +page/page-wait-for-load-state.spec.ts › should work with broken blob-url iframe [fail] +page/page-wait-for-load-state.spec.ts › should work with broken data-url iframe [fail] +page/page-wait-for-load-state.spec.ts › should work with clicking target=_blank [fail] +page/page-wait-for-load-state.spec.ts › should work with javascript: iframe [fail] +page/page-wait-for-load-state.spec.ts › should work with pages that have loaded before being connected to [timeout] +page/page-wait-for-navigation.spec.ts › should fail when frame detaches [pass] +page/page-wait-for-navigation.spec.ts › should respect timeout [pass] +page/page-wait-for-navigation.spec.ts › should work [fail] +page/page-wait-for-navigation.spec.ts › should work for cross-process navigations [fail] +page/page-wait-for-navigation.spec.ts › should work on frame [pass] +page/page-wait-for-navigation.spec.ts › should work when subframe issues window.stop() [pass] +page/page-wait-for-navigation.spec.ts › should work with DOM history.back()/history.forward() [fail] +page/page-wait-for-navigation.spec.ts › should work with both domcontentloaded and load [pass] +page/page-wait-for-navigation.spec.ts › should work with clicking on anchor links [fail] +page/page-wait-for-navigation.spec.ts › should work with clicking on links which do not commit navigation [fail] +page/page-wait-for-navigation.spec.ts › should work with commit [pass] +page/page-wait-for-navigation.spec.ts › should work with history.pushState() [fail] +page/page-wait-for-navigation.spec.ts › should work with history.replaceState() [fail] +page/page-wait-for-navigation.spec.ts › should work with url match [pass] +page/page-wait-for-navigation.spec.ts › should work with url match for same document navigations [timeout] +page/page-wait-for-request.spec.ts › should log the url [pass] +page/page-wait-for-request.spec.ts › should respect default timeout [pass] +page/page-wait-for-request.spec.ts › should respect timeout [pass] +page/page-wait-for-request.spec.ts › should work [pass] +page/page-wait-for-request.spec.ts › should work with no timeout [pass] +page/page-wait-for-request.spec.ts › should work with predicate [pass] +page/page-wait-for-request.spec.ts › should work with url match [pass] +page/page-wait-for-request.spec.ts › should work with url match regular expression from a different context [pass] +page/page-wait-for-response.spec.ts › should log the url [pass] +page/page-wait-for-response.spec.ts › should respect default timeout [pass] +page/page-wait-for-response.spec.ts › should respect timeout [pass] +page/page-wait-for-response.spec.ts › should work [pass] +page/page-wait-for-response.spec.ts › should work with async predicate [fail] +page/page-wait-for-response.spec.ts › should work with no timeout [pass] +page/page-wait-for-response.spec.ts › should work with predicate [pass] +page/page-wait-for-response.spec.ts › should work with re-rendered cached IMG elements [fail] +page/page-wait-for-response.spec.ts › sync predicate should be only called once [pass] +page/page-wait-for-selector-1.spec.ts › elementHandle.waitForSelector should immediately resolve if node exists [fail] +page/page-wait-for-selector-1.spec.ts › elementHandle.waitForSelector should throw on navigation [fail] +page/page-wait-for-selector-1.spec.ts › elementHandle.waitForSelector should timeout [fail] +page/page-wait-for-selector-1.spec.ts › elementHandle.waitForSelector should wait [fail] +page/page-wait-for-selector-1.spec.ts › page.waitForSelector is shortcut for main frame [fail] +page/page-wait-for-selector-1.spec.ts › should immediately resolve promise if node exists [pass] +page/page-wait-for-selector-1.spec.ts › should report logs when the selector resolves to multiple elements [fail] +page/page-wait-for-selector-1.spec.ts › should report logs while waiting for hidden [pass] +page/page-wait-for-selector-1.spec.ts › should report logs while waiting for visible [pass] +page/page-wait-for-selector-1.spec.ts › should resolve promise when node is added [fail] +page/page-wait-for-selector-1.spec.ts › should resolve promise when node is added in shadow dom [pass] +page/page-wait-for-selector-1.spec.ts › should run in specified frame [fail] +page/page-wait-for-selector-1.spec.ts › should throw on waitFor [pass] +page/page-wait-for-selector-1.spec.ts › should throw when frame is detached [pass] +page/page-wait-for-selector-1.spec.ts › should tolerate waitFor=visible [pass] +page/page-wait-for-selector-1.spec.ts › should work when node is added through innerHTML [pass] +page/page-wait-for-selector-1.spec.ts › should work with removed MutationObserver [fail] +page/page-wait-for-selector-2.spec.ts › hidden should wait for display: none [fail] +page/page-wait-for-selector-2.spec.ts › hidden should wait for hidden [fail] +page/page-wait-for-selector-2.spec.ts › hidden should wait for removal [fail] +page/page-wait-for-selector-2.spec.ts › should allow you to select an element with single slash xpath [fail] +page/page-wait-for-selector-2.spec.ts › should consider outside of viewport visible [fail] +page/page-wait-for-selector-2.spec.ts › should correctly handle hidden shadow host [fail] +page/page-wait-for-selector-2.spec.ts › should fail when navigating while on handle [fail] +page/page-wait-for-selector-2.spec.ts › should have an error message specifically for awaiting an element to be hidden [fail] +page/page-wait-for-selector-2.spec.ts › should have correct stack trace for timeout [pass] +page/page-wait-for-selector-2.spec.ts › should not consider visible when zero-sized [fail] +page/page-wait-for-selector-2.spec.ts › should respect timeout [pass] +page/page-wait-for-selector-2.spec.ts › should respect timeout xpath [pass] +page/page-wait-for-selector-2.spec.ts › should respond to node attribute mutation [fail] +page/page-wait-for-selector-2.spec.ts › should return null if waiting to hide non-existing element [pass] +page/page-wait-for-selector-2.spec.ts › should return the element handle [fail] +page/page-wait-for-selector-2.spec.ts › should return the element handle xpath [fail] +page/page-wait-for-selector-2.spec.ts › should run in specified frame xpath [fail] +page/page-wait-for-selector-2.spec.ts › should support >> selector syntax [fail] +page/page-wait-for-selector-2.spec.ts › should support some fancy xpath [fail] +page/page-wait-for-selector-2.spec.ts › should survive cross-process navigation [pass] +page/page-wait-for-selector-2.spec.ts › should throw for false state option [fail] +page/page-wait-for-selector-2.spec.ts › should throw for true state option [fail] +page/page-wait-for-selector-2.spec.ts › should throw for unknown state option [fail] +page/page-wait-for-selector-2.spec.ts › should throw for visibility option [fail] +page/page-wait-for-selector-2.spec.ts › should throw when frame is detached xpath [pass] +page/page-wait-for-selector-2.spec.ts › should wait for detached [fail] +page/page-wait-for-selector-2.spec.ts › should wait for detached if already detached [fail] +page/page-wait-for-selector-2.spec.ts › should wait for visible [fail] +page/page-wait-for-selector-2.spec.ts › should wait for visible recursively [fail] +page/page-wait-for-selector-2.spec.ts › should work when navigating before node adoption [fail] +page/page-wait-for-url.spec.ts › should respect timeout [pass] +page/page-wait-for-url.spec.ts › should work [pass] +page/page-wait-for-url.spec.ts › should work on frame [pass] +page/page-wait-for-url.spec.ts › should work with DOM history.back()/history.forward() [fail] +page/page-wait-for-url.spec.ts › should work with both domcontentloaded and load [pass] +page/page-wait-for-url.spec.ts › should work with clicking on anchor links [fail] +page/page-wait-for-url.spec.ts › should work with commit [pass] +page/page-wait-for-url.spec.ts › should work with commit and about:blank [pass] +page/page-wait-for-url.spec.ts › should work with history.pushState() [fail] +page/page-wait-for-url.spec.ts › should work with history.replaceState() [fail] +page/page-wait-for-url.spec.ts › should work with url match for same document navigations [timeout] +page/queryselector.spec.ts › $$ should work with bogus Array.from [fail] +page/queryselector.spec.ts › should auto-detect css selector [fail] +page/queryselector.spec.ts › should auto-detect text selector [fail] +page/queryselector.spec.ts › should auto-detect xpath selector [fail] +page/queryselector.spec.ts › should auto-detect xpath selector starting with .. [fail] +page/queryselector.spec.ts › should auto-detect xpath selector with starting parenthesis [fail] +page/queryselector.spec.ts › should query existing element with css selector @smoke [fail] +page/queryselector.spec.ts › should query existing element with text selector [fail] +page/queryselector.spec.ts › should query existing element with xpath selector [fail] +page/queryselector.spec.ts › should query existing elements [fail] +page/queryselector.spec.ts › should return empty array if nothing is found [fail] +page/queryselector.spec.ts › should return null for non-existing element [pass] +page/queryselector.spec.ts › should support >> syntax [fail] +page/queryselector.spec.ts › should throw for non-string selector [pass] +page/queryselector.spec.ts › xpath should query existing element [fail] +page/queryselector.spec.ts › xpath should return empty array for non-existing element [fail] +page/queryselector.spec.ts › xpath should return multiple elements [fail] +page/retarget.spec.ts › check retargeting [fail] +page/retarget.spec.ts › direct actions retargeting [fail] +page/retarget.spec.ts › editable retargeting [fail] +page/retarget.spec.ts › element state checks should work as expected for label with zero-sized input [fail] +page/retarget.spec.ts › enabled/disabled retargeting [fail] +page/retarget.spec.ts › input value retargeting [fail] +page/retarget.spec.ts › select options retargeting [fail] +page/retarget.spec.ts › selection retargeting [fail] +page/retarget.spec.ts › setInputFiles should work with label [fail] +page/retarget.spec.ts › should check the box outside shadow dom label [fail] +page/retarget.spec.ts › should not retarget anchor into parent label [fail] +page/retarget.spec.ts › should wait for enclosing button with a disabled fieldset [fail] +page/retarget.spec.ts › should wait for enclosing disabled button [fail] +page/retarget.spec.ts › should wait for enclosing enabled button [fail] +page/retarget.spec.ts › visible/hidden retargeting [fail] +page/selectors-css.spec.ts › css on the handle should be relative [fail] +page/selectors-css.spec.ts › should absolutize relative selectors [fail] +page/selectors-css.spec.ts › should keep dom order with comma separated list [fail] +page/selectors-css.spec.ts › should not match root after >> [fail] +page/selectors-css.spec.ts › should return multiple captures for the same node [fail] +page/selectors-css.spec.ts › should return multiple captures when going up the hierarchy [fail] +page/selectors-css.spec.ts › should work for open shadow roots [fail] +page/selectors-css.spec.ts › should work with * [fail] +page/selectors-css.spec.ts › should work with + [fail] +page/selectors-css.spec.ts › should work with :has [fail] +page/selectors-css.spec.ts › should work with :is [pass] +page/selectors-css.spec.ts › should work with :not [pass] +page/selectors-css.spec.ts › should work with :nth-child [pass] +page/selectors-css.spec.ts › should work with :nth-child(of) notation with nested functions [fail] +page/selectors-css.spec.ts › should work with :scope [fail] +page/selectors-css.spec.ts › should work with :scope and class [fail] +page/selectors-css.spec.ts › should work with > combinator and spaces [fail] +page/selectors-css.spec.ts › should work with attribute selectors [fail] +page/selectors-css.spec.ts › should work with comma inside text [fail] +page/selectors-css.spec.ts › should work with comma separated list [pass] +page/selectors-css.spec.ts › should work with comma separated list in various positions [fail] +page/selectors-css.spec.ts › should work with large DOM @smoke [pass] +page/selectors-css.spec.ts › should work with numerical id [fail] +page/selectors-css.spec.ts › should work with spaces in :nth-child and :not [pass] +page/selectors-css.spec.ts › should work with wrong-case id [fail] +page/selectors-css.spec.ts › should work with ~ [fail] +page/selectors-frame.spec.ts › $ should not wait for frame [pass] +page/selectors-frame.spec.ts › $$ should not wait for frame [pass] +page/selectors-frame.spec.ts › $$eval should throw for missing frame [pass] +page/selectors-frame.spec.ts › $eval should throw for missing frame [pass] +page/selectors-frame.spec.ts › click should survive frame reattach [pass] +page/selectors-frame.spec.ts › click should survive iframe navigation [pass] +page/selectors-frame.spec.ts › click should survive navigation [fail] +page/selectors-frame.spec.ts › should capture after the enter-frame [pass] +page/selectors-frame.spec.ts › should click in lazy iframe [pass] +page/selectors-frame.spec.ts › should fail if element removed while waiting on element handle [fail] +page/selectors-frame.spec.ts › should non work for non-frame [fail] +page/selectors-frame.spec.ts › should not allow capturing before enter-frame [pass] +page/selectors-frame.spec.ts › should not allow dangling enter-frame [pass] +page/selectors-frame.spec.ts › should not allow leading enter-frame [pass] +page/selectors-frame.spec.ts › should work for $ and $$ [fail] +page/selectors-frame.spec.ts › should work for $ and $$ (handle) [fail] +page/selectors-frame.spec.ts › should work for $$eval [pass] +page/selectors-frame.spec.ts › should work for $$eval (handle) [pass] +page/selectors-frame.spec.ts › should work for $eval [pass] +page/selectors-frame.spec.ts › should work for $eval (handle) [pass] +page/selectors-frame.spec.ts › should work for iframe (handle) [pass] +page/selectors-frame.spec.ts › should work for iframe @smoke [pass] +page/selectors-frame.spec.ts › should work for nested iframe [pass] +page/selectors-frame.spec.ts › should work for nested iframe (handle) [pass] +page/selectors-frame.spec.ts › waitFor should survive frame reattach [pass] +page/selectors-frame.spec.ts › waitForSelector should survive frame reattach (handle) [pass] +page/selectors-frame.spec.ts › waitForSelector should survive iframe navigation (handle) [pass] +page/selectors-get-by.spec.ts › getBy escaping [fail] +page/selectors-get-by.spec.ts › getByAltText should work [fail] +page/selectors-get-by.spec.ts › getByLabel should ignore empty aria-label [fail] +page/selectors-get-by.spec.ts › getByLabel should prioritize aria-labelledby over aria-label [fail] +page/selectors-get-by.spec.ts › getByLabel should prioritize aria-labelledby over native label [fail] +page/selectors-get-by.spec.ts › getByLabel should work [fail] +page/selectors-get-by.spec.ts › getByLabel should work with ancestor label and for [fail] +page/selectors-get-by.spec.ts › getByLabel should work with ancestor label and multiple controls [fail] +page/selectors-get-by.spec.ts › getByLabel should work with aria-label [fail] +page/selectors-get-by.spec.ts › getByLabel should work with aria-labelledby [fail] +page/selectors-get-by.spec.ts › getByLabel should work with multiply-labelled input [fail] +page/selectors-get-by.spec.ts › getByLabel should work with nested elements [fail] +page/selectors-get-by.spec.ts › getByPlaceholder should work [fail] +page/selectors-get-by.spec.ts › getByRole escaping [fail] +page/selectors-get-by.spec.ts › getByTestId should escape id [fail] +page/selectors-get-by.spec.ts › getByTestId should work [fail] +page/selectors-get-by.spec.ts › getByTestId should work for regex [fail] +page/selectors-get-by.spec.ts › getByTestId with custom testId should work [fail] +page/selectors-get-by.spec.ts › getByText should work [fail] +page/selectors-get-by.spec.ts › getByTitle should work [fail] +page/selectors-misc.spec.ts › chaining should work with large DOM @smoke [pass] +page/selectors-misc.spec.ts › data-testid on the handle should be relative [fail] +page/selectors-misc.spec.ts › should click on links in shadow dom [pass] +page/selectors-misc.spec.ts › should escape the scope with >> [fail] +page/selectors-misc.spec.ts › should print original xpath in error [fail] +page/selectors-misc.spec.ts › should properly determine visibility of display:contents elements [fail] +page/selectors-misc.spec.ts › should work for open shadow roots [fail] +page/selectors-misc.spec.ts › should work with :nth-match [fail] +page/selectors-misc.spec.ts › should work with :visible [fail] +page/selectors-misc.spec.ts › should work with >> visible= [fail] +page/selectors-misc.spec.ts › should work with internal:and= [fail] +page/selectors-misc.spec.ts › should work with internal:chain= [fail] +page/selectors-misc.spec.ts › should work with internal:has-not= [fail] +page/selectors-misc.spec.ts › should work with internal:has= [fail] +page/selectors-misc.spec.ts › should work with internal:or= [fail] +page/selectors-misc.spec.ts › should work with layout selectors [fail] +page/selectors-misc.spec.ts › should work with nth= [fail] +page/selectors-misc.spec.ts › should work with pipe in xpath [fail] +page/selectors-misc.spec.ts › should work with strict mode and chaining [fail] +page/selectors-misc.spec.ts › xpath should be relative [fail] +page/selectors-react.spec.ts › react15 › should compose [pass] +page/selectors-react.spec.ts › react15 › should exact match by props [fail] +page/selectors-react.spec.ts › react15 › should not crash when there is no match [pass] +page/selectors-react.spec.ts › react15 › should partially match by props [pass] +page/selectors-react.spec.ts › react15 › should query by props combinations [pass] +page/selectors-react.spec.ts › react15 › should support all string operators [pass] +page/selectors-react.spec.ts › react15 › should support nested react trees [pass] +page/selectors-react.spec.ts › react15 › should support regex [fail] +page/selectors-react.spec.ts › react15 › should support truthy querying [pass] +page/selectors-react.spec.ts › react15 › should work with multi-root elements (fragments) [unknown] +page/selectors-react.spec.ts › react15 › should work with multiroot react [pass] +page/selectors-react.spec.ts › react15 › should work with multiroot react after unmount [pass] +page/selectors-react.spec.ts › react15 › should work with multiroot react inside shadow DOM [fail] +page/selectors-react.spec.ts › react15 › should work with react memo [unknown] +page/selectors-react.spec.ts › react15 › should work with single-root elements @smoke [fail] +page/selectors-react.spec.ts › react16 › should compose [pass] +page/selectors-react.spec.ts › react16 › should exact match by props [pass] +page/selectors-react.spec.ts › react16 › should not crash when there is no match [pass] +page/selectors-react.spec.ts › react16 › should partially match by props [pass] +page/selectors-react.spec.ts › react16 › should query by props combinations [pass] +page/selectors-react.spec.ts › react16 › should support all string operators [pass] +page/selectors-react.spec.ts › react16 › should support nested react trees [pass] +page/selectors-react.spec.ts › react16 › should support regex [pass] +page/selectors-react.spec.ts › react16 › should support truthy querying [pass] +page/selectors-react.spec.ts › react16 › should work with multi-root elements (fragments) [pass] +page/selectors-react.spec.ts › react16 › should work with multiroot react [pass] +page/selectors-react.spec.ts › react16 › should work with multiroot react after unmount [pass] +page/selectors-react.spec.ts › react16 › should work with multiroot react inside shadow DOM [pass] +page/selectors-react.spec.ts › react16 › should work with react memo [unknown] +page/selectors-react.spec.ts › react16 › should work with single-root elements @smoke [pass] +page/selectors-react.spec.ts › react17 › should compose [pass] +page/selectors-react.spec.ts › react17 › should exact match by props [pass] +page/selectors-react.spec.ts › react17 › should not crash when there is no match [pass] +page/selectors-react.spec.ts › react17 › should partially match by props [pass] +page/selectors-react.spec.ts › react17 › should query by props combinations [pass] +page/selectors-react.spec.ts › react17 › should support all string operators [pass] +page/selectors-react.spec.ts › react17 › should support nested react trees [pass] +page/selectors-react.spec.ts › react17 › should support regex [pass] +page/selectors-react.spec.ts › react17 › should support truthy querying [pass] +page/selectors-react.spec.ts › react17 › should work with multi-root elements (fragments) [pass] +page/selectors-react.spec.ts › react17 › should work with multiroot react [pass] +page/selectors-react.spec.ts › react17 › should work with multiroot react after unmount [pass] +page/selectors-react.spec.ts › react17 › should work with multiroot react inside shadow DOM [pass] +page/selectors-react.spec.ts › react17 › should work with react memo [pass] +page/selectors-react.spec.ts › react17 › should work with single-root elements @smoke [pass] +page/selectors-react.spec.ts › react18 › should compose [pass] +page/selectors-react.spec.ts › react18 › should exact match by props [pass] +page/selectors-react.spec.ts › react18 › should not crash when there is no match [pass] +page/selectors-react.spec.ts › react18 › should partially match by props [pass] +page/selectors-react.spec.ts › react18 › should query by props combinations [pass] +page/selectors-react.spec.ts › react18 › should support all string operators [pass] +page/selectors-react.spec.ts › react18 › should support nested react trees [pass] +page/selectors-react.spec.ts › react18 › should support regex [pass] +page/selectors-react.spec.ts › react18 › should support truthy querying [pass] +page/selectors-react.spec.ts › react18 › should work with multi-root elements (fragments) [pass] +page/selectors-react.spec.ts › react18 › should work with multiroot react [pass] +page/selectors-react.spec.ts › react18 › should work with multiroot react after unmount [pass] +page/selectors-react.spec.ts › react18 › should work with multiroot react inside shadow DOM [pass] +page/selectors-react.spec.ts › react18 › should work with react memo [pass] +page/selectors-react.spec.ts › react18 › should work with single-root elements @smoke [pass] +page/selectors-register.spec.ts › getAttribute should be atomic [fail] +page/selectors-register.spec.ts › innerHTML should be atomic [fail] +page/selectors-register.spec.ts › innerText should be atomic [fail] +page/selectors-register.spec.ts › isVisible should be atomic [fail] +page/selectors-register.spec.ts › textContent should be atomic [fail] +page/selectors-role.spec.ts › errors [pass] +page/selectors-role.spec.ts › hidden with shadow dom slots [fail] +page/selectors-role.spec.ts › should detect roles [fail] +page/selectors-role.spec.ts › should filter hidden, unless explicitly asked for [fail] +page/selectors-role.spec.ts › should not match scope by default [fail] +page/selectors-role.spec.ts › should support checked [fail] +page/selectors-role.spec.ts › should support disabled [fail] +page/selectors-role.spec.ts › should support expanded [fail] +page/selectors-role.spec.ts › should support level [fail] +page/selectors-role.spec.ts › should support name [fail] +page/selectors-role.spec.ts › should support output accessible name [fail] +page/selectors-role.spec.ts › should support pressed [fail] +page/selectors-role.spec.ts › should support selected [fail] +page/selectors-text.spec.ts › hasText and internal:text should match full node text in strict mode [fail] +page/selectors-text.spec.ts › should be case sensitive if quotes are specified [fail] +page/selectors-text.spec.ts › should clear caches [fail] +page/selectors-text.spec.ts › should match input[type=button|submit] [fail] +page/selectors-text.spec.ts › should match root after >> [fail] +page/selectors-text.spec.ts › should match root after >> with * [fail] +page/selectors-text.spec.ts › should prioritize light dom over shadow dom in the same parent [pass] +page/selectors-text.spec.ts › should search for a substring without quotes [fail] +page/selectors-text.spec.ts › should skip head, script and style [fail] +page/selectors-text.spec.ts › should support empty string [fail] +page/selectors-text.spec.ts › should waitForSelector with distributed elements [pass] +page/selectors-text.spec.ts › should work @smoke [fail] +page/selectors-text.spec.ts › should work across nodes [fail] +page/selectors-text.spec.ts › should work for open shadow roots [pass] +page/selectors-text.spec.ts › should work with :has-text [fail] +page/selectors-text.spec.ts › should work with :text [fail] +page/selectors-text.spec.ts › should work with large DOM [pass] +page/selectors-text.spec.ts › should work with leading and trailing spaces [fail] +page/selectors-text.spec.ts › should work with paired quotes in the middle of selector [fail] +page/selectors-text.spec.ts › should work with text nodes in quoted mode [fail] +page/selectors-text.spec.ts › should work with unpaired quotes when not at the start [fail] +page/selectors-vue.spec.ts › vue2 › should compose [pass] +page/selectors-vue.spec.ts › vue2 › should exact match by props [pass] +page/selectors-vue.spec.ts › vue2 › should not crash when there is no match [pass] +page/selectors-vue.spec.ts › vue2 › should partially match by props [pass] +page/selectors-vue.spec.ts › vue2 › should query by props combinations [pass] +page/selectors-vue.spec.ts › vue2 › should support all string operators [pass] +page/selectors-vue.spec.ts › vue2 › should support nested vue trees [pass] +page/selectors-vue.spec.ts › vue2 › should support regex [pass] +page/selectors-vue.spec.ts › vue2 › should support truthy querying [pass] +page/selectors-vue.spec.ts › vue2 › should work with multi-root elements (fragments) [unknown] +page/selectors-vue.spec.ts › vue2 › should work with multiroot react [pass] +page/selectors-vue.spec.ts › vue2 › should work with multiroot vue inside shadow DOM [pass] +page/selectors-vue.spec.ts › vue2 › should work with single-root elements @smoke [pass] +page/selectors-vue.spec.ts › vue3 › should compose [pass] +page/selectors-vue.spec.ts › vue3 › should exact match by props [pass] +page/selectors-vue.spec.ts › vue3 › should not crash when there is no match [pass] +page/selectors-vue.spec.ts › vue3 › should partially match by props [pass] +page/selectors-vue.spec.ts › vue3 › should query by props combinations [pass] +page/selectors-vue.spec.ts › vue3 › should support all string operators [pass] +page/selectors-vue.spec.ts › vue3 › should support nested vue trees [pass] +page/selectors-vue.spec.ts › vue3 › should support regex [pass] +page/selectors-vue.spec.ts › vue3 › should support truthy querying [pass] +page/selectors-vue.spec.ts › vue3 › should work with multi-root elements (fragments) [pass] +page/selectors-vue.spec.ts › vue3 › should work with multiroot react [pass] +page/selectors-vue.spec.ts › vue3 › should work with multiroot vue inside shadow DOM [pass] +page/selectors-vue.spec.ts › vue3 › should work with single-root elements @smoke [pass] +page/wheel.spec.ts › should dispatch wheel event on svg element [fail] +page/wheel.spec.ts › should dispatch wheel events @smoke [fail] +page/wheel.spec.ts › should dispatch wheel events after context menu was opened [fail] +page/wheel.spec.ts › should dispatch wheel events after popup was opened @smoke [fail] +page/wheel.spec.ts › should scroll horizontally [fail] +page/wheel.spec.ts › should scroll when nobody is listening [pass] +page/wheel.spec.ts › should set the modifiers [fail] +page/wheel.spec.ts › should work when the event is canceled [fail] +page/workers.spec.ts › Page.workers @smoke [timeout] +page/workers.spec.ts › should attribute network activity for worker inside iframe to the iframe [timeout] +page/workers.spec.ts › should clear upon cross-process navigation [timeout] +page/workers.spec.ts › should clear upon navigation [timeout] +page/workers.spec.ts › should dispatch console messages when page has workers [timeout] +page/workers.spec.ts › should emit created and destroyed events [timeout] +page/workers.spec.ts › should evaluate [timeout] +page/workers.spec.ts › should have JSHandles for console logs [fail] +page/workers.spec.ts › should not report console logs from workers twice [pass] +page/workers.spec.ts › should report and intercept network from nested worker [fail] +page/workers.spec.ts › should report console logs [pass] +page/workers.spec.ts › should report errors [timeout] +page/workers.spec.ts › should report network activity [timeout] +page/workers.spec.ts › should report network activity on worker creation [pass] +page/workers.spec.ts › should support extra http headers [timeout] +page/workers.spec.ts › should support offline [timeout] \ No newline at end of file diff --git a/tests/bidi/playwright.config.ts b/tests/bidi/playwright.config.ts index 798f59fb7d..39df88f537 100644 --- a/tests/bidi/playwright.config.ts +++ b/tests/bidi/playwright.config.ts @@ -21,18 +21,22 @@ import { type Config, type PlaywrightTestOptions, type PlaywrightWorkerOptions, import * as path from 'path'; import type { TestModeWorkerOptions } from '../config/testModeFixtures'; -const getExecutablePath = () => { - return process.env.BIDIPATH; -}; - const headed = process.argv.includes('--headed'); const trace = !!process.env.PWTEST_TRACE; +const hasDebugOutput = process.env.DEBUG?.includes('pw:'); + +function firefoxUserPrefs() { + const prefsString = process.env.PWTEST_FIREFOX_USER_PREFS; + if (!prefsString) + return undefined; + return JSON.parse(prefsString); +} const outputDir = path.join(__dirname, '..', '..', 'test-results'); const testDir = path.join(__dirname, '..'); const reporters = () => { const result: ReporterDescription[] = process.env.CI ? [ - ['dot'], + hasDebugOutput ? ['list'] : ['dot'], ['json', { outputFile: path.join(outputDir, 'report.json') }], ['blob', { fileName: `${process.env.PWTEST_BOT_NAME}.zip` }], ] : [ @@ -50,7 +54,7 @@ const config: Config<PlaywrightWorkerOptions & PlaywrightTestOptions & TestModeW }, maxFailures: 0, timeout: 15 * 1000, - globalTimeout: 30 * 60 * 1000, + globalTimeout: 60 * 60 * 1000, workers: process.env.CI ? 2 : undefined, fullyParallel: !process.env.CI, forbidOnly: !!process.env.CI, @@ -59,7 +63,7 @@ const config: Config<PlaywrightWorkerOptions & PlaywrightTestOptions & TestModeW projects: [], }; -const executablePath = getExecutablePath(); +const executablePath = process.env.BIDIPATH; if (executablePath && !process.env.TEST_WORKER_INDEX) console.error(`Using executable at ${executablePath}`); const testIgnore: RegExp[] = []; @@ -83,6 +87,7 @@ for (const [key, channels] of Object.entries(browserToChannels)) { video: 'off', launchOptions: { executablePath, + firefoxUserPrefs: firefoxUserPrefs(), }, trace: trace ? 'on' : undefined, }, diff --git a/tests/components/ct-react-vite/tests/render.spec.tsx b/tests/components/ct-react-vite/tests/render.spec.tsx index d00a313e9e..b7d0e61b29 100644 --- a/tests/components/ct-react-vite/tests/render.spec.tsx +++ b/tests/components/ct-react-vite/tests/render.spec.tsx @@ -46,3 +46,8 @@ test('render inline component with an error if its nested', async ({ mount }) => <MyInlineComponent value="Max" /> </DefaultChildren>)).rejects.toThrow('Component "MyInlineComponent" cannot be mounted.'); }); + +test('render Fragment shorthand notation', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32853' } }, async ({ mount }) => { + const component = await mount(<>Learn React</>); + await expect(component).toContainText('Learn React'); +}); diff --git a/tests/components/ct-vue-vite/src/components/SlotDefaultValue.vue b/tests/components/ct-vue-vite/src/components/SlotDefaultValue.vue new file mode 100644 index 0000000000..c5944a3f8a --- /dev/null +++ b/tests/components/ct-vue-vite/src/components/SlotDefaultValue.vue @@ -0,0 +1,3 @@ +<template> + <slot>default value</slot> +</template> diff --git a/tests/components/ct-vue-vite/tests/slots/slots.spec.js b/tests/components/ct-vue-vite/tests/slots/slots.spec.js index a33c9dac92..7d36d9733b 100644 --- a/tests/components/ct-vue-vite/tests/slots/slots.spec.js +++ b/tests/components/ct-vue-vite/tests/slots/slots.spec.js @@ -2,6 +2,7 @@ import { test, expect } from '@playwright/experimental-ct-vue'; import DefaultSlot from '@/components/DefaultSlot.vue'; import NamedSlots from '@/components/NamedSlots.vue'; import Button from '@/components/Button.vue'; +import SlotDefaultValue from "@/components/SlotDefaultValue.vue"; test('render a default slot', async ({ mount }) => { const component = await mount(DefaultSlot, { @@ -49,3 +50,13 @@ test('render a component with a named slot', async ({ mount }) => { await expect(component).toContainText('Main Content'); await expect(component).toContainText('Footer'); }); + +test('updating default slot should work', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32809' } }, async ({ mount }) => { + const slots = { default: 'foo' }; + + const component = await mount(SlotDefaultValue, { slots }); + await expect(component).toHaveText('foo'); + + await component.update({ slots }); + await expect(component).toHaveText('foo'); +}); diff --git a/tests/components/ct-vue-vite/tests/slots/slots.spec.ts b/tests/components/ct-vue-vite/tests/slots/slots.spec.ts index a33c9dac92..7d36d9733b 100644 --- a/tests/components/ct-vue-vite/tests/slots/slots.spec.ts +++ b/tests/components/ct-vue-vite/tests/slots/slots.spec.ts @@ -2,6 +2,7 @@ import { test, expect } from '@playwright/experimental-ct-vue'; import DefaultSlot from '@/components/DefaultSlot.vue'; import NamedSlots from '@/components/NamedSlots.vue'; import Button from '@/components/Button.vue'; +import SlotDefaultValue from "@/components/SlotDefaultValue.vue"; test('render a default slot', async ({ mount }) => { const component = await mount(DefaultSlot, { @@ -49,3 +50,13 @@ test('render a component with a named slot', async ({ mount }) => { await expect(component).toContainText('Main Content'); await expect(component).toContainText('Footer'); }); + +test('updating default slot should work', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32809' } }, async ({ mount }) => { + const slots = { default: 'foo' }; + + const component = await mount(SlotDefaultValue, { slots }); + await expect(component).toHaveText('foo'); + + await component.update({ slots }); + await expect(component).toHaveText('foo'); +}); diff --git a/tests/config/browserTest.ts b/tests/config/browserTest.ts index 44aaca9d26..6a178bbaf2 100644 --- a/tests/config/browserTest.ts +++ b/tests/config/browserTest.ts @@ -30,6 +30,7 @@ import type { TestInfo } from '@playwright/test'; export type BrowserTestWorkerFixtures = PageWorkerFixtures & { browserVersion: string; defaultSameSiteCookieValue: string; + sameSiteStoredValueForNone: string; allowsThirdParty: boolean; browserMajorVersion: number; browserType: BrowserType; @@ -69,19 +70,28 @@ const test = baseTest.extend<BrowserTestTestFixtures, BrowserTestWorkerFixtures> await run(false); }, { scope: 'worker' }], - defaultSameSiteCookieValue: [async ({ browserName, isLinux }, run) => { + defaultSameSiteCookieValue: [async ({ browserName, platform, macVersion }, run) => { if (browserName === 'chromium' || browserName as any === '_bidiChromium') await run('Lax'); - else if (browserName === 'webkit' && isLinux) + else if (browserName === 'webkit' && platform === 'linux') await run('Lax'); - else if (browserName === 'webkit' && !isLinux) - await run('None'); + else if (browserName === 'webkit' && platform === 'darwin' && macVersion >= 15) + await run('Lax'); + else if (browserName === 'webkit') + await run('None'); // Windows + older macOS else if (browserName === 'firefox' || browserName as any === '_bidiFirefox') await run('None'); else throw new Error('unknown browser - ' + browserName); }, { scope: 'worker' }], + sameSiteStoredValueForNone: [async ({ browserName, isMac, macVersion }, run) => { + if (browserName === 'webkit' && isMac && macVersion >= 15) + await run('Lax'); + else + await run('None'); + }, { scope: 'worker' }], + browserMajorVersion: [async ({ browserVersion }, run) => { await run(Number(browserVersion.split('.')[0])); }, { scope: 'worker' }], diff --git a/tests/config/platformFixtures.ts b/tests/config/platformFixtures.ts index 34d340f718..3bc8ec52cd 100644 --- a/tests/config/platformFixtures.ts +++ b/tests/config/platformFixtures.ts @@ -15,12 +15,14 @@ */ import { test } from '@playwright/test'; +import os from 'os'; export type PlatformWorkerFixtures = { platform: 'win32' | 'darwin' | 'linux'; isWindows: boolean; isMac: boolean; isLinux: boolean; + macVersion: number; // major only, 11 or later, zero if not mac }; function platform(): 'win32' | 'darwin' | 'linux' { @@ -33,9 +35,16 @@ function platform(): 'win32' | 'darwin' | 'linux' { return process.platform as 'win32' | 'darwin' | 'linux'; } +function macVersion() { + if (process.platform !== 'darwin') + return 0; + return +os.release().split('.')[0] - 9; +} + export const platformTest = test.extend<{}, PlatformWorkerFixtures>({ platform: [platform(), { scope: 'worker' }], isWindows: [platform() === 'win32', { scope: 'worker' }], isMac: [platform() === 'darwin', { scope: 'worker' }], isLinux: [platform() === 'linux', { scope: 'worker' }], + macVersion: [macVersion(), { scope: 'worker' }], }); diff --git a/tests/config/proxy.ts b/tests/config/proxy.ts index dc2d51b3ec..f9672cfb1b 100644 --- a/tests/config/proxy.ts +++ b/tests/config/proxy.ts @@ -57,14 +57,14 @@ export class TestProxy { this._prependHandler('request', (req: IncomingMessage) => { this.requestUrls.push(req.url); const url = new URL(req.url); - url.host = `localhost:${port}`; + url.host = `127.0.0.1:${port}`; req.url = url.toString(); }); this._prependHandler('connect', (req: IncomingMessage) => { if (!options?.allowConnectRequests) return; this.connectHosts.push(req.url); - req.url = `localhost:${port}`; + req.url = `127.0.0.1:${port}`; }); } @@ -138,10 +138,10 @@ export async function setupSocksForwardingServer({ connections.get(payload.uid)?.destroy(); connections.delete(payload.uid); }); - await socksProxy.listen(port, 'localhost'); + await socksProxy.listen(port, '127.0.0.1'); return { closeProxyServer: () => socksProxy.close(), - proxyServerAddr: `socks5://localhost:${port}`, + proxyServerAddr: `socks5://127.0.0.1:${port}`, connectHosts, }; } diff --git a/tests/config/testModeFixtures.ts b/tests/config/testModeFixtures.ts index 6b6feff7c2..1231a78260 100644 --- a/tests/config/testModeFixtures.ts +++ b/tests/config/testModeFixtures.ts @@ -21,6 +21,7 @@ import * as playwrightLibrary from 'playwright-core'; export type TestModeWorkerOptions = { mode: TestModeName; + codegenMode: 'trace-events' | 'actions'; }; export type TestModeTestFixtures = { @@ -48,6 +49,7 @@ export const testModeTest = test.extend<TestModeTestFixtures, TestModeWorkerOpti await run(playwright); await testMode.teardown(); }, { scope: 'worker' }], + codegenMode: ['actions', { scope: 'worker', option: true }], toImplInWorkerScope: [async ({ playwright }, use) => { await use((playwright as any)._toImpl); diff --git a/tests/config/testserver/index.ts b/tests/config/testserver/index.ts index fa20a61762..ec7d4c1f69 100644 --- a/tests/config/testserver/index.ts +++ b/tests/config/testserver/index.ts @@ -22,6 +22,7 @@ import type net from 'net'; import path from 'path'; import url from 'url'; import util from 'util'; +import type stream from 'stream'; import ws from 'ws'; import zlib, { gzip } from 'zlib'; import { createHttpServer, createHttpsServer } from '../../../packages/playwright-core/lib/utils/network'; @@ -31,6 +32,11 @@ const rejectSymbol = Symbol('reject callback'); const gzipAsync = util.promisify(gzip.bind(zlib)); +type UpgradeActions = { + doUpgrade: () => void; + socket: stream.Duplex; +}; + export class TestServer { private _server: http.Server; private _wsServer: ws.WebSocketServer; @@ -44,6 +50,7 @@ export class TestServer { private _extraHeaders = new Map<string, object>(); private _gzipRoutes = new Set<string>(); private _requestSubscribers = new Map<string, Promise<any>>(); + private _upgradeCallback: (actions: UpgradeActions) => void | undefined; readonly PORT: number; readonly PREFIX: string; readonly CROSS_PROCESS_PREFIX: string; @@ -73,6 +80,16 @@ export class TestServer { this._server.on('connection', socket => this._onSocket(socket)); this._wsServer = new ws.WebSocketServer({ noServer: true }); this._server.on('upgrade', async (request, socket, head) => { + const doUpgrade = () => { + this._wsServer.handleUpgrade(request, socket, head, ws => { + // Next emit is only for our internal 'connection' listeners. + this._wsServer.emit('connection', ws, request); + }); + }; + if (this._upgradeCallback) { + this._upgradeCallback({ doUpgrade, socket }); + return; + } const pathname = url.parse(request.url!).path; if (pathname === '/ws-401') { socket.write('HTTP/1.1 401 Unauthorized\r\n\r\nUnauthorized body'); @@ -86,10 +103,7 @@ export class TestServer { socket.destroy(); return; } - this._wsServer.handleUpgrade(request, socket, head, ws => { - // Next emit is only for our internal 'connection' listeners. - this._wsServer.emit('connection', ws, request); - }); + doUpgrade(); }); this._server.listen(port); this._dirPath = dirPath; @@ -177,6 +191,8 @@ export class TestServer { this._csp.clear(); this._extraHeaders.clear(); this._gzipRoutes.clear(); + this._upgradeCallback = undefined; + this._wsServer.removeAllListeners('connection'); this._server.closeAllConnections(); const error = new Error('Static Server has been reset'); for (const subscriber of this._requestSubscribers.values()) @@ -294,6 +310,14 @@ export class TestServer { }); } + waitForUpgrade() { + return new Promise<UpgradeActions>(fulfill => this._upgradeCallback = fulfill); + } + + waitForWebSocket() { + return new Promise<ws.WebSocket>(fulfill => this._wsServer.once('connection', (ws, req) => fulfill(ws))); + } + sendOnWebSocketConnection(data) { this.onceWebSocketConnection(ws => ws.send(data)); } diff --git a/tests/config/traceViewerFixtures.ts b/tests/config/traceViewerFixtures.ts index 3b79a55e98..0fe4a9a5c9 100644 --- a/tests/config/traceViewerFixtures.ts +++ b/tests/config/traceViewerFixtures.ts @@ -71,10 +71,17 @@ class TraceViewerPage { return await this.page.waitForSelector(`.list-view-entry:has-text("${action}") .action-icons`); } + @step async selectAction(title: string, ordinal: number = 0) { await this.page.locator(`.action-title:has-text("${title}")`).nth(ordinal).click(); } + @step + async hoverAction(title: string, ordinal: number = 0) { + await this.page.locator(`.action-title:has-text("${title}")`).nth(ordinal).hover(); + } + + @step async selectSnapshot(name: string) { await this.page.click(`.snapshot-tab .tabbed-pane-tab-label:has-text("${name}")`); } diff --git a/tests/config/utils.ts b/tests/config/utils.ts index d01d40771f..74158aeeae 100644 --- a/tests/config/utils.ts +++ b/tests/config/utils.ts @@ -16,10 +16,10 @@ import type { Frame, Page } from 'playwright-core'; import { ZipFile } from '../../packages/playwright-core/lib/utils/zipFile'; -import type { TraceModelBackend } from '../../packages/trace-viewer/src/traceModel'; +import type { TraceModelBackend } from '../../packages/trace-viewer/src/sw/traceModel'; import type { StackFrame } from '../../packages/protocol/src/channels'; import { parseClientSideCallMetadata } from '../../packages/playwright-core/lib/utils/isomorphic/traceUtils'; -import { TraceModel } from '../../packages/trace-viewer/src/traceModel'; +import { TraceModel } from '../../packages/trace-viewer/src/sw/traceModel'; import type { ActionTreeItem } from '../../packages/trace-viewer/src/ui/modelUtil'; import { buildActionTree, MultiTraceModel } from '../../packages/trace-viewer/src/ui/modelUtil'; import type { ActionTraceEvent, ConsoleMessageTraceEvent, EventTraceEvent, TraceEvent } from '@trace/trace'; diff --git a/tests/installation/playwright.config.ts b/tests/installation/playwright.config.ts index 25ca82017b..f61efa87ff 100644 --- a/tests/installation/playwright.config.ts +++ b/tests/installation/playwright.config.ts @@ -38,7 +38,7 @@ export default defineConfig({ outputDir, testIgnore: '**\/fixture-scripts/**', timeout: 5 * 60 * 1000, - retries: 0, + retries: process.env.CI ? 3 : 0, reporter: reporters(), forbidOnly: !!process.env.CI, workers: 1, diff --git a/tests/library/browsercontext-cookies.spec.ts b/tests/library/browsercontext-cookies.spec.ts index df923ef3ff..9bb600c786 100644 --- a/tests/library/browsercontext-cookies.spec.ts +++ b/tests/library/browsercontext-cookies.spec.ts @@ -142,7 +142,7 @@ it('should get multiple cookies', async ({ context, page, server, defaultSameSit ])); }); -it('should get cookies from multiple urls', async ({ context, browserName, isWindows }) => { +it('should get cookies from multiple urls', async ({ context, browserName, isWindows, sameSiteStoredValueForNone }) => { await context.addCookies([{ url: 'https://foo.com', name: 'doggo', @@ -177,7 +177,7 @@ it('should get cookies from multiple urls', async ({ context, browserName, isWin expires: -1, httpOnly: false, secure: true, - sameSite: 'None', + sameSite: sameSiteStoredValueForNone, }])); }); @@ -273,7 +273,7 @@ it('should return secure cookies based on HTTP(S) protocol', async ({ context, b }]); }); -it('should add cookies with an expiration', async ({ context }) => { +it('should add cookies with an expiration', async ({ context, sameSiteStoredValueForNone }) => { const expires = Math.floor((Date.now() / 1000)) + 3600; await context.addCookies([{ url: 'https://foo.com', @@ -292,7 +292,7 @@ it('should add cookies with an expiration', async ({ context }) => { expires, httpOnly: false, secure: true, - sameSite: 'None', + sameSite: sameSiteStoredValueForNone, }]); { // Rollover to 5-digit year @@ -350,7 +350,7 @@ it('should be able to send third party cookies via an iframe', async ({ browser, } }); -it('should support requestStorageAccess', async ({ page, server, channel, browserName, isMac, isLinux, isWindows }) => { +it('should support requestStorageAccess', async ({ page, server, channel, browserName, isMac, isLinux, isWindows, macVersion }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/17285' }); it.skip(browserName === 'chromium', 'requestStorageAccess API is not available in Chromium'); it.skip(channel === 'firefox-beta', 'hasStorageAccess returns true, but no cookie is sent'); @@ -396,7 +396,7 @@ it('should support requestStorageAccess', async ({ page, server, channel, browse server.waitForRequest('/title.html'), frame.evaluate(() => fetch('/title.html')) ]); - if (isLinux && browserName === 'webkit') + if ((isLinux || (isMac && macVersion >= 15)) && browserName === 'webkit') expect(serverRequest.headers.cookie).toBe(undefined); else expect(serverRequest.headers.cookie).toBe('name=value'); diff --git a/tests/library/browsercontext-credentials.spec.ts b/tests/library/browsercontext-credentials.spec.ts index cc991e0e5a..7783b65aea 100644 --- a/tests/library/browsercontext-credentials.spec.ts +++ b/tests/library/browsercontext-credentials.spec.ts @@ -25,30 +25,30 @@ const it = base.extend<{ isChromiumHeadedLike: boolean }>({ }); it('should fail without credentials', async ({ browser, server, isChromiumHeadedLike }) => { - it.fail(isChromiumHeadedLike); - server.setAuth('/empty.html', 'user', 'pass'); const context = await browser.newContext(); const page = await context.newPage(); - try { - const response = await page.goto(server.EMPTY_PAGE); - expect(response!.status()).toBe(401); - } finally { - await context.close(); - } + const responseOrError = await page.goto(server.EMPTY_PAGE).catch(e => e); + if (isChromiumHeadedLike) + expect(responseOrError.message).toContain('net::ERR_INVALID_AUTH_CREDENTIALS'); + else + expect(responseOrError.status()).toBe(401); }); it('should work with setHTTPCredentials', async ({ browser, server, isChromiumHeadedLike }) => { - it.fail(isChromiumHeadedLike); - server.setAuth('/empty.html', 'user', 'pass'); const context = await browser.newContext(); const page = await context.newPage(); - let response = await page.goto(server.EMPTY_PAGE); - expect(response!.status()).toBe(401); + + let responseOrError = await page.goto(server.EMPTY_PAGE).catch(e => e); + if (isChromiumHeadedLike) + expect(responseOrError.message).toContain('net::ERR_INVALID_AUTH_CREDENTIALS'); + else + expect(responseOrError.status()).toBe(401); + await context.setHTTPCredentials({ username: 'user', password: 'pass' }); - response = await page.reload(); - expect(response!.status()).toBe(200); + responseOrError = await page.reload(); + expect(responseOrError.status()).toBe(200); await context.close(); }); @@ -110,19 +110,20 @@ it('should work with correct credentials and matching origin case insensitive', }); it('should fail with correct credentials and mismatching scheme', async ({ browser, server, isChromiumHeadedLike }) => { - it.fail(isChromiumHeadedLike); server.setAuth('/empty.html', 'user', 'pass'); const context = await browser.newContext({ httpCredentials: { username: 'user', password: 'pass', origin: server.PREFIX.replace('http://', 'https://') } }); const page = await context.newPage(); - const response = await page.goto(server.EMPTY_PAGE); - expect(response!.status()).toBe(401); + const responseOrError = await page.goto(server.EMPTY_PAGE).catch(e => e); + if (isChromiumHeadedLike) + expect(responseOrError.message).toContain('net::ERR_INVALID_AUTH_CREDENTIALS'); + else + expect(responseOrError.status()).toBe(401); await context.close(); }); it('should fail with correct credentials and mismatching hostname', async ({ browser, server, isChromiumHeadedLike }) => { - it.fail(isChromiumHeadedLike); server.setAuth('/empty.html', 'user', 'pass'); const hostname = new URL(server.PREFIX).hostname; const origin = server.PREFIX.replace(hostname, 'mismatching-hostname'); @@ -130,20 +131,25 @@ it('should fail with correct credentials and mismatching hostname', async ({ bro httpCredentials: { username: 'user', password: 'pass', origin: origin } }); const page = await context.newPage(); - const response = await page.goto(server.EMPTY_PAGE); - expect(response!.status()).toBe(401); + const responseOrError = await page.goto(server.EMPTY_PAGE).catch(e => e); + if (isChromiumHeadedLike) + expect(responseOrError.message).toContain('net::ERR_INVALID_AUTH_CREDENTIALS'); + else + expect(responseOrError.status()).toBe(401); await context.close(); }); it('should fail with correct credentials and mismatching port', async ({ browser, server, isChromiumHeadedLike }) => { - it.fail(isChromiumHeadedLike); server.setAuth('/empty.html', 'user', 'pass'); const origin = server.PREFIX.replace(server.PORT.toString(), (server.PORT + 1).toString()); const context = await browser.newContext({ httpCredentials: { username: 'user', password: 'pass', origin: origin } }); const page = await context.newPage(); - const response = await page.goto(server.EMPTY_PAGE); - expect(response!.status()).toBe(401); + const responseOrError = await page.goto(server.EMPTY_PAGE).catch(e => e); + if (isChromiumHeadedLike) + expect(responseOrError.message).toContain('net::ERR_INVALID_AUTH_CREDENTIALS'); + else + expect(responseOrError.status()).toBe(401); await context.close(); }); diff --git a/tests/library/browsercontext-fetch.spec.ts b/tests/library/browsercontext-fetch.spec.ts index d10b182cf0..ae5cf5e7d5 100644 --- a/tests/library/browsercontext-fetch.spec.ts +++ b/tests/library/browsercontext-fetch.spec.ts @@ -1245,7 +1245,7 @@ it('should work with connectOverCDP', async ({ browserName, browserType, server } }); -it('should support SameSite cookie attribute over https', async ({ contextFactory, httpsServer, browserName, isWindows }) => { +it('should support SameSite cookie attribute over https', async ({ contextFactory, httpsServer, browserName, isWindows, sameSiteStoredValueForNone }) => { // Cookies with SameSite=None must also specify the Secure attribute. WebKit navigation // to HTTP url will fail if the response contains a cookie with Secure attribute, so // we do HTTPS navigation. @@ -1261,6 +1261,8 @@ it('should support SameSite cookie attribute over https', async ({ contextFactor const [cookie] = await page.context().cookies(); if (browserName === 'webkit' && isWindows) expect(cookie.sameSite).toBe('None'); + else if (value === 'None') + expect(cookie.sameSite).toBe(sameSiteStoredValueForNone); else expect(cookie.sameSite).toBe(value); }); @@ -1290,7 +1292,7 @@ it('fetch should not throw on long set-cookie value', async ({ context, server } expect(cookies.map(c => c.name)).toContain('bar'); }); -it('should support set-cookie with SameSite and without Secure attribute over HTTP', async ({ page, server, browserName, isWindows, isLinux }) => { +it('should support set-cookie with SameSite and without Secure attribute over HTTP', async ({ page, server, browserName, isWindows, isLinux, sameSiteStoredValueForNone }) => { for (const value of ['None', 'Lax', 'Strict']) { await it.step(`SameSite=${value}`, async () => { server.setRoute('/empty.html', (req, res) => { @@ -1305,6 +1307,8 @@ it('should support set-cookie with SameSite and without Secure attribute over HT expect(cookie).toBeFalsy(); else if (browserName === 'webkit' && isWindows) expect(cookie.sameSite).toBe('None'); + else if (value === 'None') + expect(cookie.sameSite).toBe(sameSiteStoredValueForNone); else expect(cookie.sameSite).toBe(value); }); diff --git a/tests/library/browsercontext-network-event.spec.ts b/tests/library/browsercontext-network-event.spec.ts index 14684d82c0..45d42c41bf 100644 --- a/tests/library/browsercontext-network-event.spec.ts +++ b/tests/library/browsercontext-network-event.spec.ts @@ -144,3 +144,23 @@ it('should not fire events for favicon or favicon redirects', async ({ context, expect(events).not.toEqual(expect.arrayContaining([expect.stringContaining(favicon)])); expect(events).not.toEqual(expect.arrayContaining([expect.stringContaining(hashedFaviconUrl)])); }); + +it('should reject response.finished if context closes', async ({ page, server }) => { + await page.goto(server.EMPTY_PAGE); + server.setRoute('/get', (req, res) => { + // In Firefox, |fetch| will be hanging until it receives |Content-Type| header + // from server. + res.setHeader('Content-Type', 'text/plain; charset=utf-8'); + res.write('hello '); + }); + // send request and wait for server response + const [pageResponse] = await Promise.all([ + page.waitForEvent('response'), + page.evaluate(() => fetch('./get', { method: 'GET' })), + ]); + + const finishPromise = pageResponse.finished().catch(e => e); + await page.context().close(); + const error = await finishPromise; + expect(error.message).toContain('closed'); +}); diff --git a/tests/library/browsercontext-reuse.spec.ts b/tests/library/browsercontext-reuse.spec.ts index 14590a3ae3..30319f0aad 100644 --- a/tests/library/browsercontext-reuse.spec.ts +++ b/tests/library/browsercontext-reuse.spec.ts @@ -203,10 +203,10 @@ test('should ignore binding from beforeunload', async ({ reusedContext }) => { expect(called).toBe(false); }); -test('should reset mouse position', async ({ reusedContext, browserName, platform }) => { +test('should reset mouse position', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/22432' }, +}, async ({ reusedContext, browserName, platform }) => { // Note: this test only reproduces the issue locally when run with --repeat-each=20. - test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/22432' }); - test.fixme(browserName === 'chromium' && platform !== 'darwin', 'chromium keeps hover on linux/win'); const pageContent = ` <style> @@ -214,6 +214,7 @@ test('should reset mouse position', async ({ reusedContext, browserName, platfor div:hover { background: red; } html, body { margin: 0; padding: 0; } </style> + <div id=filler>one</div> <div id=one>one</div> <div id=two>two</div> `; @@ -224,7 +225,7 @@ test('should reset mouse position', async ({ reusedContext, browserName, platfor await expect(page.locator('#one')).toHaveCSS('background-color', 'rgb(0, 0, 255)'); await expect(page.locator('#two')).toHaveCSS('background-color', 'rgb(0, 0, 255)'); - await page.mouse.move(10, 45); + await page.mouse.move(10, 75); await expect(page.locator('#one')).toHaveCSS('background-color', 'rgb(0, 0, 255)'); await expect(page.locator('#two')).toHaveCSS('background-color', 'rgb(255, 0, 0)'); diff --git a/tests/library/browsercontext-storage-state.spec.ts b/tests/library/browsercontext-storage-state.spec.ts index 89bb84d2c3..02db42506d 100644 --- a/tests/library/browsercontext-storage-state.spec.ts +++ b/tests/library/browsercontext-storage-state.spec.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { attachFrame } from 'tests/config/utils'; import { browserTest as it, expect } from '../config/browserTest'; import fs from 'fs'; @@ -275,3 +276,43 @@ it('should work when service worker is intefering', async ({ page, context, serv const storageState = await context.storageState(); expect(storageState.origins[0].localStorage[0]).toEqual({ name: 'foo', value: 'bar' }); }); + +it('should set local storage in third-party context', async ({ contextFactory, server }) => { + const context = await contextFactory({ + storageState: { + cookies: [], + origins: [ + { + origin: server.CROSS_PROCESS_PREFIX, + localStorage: [{ + name: 'name1', + value: 'value1' + }] + }, + ] + } + }); + const page = await context.newPage(); + await page.goto(server.EMPTY_PAGE); + const frame = await attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html'); + + const localStorage = await frame.evaluate('window.localStorage'); + expect(localStorage).toEqual({ name1: 'value1' }); + await context.close(); +}); + +it('should roundtrip local storage in third-party context', async ({ page, contextFactory, server }) => { + await page.goto(server.EMPTY_PAGE); + const frame = await attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html'); + await frame.evaluate(() => window.localStorage.setItem('name1', 'value1')); + const storageState = await page.context().storageState(); + + const context2 = await contextFactory({ storageState }); + const page2 = await context2.newPage(); + await page2.goto(server.EMPTY_PAGE); + const frame2 = await attachFrame(page2, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html'); + + const localStorage = await frame2.evaluate('window.localStorage'); + expect(localStorage).toEqual({ name1: 'value1' }); + await context2.close(); +}); diff --git a/tests/library/browsercontext-viewport-mobile.spec.ts b/tests/library/browsercontext-viewport-mobile.spec.ts index 2e0f90ceb0..ed774e383a 100644 --- a/tests/library/browsercontext-viewport-mobile.spec.ts +++ b/tests/library/browsercontext-viewport-mobile.spec.ts @@ -188,6 +188,22 @@ it.describe('mobile viewport', () => { await context.close(); }); + it('should scroll mobile page with background-attachment: fixed', { + annotation: [ + { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31551' }, + { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23573' }, + ] + }, async ({ playwright, browser, server, browserName, isLinux, headless }) => { + it.fixme(browserName === 'webkit' && isLinux && headless, 'Fails on WPE apparently due to accelerated compositing + fixed layout'); + const iPhone = playwright.devices['iPhone 12']; + const context = await browser.newContext({ ...iPhone }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/input/background-fixed.html'); + await page.getByRole('button').click(); + expect(await page.evaluate(() => window.scrollY)).toBeGreaterThan(1000); + await context.close(); + }); + it('view scale should reset after navigation', async ({ browser, browserName }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/26876' }); const context = await browser.newContext({ diff --git a/tests/library/browsercontext-viewport.spec.ts b/tests/library/browsercontext-viewport.spec.ts index abb14b3a71..4fdd181f8f 100644 --- a/tests/library/browsercontext-viewport.spec.ts +++ b/tests/library/browsercontext-viewport.spec.ts @@ -19,7 +19,6 @@ import { devices } from '@playwright/test'; import { contextTest as it, expect } from '../config/browserTest'; import { browserTest } from '../config/browserTest'; import { verifyViewport } from '../config/utils'; -import * as os from 'os'; it('should get the proper default viewport size', async ({ page, server }) => { await verifyViewport(page, 1280, 720); @@ -176,10 +175,10 @@ browserTest('should be able to get correct orientation angle on non-mobile devic await context.close(); }); -it('should set window.screen.orientation.type for mobile devices', async ({ contextFactory, browserName, server, isMac }) => { +it('should set window.screen.orientation.type for mobile devices', async ({ contextFactory, browserName, server, isMac, macVersion }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31151' }); it.skip(browserName === 'firefox', 'Firefox does not support mobile emulation'); - it.skip(browserName === 'webkit' && isMac && parseInt(os.release().split('.')[0], 10) <= 21, 'WebKit on macOS 12 is frozen and does not support orientation.type override'); + it.skip(browserName === 'webkit' && isMac && macVersion <= 12, 'WebKit on macOS 12 is frozen and does not support orientation.type override'); const context = await contextFactory(devices['iPhone 14']); const page = await context.newPage(); await page.goto(server.PREFIX + '/index.html'); diff --git a/tests/library/browsertype-connect.spec.ts b/tests/library/browsertype-connect.spec.ts index 4a3cdb1efa..d494f63b19 100644 --- a/tests/library/browsertype-connect.spec.ts +++ b/tests/library/browsertype-connect.spec.ts @@ -16,7 +16,6 @@ */ import fs from 'fs'; -import os from 'os'; import type http from 'http'; import type net from 'net'; import * as path from 'path'; @@ -678,9 +677,9 @@ for (const kind of ['launchServer', 'run-server'] as const) { expect(await response.json()).toEqual({ 'foo': 'bar' }); }); - test('should upload large file', async ({ connect, startRemoteServer, server, browserName, isMac, mode }, testInfo) => { + test('should upload large file', async ({ connect, startRemoteServer, server, browserName, isMac, macVersion, mode }, testInfo) => { test.skip(mode.startsWith('service'), 'Take it easy on service'); - test.skip(browserName === 'webkit' && isMac && parseInt(os.release(), 10) < 20, 'WebKit for macOS 10.15 is frozen and does not have corresponding protocol features.'); + test.skip(browserName === 'webkit' && isMac && macVersion < 11, 'WebKit for macOS 10.15 is frozen and does not have corresponding protocol features.'); test.slow(); const remoteServer = await startRemoteServer(kind); const browser = await connect(remoteServer.wsEndpoint()); diff --git a/tests/library/capabilities.spec.ts b/tests/library/capabilities.spec.ts index 20dcecfa5e..3f71c7db9e 100644 --- a/tests/library/capabilities.spec.ts +++ b/tests/library/capabilities.spec.ts @@ -19,8 +19,8 @@ import url from 'url'; import { contextTest as it, expect } from '../config/browserTest'; import { hostPlatform } from '../../packages/playwright-core/src/utils/hostPlatform'; -it('SharedArrayBuffer should work @smoke', async function({ contextFactory, httpsServer, isMac, browserName }) { - it.skip(browserName === 'webkit' && isMac && parseInt(os.release().split('.')[0], 10) <= 21, 'WebKit on macOS 12 is frozen and does not support SharedArrayBuffer'); +it('SharedArrayBuffer should work @smoke', async function({ contextFactory, httpsServer, isMac, macVersion, browserName }) { + it.skip(browserName === 'webkit' && isMac && macVersion <= 12, 'WebKit on macOS 12 is frozen and does not support SharedArrayBuffer'); const context = await contextFactory({ ignoreHTTPSErrors: true }); const page = await context.newPage(); httpsServer.setRoute('/sharedarraybuffer', (req, res) => { @@ -65,13 +65,13 @@ it('should respect CSP @smoke', async ({ page, server }) => { expect(await page.evaluate(() => window['testStatus'])).toBe('SUCCESS'); }); -it('should play video @smoke', async ({ page, asset, browserName, platform, mode }) => { +it('should play video @smoke', async ({ page, asset, browserName, platform, macVersion, mode }) => { // TODO: the test passes on Windows locally but fails on GitHub Action bot, // apparently due to a Media Pack issue in the Windows Server. // Also the test is very flaky on Linux WebKit. it.fixme(browserName === 'webkit' && platform !== 'darwin'); it.fixme(browserName === 'firefox', 'https://github.com/microsoft/playwright/issues/5721'); - it.fixme(browserName === 'webkit' && platform === 'darwin' && parseInt(os.release(), 10) === 20, 'Does not work on BigSur'); + it.fixme(browserName === 'webkit' && platform === 'darwin' && macVersion === 11, 'Does not work on BigSur'); it.skip(mode.startsWith('service')); // Safari only plays mp4 so we test WebKit with an .mp4 clip. @@ -84,8 +84,8 @@ it('should play video @smoke', async ({ page, asset, browserName, platform, mode await page.$eval('video', v => v.pause()); }); -it('should play webm video @smoke', async ({ page, asset, browserName, platform, mode }) => { - it.fixme(browserName === 'webkit' && platform === 'darwin' && parseInt(os.release(), 10) === 20, 'Does not work on BigSur'); +it('should play webm video @smoke', async ({ page, asset, browserName, platform, macVersion, mode }) => { + it.fixme(browserName === 'webkit' && platform === 'darwin' && macVersion === 11, 'Does not work on BigSur'); it.fixme(browserName === 'webkit' && platform === 'win32'); it.skip(mode.startsWith('service')); @@ -143,6 +143,8 @@ it('should not crash on showDirectoryPicker', async ({ page, server, browserName it.skip(browserName === 'chromium' && browserMajorVersion < 99, 'Fixed in Chromium r956769'); it.skip(browserName !== 'chromium', 'showDirectoryPicker is only available in Chromium'); await page.goto(server.EMPTY_PAGE); + // "User activation is required to show a file picker." - so we click first. + await page.locator('body').click(); page.evaluate(async () => { const dir = await (window as any).showDirectoryPicker(); return dir.name; @@ -240,9 +242,9 @@ it('make sure that XMLHttpRequest upload events are emitted correctly', async ({ expect(events).toEqual(['loadstart', 'progress', 'load', 'loadend']); }); -it('loading in HTMLImageElement.prototype', async ({ page, server, browserName, isMac }) => { +it('loading in HTMLImageElement.prototype', async ({ page, server, browserName, isMac, macVersion }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/22738' }); - it.skip(browserName === 'webkit' && isMac && parseInt(os.release(), 10) < 21, 'macOS 11 is frozen'); + it.skip(browserName === 'webkit' && isMac && macVersion < 12, 'macOS 11 is frozen'); await page.goto(server.EMPTY_PAGE); const defined = await page.evaluate(() => 'loading' in HTMLImageElement.prototype); expect(defined).toBeTruthy(); @@ -399,3 +401,20 @@ it('service worker should register in an iframe', async ({ page, server }) => { }); expect(response).toBe('responseFromServiceWorker'); }); + +it('should be able to render avif images', { + annotation: { + type: 'issue', + description: 'https://github.com/microsoft/playwright/issues/32673', + } +}, async ({ page, server, browserName, platform }) => { + it.fixme(browserName === 'webkit' && platform === 'win32'); + it.fixme(browserName === 'webkit' && platform === 'linux', 'https://github.com/microsoft/playwright/issues/32673'); + await page.goto(server.EMPTY_PAGE); + await page.setContent(`<img src="${server.PREFIX}/rgb.avif" onerror="window.error = true">`); + await expect.poll(() => page.locator('img').boundingBox()).toEqual(expect.objectContaining({ + width: 128, + height: 128, + })); + expect(await page.evaluate(() => (window as any).error)).toBe(undefined); +}); diff --git a/tests/library/chromium/connect-over-cdp.spec.ts b/tests/library/chromium/connect-over-cdp.spec.ts index a473539e8e..f31f60baf0 100644 --- a/tests/library/chromium/connect-over-cdp.spec.ts +++ b/tests/library/chromium/connect-over-cdp.spec.ts @@ -409,13 +409,13 @@ test('should connect to an existing cdp session when passed as a first argument' } }); -test('should use proxy with connectOverCDP', async ({ browserType, server, mode }, testInfo) => { +test('should use proxy with connectOverCDP', async ({ browserType, server }, testInfo) => { server.setRoute('/target.html', async (req, res) => { res.end('<html><title>Served by the proxy'); }); const port = 9339 + testInfo.workerIndex; const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port, ...(process.platform === 'win32' ? ['--proxy-server=some-value'] : [])] + args: ['--remote-debugging-port=' + port] }); try { const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); diff --git a/tests/library/chromium/launcher.spec.ts b/tests/library/chromium/launcher.spec.ts index eb0fc997ad..f36a089fa7 100644 --- a/tests/library/chromium/launcher.spec.ts +++ b/tests/library/chromium/launcher.spec.ts @@ -146,6 +146,27 @@ it('should support request/response events when using backgroundPage()', async ( await context.close(); }); +it('should report console messages from content script', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32762' } +}, async ({ browserType, createUserDataDir, asset, server }) => { + const userDataDir = await createUserDataDir(); + const extensionPath = asset('extension-with-logging'); + const extensionOptions = { + headless: false, + args: [ + `--disable-extensions-except=${extensionPath}`, + `--load-extension=${extensionPath}`, + ], + }; + const context = await browserType.launchPersistentContext(userDataDir, extensionOptions); + const page = await context.newPage(); + const consolePromise = page.waitForEvent('console', e => e.text().includes('Test console log from a third-party execution context')); + await page.goto(server.EMPTY_PAGE); + const message = await consolePromise; + expect(message.text()).toContain('Test console log from a third-party execution context'); + await context.close(); +}); + it('should not create pages automatically', async ({ browserType }) => { const browser = await browserType.launch(); const browserSession = await browser.newBrowserCDPSession(); diff --git a/tests/library/chromium/oopif.spec.ts b/tests/library/chromium/oopif.spec.ts index bea16bdb75..3878aa5142 100644 --- a/tests/library/chromium/oopif.spec.ts +++ b/tests/library/chromium/oopif.spec.ts @@ -269,7 +269,7 @@ it('ElementHandle.boundingBox() should work', async function({ page, browser, se await assertOOPIFCount(browser, 1); const handle1 = await page.frames()[1].$('.box:nth-of-type(13)'); - expect(await handle1!.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 }); + await expect.poll(() => handle1!.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 }); await Promise.all([ page.frames()[1].waitForNavigation(), @@ -277,7 +277,7 @@ it('ElementHandle.boundingBox() should work', async function({ page, browser, se ]); await assertOOPIFCount(browser, 0); const handle2 = await page.frames()[1].$('.box:nth-of-type(13)'); - expect(await handle2!.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 }); + await expect.poll(() => handle2!.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 }); }); it('should click', async function({ page, browser, server }) { diff --git a/tests/library/client-certificates.spec.ts b/tests/library/client-certificates.spec.ts index 156d80adfb..cadf49deb9 100644 --- a/tests/library/client-certificates.spec.ts +++ b/tests/library/client-certificates.spec.ts @@ -28,7 +28,6 @@ const { createHttpsServer, createHttp2Server } = require('../../packages/playwri type TestOptions = { startCCServer(options?: { - host?: string; http2?: boolean; enableHTTP1FallbackWhenUsingHttp2?: boolean; useFakeLocalhost?: boolean; @@ -68,8 +67,8 @@ const test = base.extend({ } res.end(parts.map(({ key, value }) => `
${value}
`).join('')); }); - await new Promise(f => server.listen(0, options?.host ?? 'localhost', () => f())); - const host = options?.useFakeLocalhost ? 'local.playwright' : 'localhost'; + await new Promise(f => server.listen(0, '127.0.0.1', () => f())); + const host = options?.useFakeLocalhost ? 'local.playwright' : '127.0.0.1'; return `https://${host}:${(server.address() as net.AddressInfo).port}/`; }); if (server) @@ -195,7 +194,7 @@ test.describe('fetch', () => { }); test('pass with trusted client certificates and when a socks proxy is used', async ({ playwright, startCCServer, asset }) => { - const serverURL = await startCCServer({ host: '127.0.0.1' }); + const serverURL = await startCCServer(); const serverPort = parseInt(new URL(serverURL).port, 10); const { proxyServerAddr, closeProxyServer, connectHosts } = await setupSocksForwardingServer({ port: test.info().workerIndex + 2048 + 2, @@ -292,8 +291,8 @@ test.describe('browser', () => { await page.close(); }); - test('should fail with no client certificates', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should fail with no client certificates', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const page = await browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -307,8 +306,8 @@ test.describe('browser', () => { await page.close(); }); - test('should fail with self-signed client certificates', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should fail with self-signed client certificates', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const page = await browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -322,8 +321,8 @@ test.describe('browser', () => { await page.close(); }); - test('should pass with matching certificates', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should pass with matching certificates', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const page = await browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -337,8 +336,8 @@ test.describe('browser', () => { await page.close(); }); - test('should pass with matching certificates when passing as content', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should pass with matching certificates when passing as content', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const page = await browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -352,8 +351,8 @@ test.describe('browser', () => { await page.close(); }); - test('should pass with matching certificates and when a http proxy is used', async ({ browser, startCCServer, asset, browserName, proxyServer }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should pass with matching certificates and when a http proxy is used', async ({ browser, startCCServer, asset, browserName, proxyServer, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); proxyServer.forwardTo(parseInt(new URL(serverURL).port, 10), { allowConnectRequests: true }); const page = await browser.newPage({ ignoreHTTPSErrors: true, @@ -366,13 +365,14 @@ test.describe('browser', () => { }); expect(proxyServer.connectHosts).toEqual([]); await page.goto(serverURL); - expect([...new Set(proxyServer.connectHosts)]).toEqual([`localhost:${new URL(serverURL).port}`]); + const host = browserName === 'webkit' && isMac ? 'localhost' : '127.0.0.1'; + expect([...new Set(proxyServer.connectHosts)]).toEqual([`${host}:${new URL(serverURL).port}`]); await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!'); await page.close(); }); - test('should pass with matching certificates and when a socks proxy is used', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin', host: '127.0.0.1' }); + test('should pass with matching certificates and when a socks proxy is used', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const serverPort = parseInt(new URL(serverURL).port, 10); const { proxyServerAddr, closeProxyServer, connectHosts } = await setupSocksForwardingServer({ port: test.info().workerIndex + 2048 + 2, @@ -390,7 +390,8 @@ test.describe('browser', () => { }); expect(connectHosts).toEqual([]); await page.goto(serverURL); - expect(connectHosts).toEqual([`localhost:${serverPort}`]); + const host = browserName === 'webkit' && isMac ? 'localhost' : '127.0.0.1'; + expect(connectHosts).toEqual([`${host}:${serverPort}`]); await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!'); await page.close(); await closeProxyServer(); @@ -438,8 +439,8 @@ test.describe('browser', () => { } }); - test('should pass with matching certificates in pfx format', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should pass with matching certificates in pfx format', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const page = await browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -513,7 +514,7 @@ test.describe('browser', () => { const stylesheet = ` button { background-color: red; - } + } `; const stylesheetBuffer = await new Promise((resolve, reject) => { @@ -585,8 +586,8 @@ test.describe('browser', () => { await new Promise(resolve => server.close(() => resolve())); }); - test('should pass with matching certificates in pfx format when passing as content', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should pass with matching certificates in pfx format when passing as content', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const page = await browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -600,8 +601,8 @@ test.describe('browser', () => { await page.close(); }); - test('should fail with matching certificates in legacy pfx format', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should fail with matching certificates in legacy pfx format', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); await expect(browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -612,8 +613,8 @@ test.describe('browser', () => { })).rejects.toThrow('Unsupported TLS certificate'); }); - test('should throw a http error if the pfx passphrase is incorect', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should throw a http error if the pfx passphrase is incorect', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); await expect(browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -625,7 +626,7 @@ test.describe('browser', () => { }); test('should pass with matching certificates on context APIRequestContext instance', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ host: '127.0.0.1' }); + const serverURL = await startCCServer(); const baseOptions = { certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'), @@ -648,8 +649,8 @@ test.describe('browser', () => { await page.close(); }); - test('should pass with matching certificates and trailing slash', async ({ browser, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should pass with matching certificates and trailing slash', async ({ browser, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const page = await browser.newPage({ ignoreHTTPSErrors: true, clientCertificates: [{ @@ -676,8 +677,8 @@ test.describe('browser', () => { await page.close(); }); - test('support http2', async ({ browser, startCCServer, asset, browserName }) => { - test.skip(browserName === 'webkit' && process.platform === 'darwin', 'WebKit on macOS doesn\n proxy localhost'); + test('support http2', async ({ browser, startCCServer, asset, browserName, isMac }) => { + test.skip(browserName === 'webkit' && isMac, 'WebKit on macOS doesn\n proxy localhost'); const serverURL = await startCCServer({ http2: true }); const page = await browser.newPage({ ignoreHTTPSErrors: true, @@ -688,7 +689,7 @@ test.describe('browser', () => { }], }); { - await page.goto(serverURL.replace('localhost', 'local.playwright')); + await page.goto(serverURL.replace('127.0.0.1', 'local.playwright')); await expect(page.getByTestId('message')).toHaveText('Sorry, but you need to provide a client certificate to continue.'); await expect(page.getByTestId('alpn-protocol')).toHaveText('h2'); await expect(page.getByTestId('servername')).toHaveText('local.playwright'); @@ -714,7 +715,7 @@ test.describe('browser', () => { }], }); { - await page.goto(serverURL.replace('localhost', 'local.playwright')); + await page.goto(serverURL.replace('127.0.0.1', 'local.playwright')); await expect(page.getByTestId('message')).toHaveText('Sorry, but you need to provide a client certificate to continue.'); await expect(page.getByTestId('alpn-protocol')).toHaveText('http/1.1'); } @@ -726,9 +727,9 @@ test.describe('browser', () => { await browser.close(); }); - test('should return target connection errors when using http2', async ({ browser, startCCServer, asset, browserName }) => { - test.skip(browserName === 'webkit' && process.platform === 'darwin', 'WebKit on macOS doesn\n proxy localhost'); - test.fixme(browserName === 'webkit' && process.platform === 'linux', 'WebKit on Linux does not support http2 https://bugs.webkit.org/show_bug.cgi?id=276990'); + test('should return target connection errors when using http2', async ({ browser, startCCServer, asset, browserName, isMac, isLinux }) => { + test.skip(browserName === 'webkit' && isMac, 'WebKit on macOS doesn\n proxy localhost'); + test.fixme(browserName === 'webkit' && isLinux, 'WebKit on Linux does not support http2 https://bugs.webkit.org/show_bug.cgi?id=276990'); test.skip(+process.versions.node.split('.')[0] < 20, 'http2.performServerHandshake is not supported in older Node.js versions'); const serverURL = await startCCServer({ http2: true }); @@ -784,8 +785,8 @@ test.describe('browser', () => { await expect(launchPersistent(contextOptions)).rejects.toThrow(expected); }); - test('should pass with matching certificates', async ({ launchPersistent, startCCServer, asset, browserName }) => { - const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + test('should pass with matching certificates', async ({ launchPersistent, startCCServer, asset, browserName, isMac }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const { page } = await launchPersistent({ ignoreHTTPSErrors: true, clientCertificates: [{ diff --git a/tests/library/debug-controller.spec.ts b/tests/library/debug-controller.spec.ts index 2da0ee1bf3..cdc579fed2 100644 --- a/tests/library/debug-controller.spec.ts +++ b/tests/library/debug-controller.spec.ts @@ -188,9 +188,9 @@ test('test', async ({ page }) => { await page.getByRole('button', { name: 'Submit' }).click(); });` }); - const length = events.length; // No events after mode disabled await backend.setRecorderMode({ mode: 'none' }); + const length = events.length; await page.getByRole('button').click(); expect(events).toHaveLength(length); }); diff --git a/tests/library/emulation-focus.spec.ts b/tests/library/emulation-focus.spec.ts index 27ad72127e..5f6b5b741f 100644 --- a/tests/library/emulation-focus.spec.ts +++ b/tests/library/emulation-focus.spec.ts @@ -104,8 +104,6 @@ it('should change document.activeElement', async ({ page, server }) => { it('should not affect screenshots', async ({ page, server, browserName, headless, isWindows }) => { it.skip(browserName === 'webkit' && isWindows && !headless, 'WebKit/Windows/headed has a larger minimal viewport. See https://github.com/microsoft/playwright/issues/22616'); it.skip(browserName === 'firefox' && !headless, 'Firefox headed produces a different image'); - const isChromiumHeadlessNew = browserName === 'chromium' && !!headless && !!process.env.PLAYWRIGHT_CHROMIUM_USE_HEADLESS_NEW; - it.fixme(isChromiumHeadlessNew, 'Times out with --headless=new'); const page2 = await page.context().newPage(); await Promise.all([ @@ -199,8 +197,9 @@ browserTest('should not fire blur events when interacting with more than one pag expect(await page2.evaluate(() => !!window['gotBlur'])).toBe(false); }); -browserTest('should trigger hover state concurrently', async ({ browserType, browserName }) => { +browserTest('should trigger hover state concurrently', async ({ browserType, browserName, headless }) => { browserTest.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/27969' }); + browserTest.skip(!headless, 'headed messes up with hover'); browserTest.fixme(browserName === 'firefox'); const browser1 = await browserType.launch(); diff --git a/tests/library/fetch-proxy.spec.ts b/tests/library/fetch-proxy.spec.ts index f6961e6de5..149bd2f2b1 100644 --- a/tests/library/fetch-proxy.spec.ts +++ b/tests/library/fetch-proxy.spec.ts @@ -20,10 +20,15 @@ it.skip(({ mode }) => mode !== 'default'); it('context request should pick up proxy credentials', async ({ browserType, server, proxyServer }) => { proxyServer.forwardTo(server.PORT, { allowConnectRequests: true }); - let auth; - proxyServer.setAuthHandler(req => { - auth = req.headers['proxy-authorization']; - return !!auth; + const authPromise = new Promise(resolve => { + proxyServer.setAuthHandler(req => { + const header = req.headers['proxy-authorization']; + // Browser can issue various unrelated requests over the proxy, + // but we are only interested in our own request. + if (proxyServer.connectHosts.includes('non-existent.com:80')) + resolve(header); + return !!header; + }); }); const browser = await browserType.launch({ proxy: { server: `localhost:${proxyServer.PORT}`, username: 'user', password: 'secret' } @@ -31,6 +36,7 @@ it('context request should pick up proxy credentials', async ({ browserType, ser const context = await browser.newContext(); const response = await context.request.get('http://non-existent.com/simple.json'); expect(proxyServer.connectHosts).toContain('non-existent.com:80'); + const auth = await authPromise; expect(auth).toBe('Basic ' + Buffer.from('user:secret').toString('base64')); expect(await response.json()).toEqual({ foo: 'bar' }); await browser.close(); diff --git a/tests/library/global-fetch.spec.ts b/tests/library/global-fetch.spec.ts index 5de98fe9ee..52d48c765c 100644 --- a/tests/library/global-fetch.spec.ts +++ b/tests/library/global-fetch.spec.ts @@ -220,6 +220,13 @@ it('should resolve url relative to global baseURL option', async ({ playwright, await request.dispose(); }); +it('should fallback to given URL if baseURL is bogus', async ({ playwright, server }) => { + const request = await playwright.request.newContext({ baseURL: 'bogus' }); + const response = await request.get(server.PREFIX + '/empty.html'); + expect(response.url()).toBe(server.EMPTY_PAGE); + await request.dispose(); +}); + it('should set playwright as user-agent', async ({ playwright, server, isWindows, isLinux, isMac }) => { const request = await playwright.request.newContext(); const [serverRequest] = await Promise.all([ diff --git a/tests/library/har.spec.ts b/tests/library/har.spec.ts index 29ee7df2d1..bbd4dcc49b 100644 --- a/tests/library/har.spec.ts +++ b/tests/library/har.spec.ts @@ -24,9 +24,9 @@ import type { Log } from '../../packages/trace/src/har'; import { parseHar } from '../config/utils'; const { createHttp2Server } = require('../../packages/playwright-core/lib/utils'); -async function pageWithHar(contextFactory: (options?: BrowserContextOptions) => Promise, testInfo: any, options: { outputPath?: string, content?: 'embed' | 'attach' | 'omit', omitContent?: boolean } = {}) { +async function pageWithHar(contextFactory: (options?: BrowserContextOptions) => Promise, testInfo: any, options: { outputPath?: string } & Partial> = {}) { const harPath = testInfo.outputPath(options.outputPath || 'test.har'); - const context = await contextFactory({ recordHar: { path: harPath, content: options.content, omitContent: options.omitContent }, ignoreHTTPSErrors: true }); + const context = await contextFactory({ recordHar: { path: harPath, ...options }, ignoreHTTPSErrors: true }); const page = await context.newPage(); return { page, @@ -610,6 +610,7 @@ it('should have security details', async ({ contextFactory, httpsServer, browser const { page, getLog } = await pageWithHar(contextFactory, testInfo); await page.goto(httpsServer.EMPTY_PAGE); + await page.request.get(httpsServer.EMPTY_PAGE); const log = await getLog(); const { serverIPAddress, _serverPort: port, _securityDetails: securityDetails } = log.entries[0]; if (!mode.startsWith('service')) { @@ -620,6 +621,8 @@ it('should have security details', async ({ contextFactory, httpsServer, browser expect(securityDetails).toEqual({ protocol: 'TLS 1.3', subjectName: 'playwright-test', validFrom: 1691708270, validTo: 2007068270 }); else expect(securityDetails).toEqual({ issuer: 'playwright-test', protocol: 'TLS 1.3', subjectName: 'playwright-test', validFrom: 1691708270, validTo: 2007068270 }); + + expect(log.entries[1]._securityDetails).toEqual({ issuer: 'playwright-test', protocol: 'TLSv1.3', subjectName: 'playwright-test', validFrom: 1691708270, validTo: 2007068270 }); }); it('should have connection details for redirects', async ({ contextFactory, server, browserName, mode }, testInfo) => { @@ -820,6 +823,56 @@ it('should include API request', async ({ contextFactory, server }, testInfo) => expect(entry.response.headers.find(h => h.name.toLowerCase() === 'content-type')?.value).toContain('application/json'); expect(entry.response.content.size).toBe(15); expect(entry.response.content.text).toBe(responseBody.toString()); + expect(entry.response.bodySize).toBe(15); + + expect(entry.time).toBeGreaterThan(0); + expect(entry.timings).toEqual(expect.objectContaining({ + blocked: -1, + connect: expect.any(Number), + dns: expect.any(Number), + receive: expect.any(Number), + send: expect.any(Number), + ssl: expect.any(Number), + wait: expect.any(Number), + })); + + expect(entry.serverIPAddress).toBeDefined(); + expect(entry._serverPort).toEqual(server.PORT); +}); + +it('should respect minimal mode for API Requests', async ({ contextFactory, server }, testInfo) => { + const { page, getLog } = await pageWithHar(contextFactory, testInfo, { mode: 'minimal' }); + const url = server.PREFIX + '/simple.json'; + await page.request.post(url, { + headers: { cookie: 'a=b; c=d' }, + data: { foo: 'bar' } + }); + const { entries } = await getLog(); + expect(entries).toHaveLength(1); + const [entry] = entries; + expect(entry.timings).toEqual({ receive: -1, send: -1, wait: -1 }); + expect(entry.serverIPAddress).toBeUndefined(); + expect(entry._serverPort).toBeUndefined(); + expect(entry.request.cookies).toEqual([]); + expect(entry.request.bodySize).toBe(-1); + expect(entry.response.bodySize).toBe(-1); +}); + +it('should include redirects from API request', async ({ contextFactory, server }, testInfo) => { + server.setRedirect('/redirect-me', '/simple.json'); + const { page, getLog } = await pageWithHar(contextFactory, testInfo); + await page.request.post(server.PREFIX + '/redirect-me', { + headers: { cookie: 'a=b; c=d' }, + data: { foo: 'bar' } + }); + const log = await getLog(); + expect(log.entries.length).toBe(2); + const [redirect, json] = log.entries; + expect(redirect.request.url).toBe(server.PREFIX + '/redirect-me'); + expect(json.request.url).toBe(server.PREFIX + '/simple.json'); + + expect(redirect.timings).toBeDefined(); + expect(json.timings).toBeDefined(); }); it('should not hang on resources served from cache', async ({ contextFactory, server, browserName }, testInfo) => { diff --git a/tests/library/inspector/cli-codegen-1.spec.ts b/tests/library/inspector/cli-codegen-1.spec.ts index c8046ef9b4..a876a25e47 100644 --- a/tests/library/inspector/cli-codegen-1.spec.ts +++ b/tests/library/inspector/cli-codegen-1.spec.ts @@ -19,9 +19,10 @@ import type { ConsoleMessage } from 'playwright'; test.describe('cli codegen', () => { test.skip(({ mode }) => mode !== 'default'); + test.skip(({ trace, codegenMode }) => trace === 'on' && codegenMode === 'trace-events'); - test('should click', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should click', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -52,8 +53,8 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync();`) expect(message.text()).toBe('click'); }); - test('should double click', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should double click', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -93,8 +94,8 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).DblClickAsync() ]); }); - test('should ignore programmatic events', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should ignore programmatic events', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -113,8 +114,8 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).DblClickAsync() expect(clicks.length).toBe(1); }); - test('should click after same-document navigation', async ({ page, openRecorder, server }) => { - const recorder = await openRecorder(); + test('should click after same-document navigation', async ({ openRecorder, server }) => { + const { page, recorder } = await openRecorder(); server.setRoute('/foo.html', (req, res) => { res.setHeader('Content-Type', 'text/html; charset=utf-8'); @@ -143,8 +144,8 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).DblClickAsync() expect(message.text()).toBe('click'); }); - test('should make a positioned click on a canvas', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should make a positioned click on a canvas', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(` @@ -196,8 +197,8 @@ await page.Locator("canvas").ClickAsync(new LocatorClickOptions expect(message.text()).toBe('click 250 250'); }); - test('should work with TrustedTypes', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should work with TrustedTypes', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(` @@ -234,8 +235,8 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync();`) expect(message.text()).toBe('click'); }); - test('should not target selector preview by text regexp', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should not target selector preview by text regexp', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(`dummy`); @@ -265,8 +266,8 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync();`) expect(message.text()).toBe('click'); }); - test('should fill', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should fill', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); const locator = await recorder.focusElement('input'); @@ -295,8 +296,8 @@ await page.Locator("#input").FillAsync(\"John\");`); expect(message.text()).toBe('John'); }); - test('should fill japanese text', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should fill japanese text', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); // In Japanese, "てすと" or "テスト" means "test". await recorder.setContentAndWait(``); @@ -329,8 +330,8 @@ await page.Locator("#input").FillAsync(\"てすと\");`); expect(message.text()).toBe('てすと'); }); - test('should fill textarea', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should fill textarea', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); const locator = await recorder.focusElement('textarea'); @@ -346,9 +347,9 @@ await page.Locator("#input").FillAsync(\"てすと\");`); expect(message.text()).toBe('John'); }); - test('should fill textarea with new lines at the end', async ({ page, openRecorder }) => { + test('should fill textarea with new lines at the end', async ({ openRecorder }) => { test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23774' }); - const recorder = await openRecorder(); + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); const textarea = page.locator('textarea'); await textarea.evaluate(e => e.addEventListener('input', () => (window as any).lastInputValue = e.value)); @@ -361,8 +362,8 @@ await page.Locator("#input").FillAsync(\"てすと\");`); expect(sources.get('JavaScript')!.text).not.toContain(`Enter`); }); - test('should fill [contentEditable]', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should fill [contentEditable]', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(`
`); const locator = await recorder.focusElement('div'); @@ -378,8 +379,8 @@ await page.Locator("#input").FillAsync(\"てすと\");`); expect(message.text()).toBe('John Doe'); }); - test('should press', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should press', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -412,23 +413,40 @@ await page.GetByRole(AriaRole.Textbox).PressAsync("Shift+Enter");`); expect(messages[0].text()).toBe('press'); }); - test('should update selected element after pressing Tab', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should update selected element after pressing Tab', async ({ openRecorder, browserName, codegenMode }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(` `); - await page.click('input[name="one"]'); - await recorder.waitForOutput('JavaScript', 'click'); - await page.keyboard.type('foobar123'); - await recorder.waitForOutput('JavaScript', 'foobar123'); + const input1 = page.locator('input[name="one"]'); + const input2 = page.locator('input[name="two"]'); - await page.keyboard.press('Tab'); - await recorder.waitForOutput('JavaScript', 'Tab'); - await page.keyboard.type('barfoo321'); - await recorder.waitForOutput('JavaScript', 'barfoo321'); + { + await input1.click(); + await recorder.waitForOutput('JavaScript', 'click'); + await expect(input1).toBeFocused(); + } + + { + await page.keyboard.type('foobar123'); + await recorder.waitForOutput('JavaScript', 'foobar123'); + await expect(input1).toHaveValue('foobar123'); + } + + { + await page.keyboard.press('Tab'); + await recorder.waitForOutput('JavaScript', 'Tab'); + await expect(input2).toBeFocused(); + } + + { + await page.keyboard.type('barfoo321'); + await recorder.waitForOutput('JavaScript', 'barfoo321'); + await expect(input2).toHaveValue('barfoo321'); + } const text = recorder.sources().get('JavaScript')!.text; expect(text).toContain(` @@ -441,8 +459,8 @@ await page.GetByRole(AriaRole.Textbox).PressAsync("Shift+Enter");`); await page.locator('input[name="two"]').fill('barfoo321');`); }); - test('should record ArrowDown', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should record ArrowDown', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -463,8 +481,8 @@ await page.GetByRole(AriaRole.Textbox).PressAsync("Shift+Enter");`); expect(messages[0].text()).toBe('press:ArrowDown'); }); - test('should emit single keyup on ArrowDown', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should emit single keyup on ArrowDown', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -488,8 +506,8 @@ await page.GetByRole(AriaRole.Textbox).PressAsync("Shift+Enter");`); expect(messages[1].text()).toBe('up:ArrowDown'); }); - test('should check', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should check', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -520,8 +538,8 @@ await page.Locator("#checkbox").CheckAsync();`); expect(message.text()).toBe('true'); }); - test('should check a radio button', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should check a radio button', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -539,8 +557,8 @@ await page.Locator("#checkbox").CheckAsync();`); expect(message.text()).toBe('true'); }); - test('should check with keyboard', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should check with keyboard', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -558,8 +576,8 @@ await page.Locator("#checkbox").CheckAsync();`); expect(message.text()).toBe('true'); }); - test('should uncheck', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should uncheck', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(``); @@ -590,8 +608,8 @@ await page.Locator("#checkbox").UncheckAsync();`); expect(message.text()).toBe('false'); }); - test('should select', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should select', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(''); @@ -623,8 +641,8 @@ await page.Locator("#age").SelectOptionAsync(new[] { "2" });`); expect(message.text()).toBe('2'); }); - test('should select with size attribute', async ({ page, openRecorder }) => { - const recorder = await openRecorder(); + test('should select with size attribute', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); await recorder.setContentAndWait(`