diff --git a/playwright/.eslintignore b/playwright/.eslintignore index 9400c2637d..152cbbe228 100644 --- a/playwright/.eslintignore +++ b/playwright/.eslintignore @@ -1,4 +1,5 @@ test/assets/modernizr.js +/tests/third_party/ /packages/*/lib/ *.js /packages/playwright-core/src/generated/* @@ -18,4 +19,5 @@ tests/components/ tests/installation/fixture-scripts/ examples/ DEPS -.cache/ \ No newline at end of file +.cache/ +utils/ diff --git a/playwright/.eslintrc-with-ts-config.js b/playwright/.eslintrc-with-ts-config.js new file mode 100644 index 0000000000..b06ec00195 --- /dev/null +++ b/playwright/.eslintrc-with-ts-config.js @@ -0,0 +1,15 @@ +module.exports = { + extends: "./.eslintrc.js", + parserOptions: { + ecmaVersion: 9, + sourceType: "module", + project: "./tsconfig.json", + }, + rules: { + "@typescript-eslint/no-base-to-string": "error", + "@typescript-eslint/no-unnecessary-boolean-literal-compare": 2, + }, + parserOptions: { + project: "./tsconfig.json" + }, +}; diff --git a/playwright/.eslintrc.js b/playwright/.eslintrc.js index 7bc5a0868f..bff9ffeeb4 100644 --- a/playwright/.eslintrc.js +++ b/playwright/.eslintrc.js @@ -48,6 +48,7 @@ module.exports = { "arrow-parens": [2, "as-needed"], "prefer-const": 2, "quote-props": [2, "consistent"], + "nonblock-statement-body-position": [2, "below"], // anti-patterns "no-var": 2, diff --git a/playwright/.github/ISSUE_TEMPLATE/bug.md b/playwright/.github/ISSUE_TEMPLATE/bug.md deleted file mode 100644 index 662784b912..0000000000 --- a/playwright/.github/ISSUE_TEMPLATE/bug.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Bug Report -about: Something doesn't work like it should? Tell us! -title: "[BUG]" -labels: '' -assignees: '' - ---- - -### System info -- Playwright Version: [v1.XX] -- Operating System: [All, Windows 11, Ubuntu 20, macOS 13.2, etc.] -- Browser: [All, Chromium, Firefox, WebKit] -- Other info: - -### Source code - -[We will only be able to work on the issues that we can reproduce.] - -[Please provide a self-contained example in a form of a snippet, archive or a repository.] - -[Repro scenario can't be large or have external dependencies.] - -**Steps** -- [Run the test] -- [...] - -**Expected** - -[Describe expected behavior] - -**Actual** - -[Describe actual behavior] diff --git a/playwright/.github/ISSUE_TEMPLATE/bug.yml b/playwright/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000000..062d7c7e74 --- /dev/null +++ b/playwright/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,100 @@ +name: Bug Report 🪲 +description: Create a bug report to help us improve +title: '[Bug]: ' +body: + - type: markdown + attributes: + value: | + # Please follow these steps first: + - type: markdown + attributes: + value: | + ## Troubleshoot + If Playwright is not behaving the way you expect, we'd ask you to look at the [documentation](https://playwright.dev/docs/intro) and search the issue tracker for evidence supporting your expectation. + Please make reasonable efforts to troubleshoot and rule out issues with your code, the configuration, or any 3rd party libraries you might be using. + Playwright offers [several debugging tools](https://playwright.dev/docs/debug) that you can use to troubleshoot your issues. + - type: markdown + attributes: + value: | + ## Ask for help through appropriate channels + If you feel unsure about the cause of the problem, consider asking for help on for example [StackOverflow](https://stackoverflow.com/questions/ask) or our [Discord channel](https://aka.ms/playwright/discord) before posting a bug report. The issue tracker is not a help forum. + - type: markdown + attributes: + value: | + ## Make a minimal reproduction + To file the report, you will need a GitHub repository with a minimal (but complete) example and simple/clear steps on how to reproduce the bug. + The simpler you can make it, the more likely we are to successfully verify and fix the bug. You can create a new project with `npm init playwright@latest new-project` and then add the test code there. + - type: markdown + attributes: + value: | + > [!IMPORTANT] + > Bug reports without a minimal reproduction will be rejected. + + --- + - type: input + id: version + attributes: + label: Version + description: | + The version of Playwright you are using. + Is it the [latest](https://github.com/microsoft/playwright/releases)? Test and see if the bug has already been fixed. + placeholder: ex. 1.41.1 + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce + description: Please link to a repository with a minimal reproduction and describe accurately how we can reproduce/verify the bug. + placeholder: | + Example steps (replace with your own): + 1. Clone my repo at https://github.com//example + 2. npm install + 3. npm run test + 4. You should see the error come up + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: A description of what you expect to happen. + placeholder: I expect to see X or Y + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: Actual behavior + description: | + A clear and concise description of the unexpected behavior. + Please include any relevant output here, especially any error messages. + placeholder: A bug happened! + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional context + description: Anything else that might be relevant + validations: + required: false + - type: textarea + id: envinfo + attributes: + label: Environment + description: | + Please paste the output of running `npx envinfo --preset playwright`. + This will be automatically formatted as a code block, so no need for backticks. + placeholder: | + System: + OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish) + CPU: (8) arm64 + Binaries: + Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node + npm: 10.2.3 - ~/.nvm/versions/node/v18.19.0/bin/npm + npmPackages: + @playwright/test: 1.41.1 => 1.41.1 + render: shell + validations: + required: true diff --git a/playwright/.github/ISSUE_TEMPLATE/config.yml b/playwright/.github/ISSUE_TEMPLATE/config.yml index de71858775..5d9345c265 100644 --- a/playwright/.github/ISSUE_TEMPLATE/config.yml +++ b/playwright/.github/ISSUE_TEMPLATE/config.yml @@ -1,3 +1,4 @@ +blank_issues_enabled: false contact_links: - name: Join our Discord Server url: https://aka.ms/playwright/discord diff --git a/playwright/.github/ISSUE_TEMPLATE/documentation.yml b/playwright/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 0000000000..0a683969a6 --- /dev/null +++ b/playwright/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,29 @@ +name: Documentation 📖 +description: Submit a request to add or update documentation +title: '[Docs]: ' +labels: ['Documentation :book:'] +body: + - type: markdown + attributes: + value: | + ### Thank you for helping us improve our documentation! + Please be sure you are looking at [the Next version of the documentation](https://playwright.dev/docs/next/intro) before opening an issue here. + - type: textarea + id: links + attributes: + label: Page(s) + description: | + Links to one or more documentation pages that should be modified. + If you are reporting an issue with a specific section of a page, try to link directly to the nearest anchor. + If you are suggesting that a new page be created, link to the parent of the proposed page. + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: | + Describe the change you are requesting. + If the issue pertains to a single function or matcher, be sure to specify the entire call signature. + validations: + required: true diff --git a/playwright/.github/ISSUE_TEMPLATE/feature.yml b/playwright/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000000..efec3315c1 --- /dev/null +++ b/playwright/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,30 @@ +name: Feature Request 🚀 +description: Submit a proposal for a new feature +title: '[Feature]: ' +body: + - type: markdown + attributes: + value: | + ### Thank you for taking the time to suggest a new feature! + - type: textarea + id: description + attributes: + label: '🚀 Feature Request' + description: A clear and concise description of what the feature is. + validations: + required: true + - type: textarea + id: example + attributes: + label: Example + description: Describe how this feature would be used. + validations: + required: false + - type: textarea + id: motivation + attributes: + label: Motivation + description: | + Outline your motivation for the proposal. How will it make Playwright better? + validations: + required: true diff --git a/playwright/.github/ISSUE_TEMPLATE/feature_request.md b/playwright/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 37ec8a7d25..0000000000 --- a/playwright/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: Feature request -about: Request new features to be added -title: "[Feature]" -labels: '' -assignees: '' - ---- - -Let us know what functionality you'd like to see in Playwright and what your use case is. -Do you think others might benefit from this as well? diff --git a/playwright/.github/ISSUE_TEMPLATE/question.yml b/playwright/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 0000000000..9615afdc8a --- /dev/null +++ b/playwright/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,27 @@ +name: 'Questions / Help 💬' +description: If you have questions, please check StackOverflow or Discord +title: '[Please read the message below]' +labels: [':speech_balloon: Question'] +body: + - type: markdown + attributes: + value: | + ## Questions and Help 💬 + + This issue tracker is reserved for bug reports and feature requests. + + For anything else, such as questions or getting help, please see: + + - [The Playwright documentation](https://playwright.dev) + - [Our Discord server](https://aka.ms/playwright/discord) + - type: checkboxes + id: no-post + attributes: + label: | + Please do not submit this issue. + description: | + > [!IMPORTANT] + > This issue will be closed. + options: + - label: I understand + required: true diff --git a/playwright/.github/ISSUE_TEMPLATE/regression.md b/playwright/.github/ISSUE_TEMPLATE/regression.md deleted file mode 100644 index d6d7510a4c..0000000000 --- a/playwright/.github/ISSUE_TEMPLATE/regression.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Report regression -about: Functionality that used to work and does not any more -title: "[REGRESSION]: " -labels: '' -assignees: '' - ---- - -**Context:** -- GOOD Playwright Version: [what Playwright version worked nicely?] -- BAD Playwright Version: [what Playwright version doesn't work any more?] -- Operating System: [e.g. Windows, Linux or Mac] -- Extra: [any specific details about your environment] - -**Code Snippet** - -Help us help you! Put down a short code snippet that illustrates your bug and -that we can run and debug locally. For example: - -```javascript -const {chromium, webkit, firefox} = require('playwright'); - -(async () => { - const browser = await chromium.launch(); - // ... -})(); -``` - -**Describe the bug** - -Add any other details about the problem here. diff --git a/playwright/.github/ISSUE_TEMPLATE/regression.yml b/playwright/.github/ISSUE_TEMPLATE/regression.yml new file mode 100644 index 0000000000..bc0a101502 --- /dev/null +++ b/playwright/.github/ISSUE_TEMPLATE/regression.yml @@ -0,0 +1,96 @@ +name: Report regression +description: Functionality that used to work and does not any more +title: "[Regression]: " + +body: + - type: markdown + attributes: + value: | + # Please follow these steps first: + - type: markdown + attributes: + value: | + ## Make a minimal reproduction + To file the report, you will need a GitHub repository with a minimal (but complete) example and simple/clear steps on how to reproduce the regression. + The simpler you can make it, the more likely we are to successfully verify and fix the regression. + - type: markdown + attributes: + value: | + > [!IMPORTANT] + > Regression reports without a minimal reproduction will be rejected. + + --- + - type: input + id: goodVersion + attributes: + label: Last Good Version + description: | + Last version of Playwright where the feature was working. + placeholder: ex. 1.40.1 + validations: + required: true + - type: input + id: badVersion + attributes: + label: First Bad Version + description: | + First version of Playwright where the feature was broken. + Is it the [latest](https://github.com/microsoft/playwright/releases)? Test and see if the regression has already been fixed. + placeholder: ex. 1.41.1 + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce + description: Please link to a repository with a minimal reproduction and describe accurately how we can reproduce/verify the bug. + placeholder: | + Example steps (replace with your own): + 1. Clone my repo at https://github.com//example + 2. npm install + 3. npm run test + 4. You should see the error come up + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: A description of what you expect to happen. + placeholder: I expect to see X or Y + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: Actual behavior + description: A clear and concise description of the unexpected behavior. + placeholder: A bug happened! + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional context + description: Anything else that might be relevant + validations: + required: false + - type: textarea + id: envinfo + attributes: + label: Environment + description: | + Please paste the output of running `npx envinfo --preset playwright`. + This will be automatically formatted as a code block, so no need for backticks. + placeholder: | + System: + OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish) + CPU: (8) arm64 + Binaries: + Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node + npm: 10.2.3 - ~/.nvm/versions/node/v18.19.0/bin/npm + npmPackages: + @playwright/test: 1.41.1 => 1.41.1 + render: shell + validations: + required: true diff --git a/playwright/.github/ISSUE_TEMPLATE/vscode-extension.md b/playwright/.github/ISSUE_TEMPLATE/vscode-extension.md deleted file mode 100644 index e623fa9de1..0000000000 --- a/playwright/.github/ISSUE_TEMPLATE/vscode-extension.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -name: VSCode extension bug -about: Something doesn't work like it should inside the Visual Studio Code extension or you have a feature request? Tell us! -title: "[BUG]" -labels: '' -assignees: '' - ---- - -**Context:** -- Playwright Version: [what Playwright version do you use?] -- Operating System: [e.g. Windows, Linux or Mac] -- Node.js version: [e.g. 12.22, 14.6] -- Visual Studio Code version: [e.g. 1.65] -- Playwright for VSCode extension version: [e.g. 1.2.3] -- Browser: [e.g. All, Chromium, Firefox, WebKit] -- Extra: [any specific details about your environment] - -**Code Snippet** - -Help us help you! Put down a short code snippet that illustrates your bug and -that we can run and debug locally. For example: - -**Describe the bug** - -Add any other details about the problem here. diff --git a/playwright/.github/actions/download-artifact/action.yml b/playwright/.github/actions/download-artifact/action.yml index fdcb87b350..d279df5f8d 100644 --- a/playwright/.github/actions/download-artifact/action.yml +++ b/playwright/.github/actions/download-artifact/action.yml @@ -18,17 +18,17 @@ runs: shell: bash run: mkdir -p '${{ inputs.path }}/artifacts' - name: Download artifacts - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | console.log(`downloading artifacts for workflow_run: ${context.payload.workflow_run.id}`); console.log(`workflow_run: ${JSON.stringify(context.payload.workflow_run, null, 2)}`); - const { data } = await github.rest.actions.listWorkflowRunArtifacts({ + const allArtifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { ...context.repo, run_id: context.payload.workflow_run.id }); - console.log('total = ', data.total_count); - const artifacts = data.artifacts.filter(a => a.name.startsWith('${{ inputs.namePrefix }}')); + console.log('total = ', allArtifacts.length); + const artifacts = allArtifacts.filter(a => a.name.startsWith('${{ inputs.namePrefix }}')); const fs = require('fs'); for (const artifact of artifacts) { const result = await github.rest.actions.downloadArtifact({ diff --git a/playwright/.github/actions/upload-blob-report/action.yml b/playwright/.github/actions/upload-blob-report/action.yml index 20347c11d5..87eda3baf8 100644 --- a/playwright/.github/actions/upload-blob-report/action.yml +++ b/playwright/.github/actions/upload-blob-report/action.yml @@ -19,7 +19,7 @@ runs: uses: actions/upload-artifact@v4 with: name: blob-report-${{ inputs.job_name }} - path: ${{ inputs.report_dir }} + path: ${{ inputs.report_dir }}/** retention-days: 7 - name: Write triggering pull request number in a file if: always() && github.event_name == 'pull_request' diff --git a/playwright/.github/workflows/cherry_pick_into_release_branch.yml b/playwright/.github/workflows/cherry_pick_into_release_branch.yml index c326509e35..6350d7ea0d 100644 --- a/playwright/.github/workflows/cherry_pick_into_release_branch.yml +++ b/playwright/.github/workflows/cherry_pick_into_release_branch.yml @@ -23,7 +23,7 @@ jobs: echo "Version is not a two digit semver version" exit 1 fi - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: release-${{ github.event.inputs.version }} fetch-depth: 0 diff --git a/playwright/.github/workflows/create_test_report.yml b/playwright/.github/workflows/create_test_report.yml index 58a6d3f155..bc31bf7623 100644 --- a/playwright/.github/workflows/create_test_report.yml +++ b/playwright/.github/workflows/create_test_report.yml @@ -12,8 +12,8 @@ jobs: if: ${{ github.event.workflow_run.event == 'pull_request' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 diff --git a/playwright/.github/workflows/infra.yml b/playwright/.github/workflows/infra.yml index d3a3fa0030..3580ecc97a 100644 --- a/playwright/.github/workflows/infra.yml +++ b/playwright/.github/workflows/infra.yml @@ -18,8 +18,8 @@ jobs: name: "docs & lint" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -40,8 +40,8 @@ jobs: name: "Lint snippets" runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - uses: actions/setup-python@v4 diff --git a/playwright/.github/workflows/publish_canary.yml b/playwright/.github/workflows/publish_canary.yml index 4ff33481de..6c9eb046e0 100644 --- a/playwright/.github/workflows/publish_canary.yml +++ b/playwright/.github/workflows/publish_canary.yml @@ -13,15 +13,15 @@ env: jobs: publish-canary: - name: "publish canary NPM & Publish canary Docker" + name: "publish canary NPM" runs-on: ubuntu-20.04 if: github.repository == 'microsoft/playwright' permissions: contents: read id-token: write steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 registry-url: 'https://registry.npmjs.org' @@ -57,25 +57,14 @@ jobs: run: | utils/build/build-playwright-driver.sh utils/build/upload-playwright-driver.sh - - uses: azure/docker-login@v1 - with: - login-server: playwright.azurecr.io - username: playwright - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up Docker QEMU for arm64 docker builds - uses: docker/setup-qemu-action@v2 - with: - platforms: arm64 - - name: publish docker canary - run: ./utils/docker/publish_docker.sh canary publish-trace-viewer: name: "publish Trace Viewer to trace.playwright.dev" runs-on: ubuntu-20.04 if: github.repository == 'microsoft/playwright' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - name: Deploy Canary diff --git a/playwright/.github/workflows/publish_release_docker.yml b/playwright/.github/workflows/publish_release_docker.yml index 31a361e564..bc836960f1 100644 --- a/playwright/.github/workflows/publish_release_docker.yml +++ b/playwright/.github/workflows/publish_release_docker.yml @@ -17,11 +17,11 @@ env: jobs: publish-docker-release: name: "publish to DockerHub" - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: github.repository == 'microsoft/playwright' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 registry-url: 'https://registry.npmjs.org' @@ -31,7 +31,7 @@ jobs: username: playwright password: ${{ secrets.DOCKER_PASSWORD }} - name: Set up Docker QEMU for arm64 docker builds - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 with: platforms: arm64 - run: npm ci diff --git a/playwright/.github/workflows/publish_release_driver.yml b/playwright/.github/workflows/publish_release_driver.yml index 56c9201bcd..61524f9cd2 100644 --- a/playwright/.github/workflows/publish_release_driver.yml +++ b/playwright/.github/workflows/publish_release_driver.yml @@ -13,8 +13,8 @@ jobs: runs-on: ubuntu-20.04 if: github.repository == 'microsoft/playwright' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 registry-url: 'https://registry.npmjs.org' diff --git a/playwright/.github/workflows/publish_release_npm.yml b/playwright/.github/workflows/publish_release_npm.yml index 68c4e27630..3b92a01b14 100644 --- a/playwright/.github/workflows/publish_release_npm.yml +++ b/playwright/.github/workflows/publish_release_npm.yml @@ -16,8 +16,8 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 registry-url: 'https://registry.npmjs.org' diff --git a/playwright/.github/workflows/publish_release_traceviewer.yml b/playwright/.github/workflows/publish_release_traceviewer.yml index 0d41425774..60af5442e9 100644 --- a/playwright/.github/workflows/publish_release_traceviewer.yml +++ b/playwright/.github/workflows/publish_release_traceviewer.yml @@ -10,8 +10,8 @@ jobs: runs-on: ubuntu-20.04 if: github.repository == 'microsoft/playwright' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - name: Deploy Stable diff --git a/playwright/.github/workflows/roll_browser_into_playwright.yml b/playwright/.github/workflows/roll_browser_into_playwright.yml index c474f848df..69dba0804d 100644 --- a/playwright/.github/workflows/roll_browser_into_playwright.yml +++ b/playwright/.github/workflows/roll_browser_into_playwright.yml @@ -7,12 +7,15 @@ on: env: ELECTRON_SKIP_BINARY_DOWNLOAD: 1 +permissions: + contents: write + jobs: roll: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci diff --git a/playwright/.github/workflows/roll_driver_nodejs.yml b/playwright/.github/workflows/roll_driver_nodejs.yml index bef065ca71..7a58f1ee2f 100644 --- a/playwright/.github/workflows/roll_driver_nodejs.yml +++ b/playwright/.github/workflows/roll_driver_nodejs.yml @@ -10,8 +10,8 @@ jobs: runs-on: ubuntu-22.04 if: github.repository == 'microsoft/playwright' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: node utils/build/update-playwright-driver-version.mjs diff --git a/playwright/.github/workflows/tests_components.yml b/playwright/.github/workflows/tests_components.yml index 2bed064f83..7291729220 100644 --- a/playwright/.github/workflows/tests_components.yml +++ b/playwright/.github/workflows/tests_components.yml @@ -32,8 +32,8 @@ jobs: node-version: 20 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci diff --git a/playwright/.github/workflows/tests_electron.yml b/playwright/.github/workflows/tests_electron.yml index 193c82f71f..4427cc5e9c 100644 --- a/playwright/.github/workflows/tests_electron.yml +++ b/playwright/.github/workflows/tests_electron.yml @@ -28,8 +28,8 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci diff --git a/playwright/.github/workflows/tests_primary.yml b/playwright/.github/workflows/tests_primary.yml index e53bf51d75..0ad4b68294 100644 --- a/playwright/.github/workflows/tests_primary.yml +++ b/playwright/.github/workflows/tests_primary.yml @@ -45,8 +45,8 @@ jobs: env: PWTEST_BOT_NAME: "${{ matrix.browser }}-${{ matrix.os }}-node${{ matrix.node-version }}" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci @@ -77,8 +77,8 @@ jobs: env: PWTEST_BOT_NAME: "${{ matrix.os }}-chromium-tip-of-tree" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -131,8 +131,8 @@ jobs: env: PWTEST_BOT_NAME: "${{ matrix.os }}-node${{ matrix.node-version }}-${{ matrix.shardIndex }}" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{matrix.node-version}} - run: npm ci @@ -158,8 +158,8 @@ jobs: name: Web Components runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -195,8 +195,8 @@ jobs: env: PWTEST_BOT_NAME: "vscode-extension" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -239,8 +239,8 @@ jobs: env: PWTEST_BOT_NAME: "package-installations-${{ matrix.os }}" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci diff --git a/playwright/.github/workflows/tests_secondary.yml b/playwright/.github/workflows/tests_secondary.yml index 6c4f5fc9df..816bf5c3f9 100644 --- a/playwright/.github/workflows/tests_secondary.yml +++ b/playwright/.github/workflows/tests_secondary.yml @@ -32,8 +32,8 @@ jobs: env: PWTEST_BOT_NAME: "${{ matrix.browser }}-${{ matrix.os }}" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -59,14 +59,14 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-12, macos-13] + os: [macos-12, macos-13, macos-14] browser: [chromium, firefox, webkit] runs-on: ${{ matrix.os }} env: PWTEST_BOT_NAME: "${{ matrix.browser }}-${{ matrix.os }}" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -96,8 +96,8 @@ jobs: env: PWTEST_BOT_NAME: "${{ matrix.browser }}-windows-latest" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -135,8 +135,8 @@ jobs: node_version: 20 timeout-minutes: 30 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node_version }} - run: npm ci @@ -165,8 +165,8 @@ jobs: env: PWTEST_BOT_NAME: "${{ matrix.browser }}-headed-${{ matrix.os }}" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -199,8 +199,8 @@ jobs: env: PWTEST_BOT_NAME: "${{ matrix.mode }}" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -237,8 +237,8 @@ jobs: env: PWTEST_BOT_NAME: "tracing-${{ matrix.channel || matrix.browser }}" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -267,8 +267,8 @@ jobs: env: PWTEST_BOT_NAME: "chrome-stable-linux" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -295,8 +295,8 @@ jobs: env: PWTEST_BOT_NAME: "chrome-stable-windows" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -324,8 +324,8 @@ jobs: env: PWTEST_BOT_NAME: "chrome-stable-mac" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -357,8 +357,8 @@ jobs: matrix: os: [ubuntu-20.04, macos-12, windows-latest] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -391,8 +391,8 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -420,8 +420,8 @@ jobs: env: PWTEST_BOT_NAME: "firefox-beta-linux" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -448,8 +448,8 @@ jobs: env: PWTEST_BOT_NAME: "firefox-beta-windows" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -477,8 +477,8 @@ jobs: env: PWTEST_BOT_NAME: "firefox-beta-mac" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -505,8 +505,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-stable-mac" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -533,8 +533,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-stable-windows" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -562,8 +562,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-stable-linux" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -590,8 +590,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-beta-mac" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -618,8 +618,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-beta-windows" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -647,8 +647,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-beta-linux" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -675,8 +675,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-dev-mac" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -703,8 +703,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-dev-windows" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -732,8 +732,8 @@ jobs: env: PWTEST_BOT_NAME: "edge-dev-linux" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -760,8 +760,8 @@ jobs: env: PWTEST_BOT_NAME: "chrome-beta-linux" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -788,8 +788,8 @@ jobs: env: PWTEST_BOT_NAME: "chrome-beta-windows" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -817,8 +817,8 @@ jobs: env: PWTEST_BOT_NAME: "chrome-beta-mac" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -843,8 +843,8 @@ jobs: name: "build-playwright-driver" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci @@ -858,8 +858,8 @@ jobs: env: PWTEST_BOT_NAME: "headless-new" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci diff --git a/playwright/.github/workflows/tests_service.yml b/playwright/.github/workflows/tests_service.yml index d9b13ca13c..9c932f38e4 100644 --- a/playwright/.github/workflows/tests_service.yml +++ b/playwright/.github/workflows/tests_service.yml @@ -17,8 +17,8 @@ jobs: browser: [chromium, firefox, webkit] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm ci env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 @@ -46,8 +46,8 @@ jobs: if: always() runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 - run: npm ci env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 diff --git a/playwright/.github/workflows/tests_stress.yml b/playwright/.github/workflows/tests_stress.yml index 418c348c92..7c2ce79088 100644 --- a/playwright/.github/workflows/tests_stress.yml +++ b/playwright/.github/workflows/tests_stress.yml @@ -27,8 +27,8 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 16 - run: npm ci diff --git a/playwright/.github/workflows/tests_video.yml b/playwright/.github/workflows/tests_video.yml index 54c97bedca..f39b544f93 100644 --- a/playwright/.github/workflows/tests_video.yml +++ b/playwright/.github/workflows/tests_video.yml @@ -22,8 +22,8 @@ jobs: os: [ubuntu-20.04, ubuntu-22.04] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci diff --git a/playwright/.github/workflows/tests_webview2.yml b/playwright/.github/workflows/tests_webview2.yml index 45e9d38a6d..425a7bbc8a 100644 --- a/playwright/.github/workflows/tests_webview2.yml +++ b/playwright/.github/workflows/tests_webview2.yml @@ -25,8 +25,8 @@ jobs: name: WebView2 runs-on: windows-2022 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - uses: actions/setup-dotnet@v3 diff --git a/playwright/CONTRIBUTING.md b/playwright/CONTRIBUTING.md index 81bbff56b6..264793fc2b 100644 --- a/playwright/CONTRIBUTING.md +++ b/playwright/CONTRIBUTING.md @@ -19,7 +19,7 @@ We strongly recommend that you open an issue before beginning any code modificat ### Getting Code -Make sure you're running Node.js 16+ and NPM 8+, to verify and upgrade NPM do: +Make sure you're running Node.js 20 to verify and upgrade NPM do: ```bash node --version diff --git a/playwright/README.md b/playwright/README.md index c64308fb91..14737d10d4 100644 --- a/playwright/README.md +++ b/playwright/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-121.0.6167.57-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-121.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-17.4-blue.svg?logo=safari)](https://webkit.org/) +[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-123.0.6312.4-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-123.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-17.4-blue.svg?logo=safari)](https://webkit.org/) ## [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 121.0.6167.57 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 123.0.6312.4 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 17.4 | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| Firefox 121.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Firefox 123.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/playwright/babel.config.json b/playwright/babel.config.json index cb1cad7f3a..3e345f4274 100644 --- a/playwright/babel.config.json +++ b/playwright/babel.config.json @@ -4,11 +4,11 @@ }, "plugins": [ ["@babel/plugin-transform-typescript", { "allowDeclareFields": true } ], - "@babel/plugin-proposal-export-namespace-from", - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-logical-assignment-operators", - "@babel/plugin-proposal-nullish-coalescing-operator", - "@babel/plugin-proposal-optional-chaining", + "@babel/plugin-transform-export-namespace-from", + "@babel/plugin-transform-class-properties", + "@babel/plugin-transform-logical-assignment-operators", + "@babel/plugin-transform-nullish-coalescing-operator", + "@babel/plugin-transform-optional-chaining", "@babel/plugin-transform-modules-commonjs" ], "ignore": [ diff --git a/playwright/browser_patches/firefox/UPSTREAM_CONFIG.sh b/playwright/browser_patches/firefox/UPSTREAM_CONFIG.sh index 5436a7ef44..c107d01d86 100644 --- a/playwright/browser_patches/firefox/UPSTREAM_CONFIG.sh +++ b/playwright/browser_patches/firefox/UPSTREAM_CONFIG.sh @@ -1,3 +1,3 @@ REMOTE_URL="https://github.com/mozilla/gecko-dev" BASE_BRANCH="release" -BASE_REVISION="7ab3cc0103090dd7bfa02e072a529b9fc784ab4e" +BASE_REVISION="a32b8662993085139ac91212a297123b632fc1c0" diff --git a/playwright/browser_patches/firefox/juggler/Helper.js b/playwright/browser_patches/firefox/juggler/Helper.js index 9572aa37ad..f5a64d6c8b 100644 --- a/playwright/browser_patches/firefox/juggler/Helper.js +++ b/playwright/browser_patches/firefox/juggler/Helper.js @@ -23,6 +23,16 @@ class Helper { return allBrowsingContexts; } + awaitTopic(topic) { + return new Promise(resolve => { + const listener = () => { + Services.obs.removeObserver(listener, topic); + resolve(); + } + Services.obs.addObserver(listener, topic); + }); + } + toProtocolNavigationId(loadIdentifier) { return `nav-${loadIdentifier}`; } diff --git a/playwright/browser_patches/firefox/juggler/NetworkObserver.js b/playwright/browser_patches/firefox/juggler/NetworkObserver.js index b0413d2e2c..628c6fcb37 100644 --- a/playwright/browser_patches/firefox/juggler/NetworkObserver.js +++ b/playwright/browser_patches/firefox/juggler/NetworkObserver.js @@ -366,13 +366,6 @@ class NetworkRequest { return; } - const browserContext = pageNetwork._target.browserContext(); - if (browserContext.crossProcessCookie.settings.onlineOverride === 'offline') { - // Implement offline. - this.abort(Cr.NS_ERROR_OFFLINE); - return; - } - // Ok, so now we have intercepted the request, let's issue onRequest. // If interception has been disabled while we were intercepting, resume and forget. const interceptionEnabled = this._shouldIntercept(); @@ -462,8 +455,6 @@ class NetworkRequest { const browserContext = pageNetwork._target.browserContext(); if (browserContext.requestInterceptionEnabled) return true; - if (browserContext.crossProcessCookie.settings.onlineOverride === 'offline') - return true; return false; } diff --git a/playwright/browser_patches/firefox/juggler/TargetRegistry.js b/playwright/browser_patches/firefox/juggler/TargetRegistry.js index 25be3c67af..0b1dcd4906 100644 --- a/playwright/browser_patches/firefox/juggler/TargetRegistry.js +++ b/playwright/browser_patches/firefox/juggler/TargetRegistry.js @@ -458,6 +458,11 @@ class PageTarget { this.updateColorSchemeOverride(browsingContext); this.updateReducedMotionOverride(browsingContext); this.updateForcedColorsOverride(browsingContext); + this.updateForceOffline(browsingContext); + } + + updateForceOffline(browsingContext = undefined) { + (browsingContext || this._linkedBrowser.browsingContext).forceOffline = this._browserContext.forceOffline; } updateTouchOverride(browsingContext = undefined) { @@ -829,6 +834,7 @@ class BrowserContext { this.defaultUserAgent = null; this.defaultPlatform = null; this.touchOverride = false; + this.forceOffline = false; this.colorScheme = 'none'; this.forcedColors = 'no-override'; this.reducedMotion = 'none'; @@ -924,6 +930,12 @@ class BrowserContext { page.updateTouchOverride(); } + setForceOffline(forceOffline) { + this.forceOffline = forceOffline; + for (const page of this.pages) + page.updateForceOffline(); + } + async setDefaultViewport(viewport) { this.defaultViewportSize = viewport ? viewport.viewportSize : undefined; this.deviceScaleFactor = viewport ? viewport.deviceScaleFactor : undefined; diff --git a/playwright/browser_patches/firefox/juggler/content/PageAgent.js b/playwright/browser_patches/firefox/juggler/content/PageAgent.js index 614d5b5101..aa69432958 100644 --- a/playwright/browser_patches/firefox/juggler/content/PageAgent.js +++ b/playwright/browser_patches/firefox/juggler/content/PageAgent.js @@ -461,16 +461,8 @@ class PageAgent { } async _dispatchKeyEvent({type, keyCode, code, key, repeat, location, text}) { - if (code === 'OSLeft') - code = 'MetaLeft'; - else if (code === 'OSRight') - code = 'MetaRight'; const frame = this._frameTree.mainFrame(); const tip = frame.textInputProcessor(); - if (key === 'Meta' && Services.appinfo.OS !== 'Darwin') - key = 'OS'; - else if (key === 'OS' && Services.appinfo.OS === 'Darwin') - key = 'Meta'; let keyEvent = new (frame.domWindow().KeyboardEvent)("", { key, code, diff --git a/playwright/browser_patches/firefox/juggler/content/main.js b/playwright/browser_patches/firefox/juggler/content/main.js index 4b891b3a76..022b1e3a5e 100644 --- a/playwright/browser_patches/firefox/juggler/content/main.js +++ b/playwright/browser_patches/firefox/juggler/content/main.js @@ -47,15 +47,6 @@ function initialize(browsingContext, docShell, actor) { } }, - onlineOverride: (onlineOverride) => { - if (!onlineOverride) { - docShell.onlineOverride = Ci.nsIDocShell.ONLINE_OVERRIDE_NONE; - return; - } - docShell.onlineOverride = onlineOverride === 'online' ? - Ci.nsIDocShell.ONLINE_OVERRIDE_ONLINE : Ci.nsIDocShell.ONLINE_OVERRIDE_OFFLINE; - }, - bypassCSP: (bypassCSP) => { docShell.bypassCSPEnabled = bypassCSP; }, diff --git a/playwright/browser_patches/firefox/juggler/protocol/BrowserHandler.js b/playwright/browser_patches/firefox/juggler/protocol/BrowserHandler.js index 7bec58108a..ba70002c3b 100644 --- a/playwright/browser_patches/firefox/juggler/protocol/BrowserHandler.js +++ b/playwright/browser_patches/firefox/juggler/protocol/BrowserHandler.js @@ -199,7 +199,8 @@ class BrowserHandler { } async ['Browser.setOnlineOverride']({browserContextId, override}) { - await this._targetRegistry.browserContextForId(browserContextId).applySetting('onlineOverride', nullToUndefined(override)); + const forceOffline = override === 'offline'; + await this._targetRegistry.browserContextForId(browserContextId).setForceOffline(forceOffline); } async ['Browser.setColorScheme']({browserContextId, colorScheme}) { diff --git a/playwright/browser_patches/firefox/juggler/protocol/PageHandler.js b/playwright/browser_patches/firefox/juggler/protocol/PageHandler.js index 8bceb06979..a893e1593b 100644 --- a/playwright/browser_patches/firefox/juggler/protocol/PageHandler.js +++ b/playwright/browser_patches/firefox/juggler/protocol/PageHandler.js @@ -488,6 +488,9 @@ class PageHandler { this._pageTarget._linkedBrowser.scrollRectIntoViewIfNeeded(x, y, 0, 0); // 2. Get element's bounding box in the browser after the scroll is completed. const boundingBox = this._pageTarget._linkedBrowser.getBoundingClientRect(); + // 3. Make sure compositor is flushed after scrolling. + if (win.windowUtils.flushApzRepaints()) + await helper.awaitTopic('apz-repaints-flushed'); const watcher = new EventWatcher(this._pageEventSink, types, this._pendingEventWatchers); const promises = []; @@ -608,7 +611,7 @@ class PageHandler { const lineOrPageDeltaX = deltaX > 0 ? Math.floor(deltaX) : Math.ceil(deltaX); const lineOrPageDeltaY = deltaY > 0 ? Math.floor(deltaY) : Math.ceil(deltaY); - await this._pageTarget.activateAndRun(() => { + await this._pageTarget.activateAndRun(async () => { this._pageTarget.ensureContextMenuClosed(); // 1. Scroll element to the desired location first; the coordinates are relative to the element. @@ -617,6 +620,10 @@ class PageHandler { const boundingBox = this._pageTarget._linkedBrowser.getBoundingClientRect(); const win = this._pageTarget._window; + // 3. Make sure compositor is flushed after scrolling. + if (win.windowUtils.flushApzRepaints()) + await helper.awaitTopic('apz-repaints-flushed'); + win.windowUtils.sendWheelEvent( x + boundingBox.left, y + boundingBox.top, diff --git a/playwright/browser_patches/firefox/patches/bootstrap.diff b/playwright/browser_patches/firefox/patches/bootstrap.diff index d58ec0332a..e325f549de 100644 --- a/playwright/browser_patches/firefox/patches/bootstrap.diff +++ b/playwright/browser_patches/firefox/patches/bootstrap.diff @@ -1,5 +1,5 @@ diff --git a/accessible/base/NotificationController.h b/accessible/base/NotificationController.h -index bc1a692c23d8599512a5ce956d99998640347c46..4e77897aa4a84ce88445ba45f1ba3b5b2dde9e23 100644 +index 137963f1170927ae0262e0dc26ef721d496376f4..41fa27bc4a3da41814a7f326792990df3424e81f 100644 --- a/accessible/base/NotificationController.h +++ b/accessible/base/NotificationController.h @@ -244,6 +244,8 @@ class NotificationController final : public EventQueue, @@ -57,7 +57,7 @@ index 8e9bf2b413585b5a3db9370eee5d57fb6c6716ed..5a3b194b54e3813c89989f13a214c989 * Return XPCOM wrapper for the internal accessible. */ diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp -index d3fa2a973619ed3dc12d9aac9bc751e21a158406..ab3b84ce0a685d79ef56cbf38b5c4beeb7a1100c 100644 +index b40e0fceb567c0d217adf284e13f434e49cc8467..2c4e6d5fbf8da40954ad6a5b15e412493e43b14e 100644 --- a/browser/app/winlauncher/LauncherProcessWin.cpp +++ b/browser/app/winlauncher/LauncherProcessWin.cpp @@ -22,6 +22,7 @@ @@ -68,7 +68,7 @@ index d3fa2a973619ed3dc12d9aac9bc751e21a158406..ab3b84ce0a685d79ef56cbf38b5c4bee #include #include -@@ -422,8 +423,18 @@ Maybe LauncherMain(int& argc, wchar_t* argv[], +@@ -421,8 +422,18 @@ Maybe LauncherMain(int& argc, wchar_t* argv[], HANDLE stdHandles[] = {::GetStdHandle(STD_INPUT_HANDLE), ::GetStdHandle(STD_OUTPUT_HANDLE), ::GetStdHandle(STD_ERROR_HANDLE)}; @@ -89,7 +89,7 @@ index d3fa2a973619ed3dc12d9aac9bc751e21a158406..ab3b84ce0a685d79ef56cbf38b5c4bee DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT; diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn -index bae6d0c5be1ef486b44563b519c5f4aa4b5bc769..636e801c940dea835dee55759cb638a6714a33cd 100644 +index 24a6f228c617c034ccc4926c238aa15e32c964f6..38c4ed9936f5033abe7a4ac7997aea2d92747733 100644 --- a/browser/installer/allowed-dupes.mn +++ b/browser/installer/allowed-dupes.mn @@ -71,6 +71,12 @@ browser/features/webcompat@mozilla.org/shims/empty-shim.txt @@ -106,10 +106,10 @@ index bae6d0c5be1ef486b44563b519c5f4aa4b5bc769..636e801c940dea835dee55759cb638a6 browser/chrome/browser/search-extensions/amazon/favicon.ico browser/chrome/browser/search-extensions/amazondotcn/favicon.ico diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in -index 1e48dd12b43ed48085f22f6576f8c6696153775f..3b79a0e6e607ae67a00c3c7403ff18261a2419b4 100644 +index b3213b8c4498b0467d7863d53c5fc4240e4609be..5ca6b68c6c7aaebd41a81999974f69810876f82b 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in -@@ -196,6 +196,9 @@ +@@ -195,6 +195,9 @@ @RESPATH@/chrome/remote.manifest #endif @@ -167,7 +167,7 @@ index 4236ec2921bd57c58cfffdf1cdcf509d76fca3db..23d0cb1f06bb8c7a1cac8fcec94a99fb const transportProvider = { setListener(upgradeListener) { diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp -index e6360a37dcdf0d01353175bb24211e48c54c6ac5..1aeaacb0f91c6bee166ec888196971d3bc957a41 100644 +index 211f83a476caa08281b3a0158da42cb08b01a374..47d78411e67d3f9b67c48ff10379e117b8631ab6 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -114,6 +114,20 @@ struct ParamTraits @@ -233,7 +233,7 @@ index e6360a37dcdf0d01353175bb24211e48c54c6ac5..1aeaacb0f91c6bee166ec888196971d3 nsString&& aOldValue) { MOZ_ASSERT(IsTop()); diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h -index f9129bb2db492c7446a092c744b14f42449dc74c..a2abdfee35289be118b29baa6a5f3385a63f7085 100644 +index 84532563e1db1941abca5afa74a7d68473d391ef..84d6f605d5f70d11b355ba76b0f5af868864ac18 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -200,10 +200,10 @@ struct EmbedderColorSchemes { @@ -260,7 +260,7 @@ index f9129bb2db492c7446a092c744b14f42449dc74c..a2abdfee35289be118b29baa6a5f3385 /* The number of entries added to the session history because of this \ * browsing context. */ \ FIELD(HistoryEntryCount, uint32_t) \ -@@ -919,6 +923,14 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { +@@ -923,6 +927,14 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { return GetPrefersColorSchemeOverride(); } @@ -275,7 +275,7 @@ index f9129bb2db492c7446a092c744b14f42449dc74c..a2abdfee35289be118b29baa6a5f3385 bool IsInBFCache() const; bool AllowJavascript() const { return GetAllowJavascript(); } -@@ -1083,6 +1095,23 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { +@@ -1087,6 +1099,23 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { void WalkPresContexts(Callback&&); void PresContextAffectingFieldChanged(); @@ -300,10 +300,10 @@ index f9129bb2db492c7446a092c744b14f42449dc74c..a2abdfee35289be118b29baa6a5f3385 bool CanSet(FieldIndex, bool, ContentParent*) { diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp -index a6bbc3c7c9a0eaf1b0dcf4a9a68c1d579aa79f70..6de37c24076abcb136e0c2014d1e94e60ea62720 100644 +index 044df3a801ed55e08347464397821261bb6761c0..8aa8e4844e4ba5364cbb0547c5397ce695798861 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp -@@ -1465,6 +1465,12 @@ void CanonicalBrowsingContext::LoadURI(nsIURI* aURI, +@@ -1466,6 +1466,12 @@ void CanonicalBrowsingContext::LoadURI(nsIURI* aURI, return; } @@ -317,7 +317,7 @@ index a6bbc3c7c9a0eaf1b0dcf4a9a68c1d579aa79f70..6de37c24076abcb136e0c2014d1e94e6 } diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp -index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955ba4437afd 100644 +index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e8aa888bf 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -15,6 +15,12 @@ @@ -333,7 +333,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/AutoRestore.h" -@@ -65,6 +71,7 @@ +@@ -64,6 +70,7 @@ #include "mozilla/dom/ContentFrameMessageManager.h" #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/Element.h" @@ -341,7 +341,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b #include "mozilla/dom/HTMLAnchorElement.h" #include "mozilla/dom/HTMLIFrameElement.h" #include "mozilla/dom/PerformanceNavigation.h" -@@ -90,6 +97,7 @@ +@@ -88,6 +95,7 @@ #include "mozilla/dom/JSWindowActorChild.h" #include "mozilla/dom/DocumentBinding.h" #include "mozilla/ipc/ProtocolUtils.h" @@ -349,7 +349,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b #include "mozilla/net/DocumentChannel.h" #include "mozilla/net/DocumentChannelChild.h" #include "mozilla/net/ParentChannelWrapper.h" -@@ -113,6 +121,7 @@ +@@ -111,6 +119,7 @@ #include "nsIDocShellTreeOwner.h" #include "mozilla/dom/Document.h" #include "nsHTMLDocument.h" @@ -357,7 +357,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b #include "nsIDocumentLoaderFactory.h" #include "nsIDOMWindow.h" #include "nsIEditingSession.h" -@@ -208,6 +217,7 @@ +@@ -206,6 +215,7 @@ #include "nsGlobalWindowInner.h" #include "nsGlobalWindowOuter.h" #include "nsJSEnvironment.h" @@ -365,7 +365,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsObjectLoadingContent.h" -@@ -350,6 +360,14 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext, +@@ -346,6 +356,13 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext, mAllowDNSPrefetch(true), mAllowWindowControl(true), mCSSErrorReportingEnabled(false), @@ -374,13 +374,12 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b + mBypassCSPEnabled(false), + mForceActiveState(false), + mDisallowBFCache(false), -+ mOnlineOverride(nsIDocShell::ONLINE_OVERRIDE_NONE), + mReducedMotionOverride(REDUCED_MOTION_OVERRIDE_NONE), + mForcedColorsOverride(FORCED_COLORS_OVERRIDE_NO_OVERRIDE), mAllowAuth(mItemType == typeContent), mAllowKeywordFixup(false), mDisableMetaRefreshWhenInactive(false), -@@ -3204,6 +3222,234 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) { +@@ -3147,6 +3164,214 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) { return NS_OK; } @@ -555,26 +554,6 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b +} + +NS_IMETHODIMP -+nsDocShell::GetOnlineOverride(OnlineOverride* aOnlineOverride) { -+ *aOnlineOverride = GetRootDocShell()->mOnlineOverride; -+ return NS_OK; -+} -+ -+NS_IMETHODIMP -+nsDocShell::SetOnlineOverride(OnlineOverride aOnlineOverride) { -+ // We don't have a way to verify this coming from Javascript, so this check is -+ // still needed. -+ if (!(aOnlineOverride == ONLINE_OVERRIDE_NONE || -+ aOnlineOverride == ONLINE_OVERRIDE_ONLINE || -+ aOnlineOverride == ONLINE_OVERRIDE_OFFLINE)) { -+ return NS_ERROR_INVALID_ARG; -+ } -+ -+ mOnlineOverride = aOnlineOverride; -+ return NS_OK; -+} -+ -+NS_IMETHODIMP +nsDocShell::GetReducedMotionOverride(ReducedMotionOverride* aReducedMotionOverride) { + *aReducedMotionOverride = GetRootDocShell()->mReducedMotionOverride; + return NS_OK; @@ -615,7 +594,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b NS_IMETHODIMP nsDocShell::GetIsNavigating(bool* aOut) { *aOut = mIsNavigating; -@@ -4895,7 +5141,7 @@ nsDocShell::GetVisibility(bool* aVisibility) { +@@ -4835,7 +5060,7 @@ nsDocShell::GetVisibility(bool* aVisibility) { } void nsDocShell::ActivenessMaybeChanged() { @@ -624,7 +603,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b if (RefPtr presShell = GetPresShell()) { presShell->ActivenessMaybeChanged(); } -@@ -6811,6 +7057,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType, +@@ -6753,6 +6978,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType, return false; // no entry to save into } @@ -635,7 +614,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b MOZ_ASSERT(!mozilla::SessionHistoryInParent(), "mOSHE cannot be non-null with SHIP"); nsCOMPtr viewer = mOSHE->GetContentViewer(); -@@ -8595,6 +8845,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) { +@@ -8536,6 +8765,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) { true, // aForceNoOpener getter_AddRefs(newBC)); MOZ_ASSERT(!newBC); @@ -648,7 +627,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b return rv; } -@@ -9670,6 +9926,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, +@@ -9611,6 +9846,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr); nsCOMPtr req; @@ -665,7 +644,7 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req)); if (NS_SUCCEEDED(rv)) { -@@ -12827,6 +13093,9 @@ class OnLinkClickEvent : public Runnable { +@@ -12768,6 +13013,9 @@ class OnLinkClickEvent : public Runnable { mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied, mTriggeringPrincipal); } @@ -675,20 +654,20 @@ index 7669fbacd09a65cda1b06c74aa75f0ef7b625da2..8fd21c46640003c3f6d3cbc79697955b return NS_OK; } -@@ -12911,6 +13180,8 @@ nsresult nsDocShell::OnLinkClick( +@@ -12852,6 +13100,8 @@ nsresult nsDocShell::OnLinkClick( nsCOMPtr ev = new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied, aIsTrusted, aTriggeringPrincipal); + nsCOMPtr observerService = mozilla::services::GetObserverService(); + observerService->NotifyObservers(ToSupports(aContent), "juggler-link-click", nullptr); - return Dispatch(TaskCategory::UI, ev.forget()); + return Dispatch(ev.forget()); } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h -index 21cd7c944b391bf0333c7bdc815200db33ef0afe..aa8d79b0d3bd34419c5d625678f3cd1231e2b46f 100644 +index 237cf8dc1a54ed8d0523272aaaebfd6f10f9213f..73c096a2850850921aff97037457cde598ffef2a 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h -@@ -16,6 +16,7 @@ +@@ -15,6 +15,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" #include "mozilla/dom/BrowsingContext.h" @@ -696,7 +675,7 @@ index 21cd7c944b391bf0333c7bdc815200db33ef0afe..aa8d79b0d3bd34419c5d625678f3cd12 #include "mozilla/dom/WindowProxyHolder.h" #include "nsCOMPtr.h" #include "nsCharsetSource.h" -@@ -77,6 +78,7 @@ class nsCommandManager; +@@ -76,6 +77,7 @@ class nsCommandManager; class nsDocShellEditorData; class nsDOMNavigationTiming; class nsDSURIContentListener; @@ -704,7 +683,7 @@ index 21cd7c944b391bf0333c7bdc815200db33ef0afe..aa8d79b0d3bd34419c5d625678f3cd12 class nsGlobalWindowOuter; class FramingChecker; -@@ -409,6 +411,15 @@ class nsDocShell final : public nsDocLoader, +@@ -408,6 +410,15 @@ class nsDocShell final : public nsDocLoader, void SetWillChangeProcess() { mWillChangeProcess = true; } bool WillChangeProcess() { return mWillChangeProcess; } @@ -720,7 +699,7 @@ index 21cd7c944b391bf0333c7bdc815200db33ef0afe..aa8d79b0d3bd34419c5d625678f3cd12 // Create a content viewer within this nsDocShell for the given // `WindowGlobalChild` actor. nsresult CreateContentViewerForActor( -@@ -1030,6 +1041,8 @@ class nsDocShell final : public nsDocLoader, +@@ -1010,6 +1021,8 @@ class nsDocShell final : public nsDocLoader, bool CSSErrorReportingEnabled() const { return mCSSErrorReportingEnabled; } @@ -729,7 +708,7 @@ index 21cd7c944b391bf0333c7bdc815200db33ef0afe..aa8d79b0d3bd34419c5d625678f3cd12 // Handles retrieval of subframe session history for nsDocShell::LoadURI. If a // load is requested in a subframe of the current DocShell, the subframe // loadType may need to reflect the loadType of the parent document, or in -@@ -1327,6 +1340,17 @@ class nsDocShell final : public nsDocLoader, +@@ -1300,6 +1313,16 @@ class nsDocShell final : public nsDocLoader, bool mAllowDNSPrefetch : 1; bool mAllowWindowControl : 1; bool mCSSErrorReportingEnabled : 1; @@ -740,7 +719,6 @@ index 21cd7c944b391bf0333c7bdc815200db33ef0afe..aa8d79b0d3bd34419c5d625678f3cd12 + bool mDisallowBFCache : 1; + nsString mLanguageOverride; + RefPtr mGeolocationServiceOverride; -+ OnlineOverride mOnlineOverride; + ReducedMotionOverride mReducedMotionOverride; + ForcedColorsOverride mForcedColorsOverride; + @@ -748,7 +726,7 @@ index 21cd7c944b391bf0333c7bdc815200db33ef0afe..aa8d79b0d3bd34419c5d625678f3cd12 bool mAllowKeywordFixup : 1; bool mDisableMetaRefreshWhenInactive : 1; diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl -index 68f32e968c7e1bc1d0b2b2894320a177a9ae44d2..9e61465ffad927d7b3e972f753940196fc986156 100644 +index a3ee2c3944018c64c93c02c5658c527ec5f9d2cb..272e5147568b95bf313ac7a940619382a0c42763 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -44,6 +44,7 @@ interface nsIURI; @@ -759,7 +737,7 @@ index 68f32e968c7e1bc1d0b2b2894320a177a9ae44d2..9e61465ffad927d7b3e972f753940196 interface nsIEditor; interface nsIEditingSession; interface nsIInputStream; -@@ -784,6 +785,43 @@ interface nsIDocShell : nsIDocShellTreeItem +@@ -760,6 +761,36 @@ interface nsIDocShell : nsIDocShellTreeItem */ void synchronizeLayoutHistoryState(); @@ -777,13 +755,6 @@ index 68f32e968c7e1bc1d0b2b2894320a177a9ae44d2..9e61465ffad927d7b3e972f753940196 + + boolean overrideTimezone(in AString timezoneId); + -+ cenum OnlineOverride: 8 { -+ ONLINE_OVERRIDE_NONE = 0, -+ ONLINE_OVERRIDE_ONLINE = 1, -+ ONLINE_OVERRIDE_OFFLINE = 2, -+ }; -+ [infallible] attribute nsIDocShell_OnlineOverride onlineOverride; -+ + cenum ReducedMotionOverride : 8 { + REDUCED_MOTION_OVERRIDE_REDUCE, + REDUCED_MOTION_OVERRIDE_NO_PREFERENCE, @@ -804,10 +775,10 @@ index 68f32e968c7e1bc1d0b2b2894320a177a9ae44d2..9e61465ffad927d7b3e972f753940196 * 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 dbb0e7d060dacf26154b30f67c5980bdebb533c1..eba7dfb80333d3473a2167d8105544d1de46d939 100644 +index ff78b31699fe28e60915703b7642a72d540f6f99..dd8f4dd0283e3d0ce1b5ae8745312fcb64126c75 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp -@@ -3671,6 +3671,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) { +@@ -3682,6 +3682,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) { } void Document::ApplySettingsFromCSP(bool aSpeculative) { @@ -817,7 +788,7 @@ index dbb0e7d060dacf26154b30f67c5980bdebb533c1..eba7dfb80333d3473a2167d8105544d1 nsresult rv = NS_OK; if (!aSpeculative) { // 1) apply settings from regular CSP -@@ -3728,6 +3731,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { +@@ -3739,6 +3742,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { MOZ_ASSERT(!mScriptGlobalObject, "CSP must be initialized before mScriptGlobalObject is set!"); @@ -829,7 +800,7 @@ index dbb0e7d060dacf26154b30f67c5980bdebb533c1..eba7dfb80333d3473a2167d8105544d1 // If this is a data document - no need to set CSP. if (mLoadedAsData) { return NS_OK; -@@ -4544,6 +4552,10 @@ bool Document::HasFocus(ErrorResult& rv) const { +@@ -4534,6 +4542,10 @@ bool Document::HasFocus(ErrorResult& rv) const { return false; } @@ -840,8 +811,8 @@ index dbb0e7d060dacf26154b30f67c5980bdebb533c1..eba7dfb80333d3473a2167d8105544d1 if (!fm->IsInActiveWindow(bc)) { return false; } -@@ -18535,6 +18547,68 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const { - return LookAndFeel::PreferredColorSchemeForContent(); +@@ -18736,6 +18748,68 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const { + return PreferenceSheet::PrefsFor(*this).mColorScheme; } +bool Document::PrefersReducedMotion() const { @@ -910,10 +881,10 @@ index dbb0e7d060dacf26154b30f67c5980bdebb533c1..eba7dfb80333d3473a2167d8105544d1 if (!sLoadingForegroundTopLevelContentDocument) { return false; diff --git a/dom/base/Document.h b/dom/base/Document.h -index ed73acda064d3c899858b4c4bd6c8df4a5b2a964..5d49feda0146088128d1901d4168ca75d7062aa3 100644 +index 55e4f71d5b33cca55454ac0d4e16d4a96137a616..8ad7117047ee6d0b5de2538cd086d60ae9837594 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h -@@ -4066,6 +4066,9 @@ class Document : public nsINode, +@@ -4030,6 +4030,9 @@ class Document : public nsINode, // color-scheme meta tag. ColorScheme DefaultColorScheme() const; @@ -924,10 +895,10 @@ index ed73acda064d3c899858b4c4bd6c8df4a5b2a964..5d49feda0146088128d1901d4168ca75 static bool AutomaticStorageAccessPermissionCanBeGranted( diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp -index 9a5f6913b0682ad39824a2504734e58af9ae845e..baf24386e37daac95700d0715bf00cca1ebcd84f 100644 +index 7e54ac18ad9a646e981e7bc356e75de273c75845..1e673db0fd8adc9a4096a826184425e939d5e926 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp -@@ -327,14 +327,18 @@ void Navigator::GetAppName(nsAString& aAppName) const { +@@ -331,14 +331,18 @@ void Navigator::GetAppName(nsAString& aAppName) const { * for more detail. */ /* static */ @@ -948,7 +919,7 @@ index 9a5f6913b0682ad39824a2504734e58af9ae845e..baf24386e37daac95700d0715bf00cca // Split values on commas. for (nsDependentSubstring lang : -@@ -386,7 +390,13 @@ void Navigator::GetLanguage(nsAString& aLanguage) { +@@ -390,7 +394,13 @@ void Navigator::GetLanguage(nsAString& aLanguage) { } void Navigator::GetLanguages(nsTArray& aLanguages) { @@ -963,26 +934,11 @@ index 9a5f6913b0682ad39824a2504734e58af9ae845e..baf24386e37daac95700d0715bf00cca // 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 -@@ -561,7 +571,13 @@ bool Navigator::CookieEnabled() { - return granted; - } - --bool Navigator::OnLine() { return !NS_IsOffline(); } -+bool Navigator::OnLine() { -+ nsDocShell* docShell = static_cast(GetDocShell()); -+ nsIDocShell::OnlineOverride onlineOverride; -+ if (!docShell || docShell->GetOnlineOverride(&onlineOverride) != NS_OK || onlineOverride == nsIDocShell::ONLINE_OVERRIDE_NONE) -+ return !NS_IsOffline(); -+ return onlineOverride == nsIDocShell::ONLINE_OVERRIDE_ONLINE; -+} - - void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType, - ErrorResult& aRv) const { diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h -index f878c11dff3d448dfa2520c7fe7e4e9cb63f7ea7..c1a30391eb31e28e1c22dff82bb9526bc7e058dd 100644 +index 851b681570e35ba86341f56912684bc8ec7001f3..2fd4d6fd9deafaa42604428dc17798142ce77e19 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h -@@ -213,7 +213,7 @@ class Navigator final : public nsISupports, public nsWrapperCache { +@@ -214,7 +214,7 @@ class Navigator final : public nsISupports, public nsWrapperCache { StorageManager* Storage(); @@ -992,10 +948,10 @@ index f878c11dff3d448dfa2520c7fe7e4e9cb63f7ea7..c1a30391eb31e28e1c22dff82bb9526b dom::MediaCapabilities* MediaCapabilities(); dom::MediaSession* MediaSession(); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp -index 1f9863a8bc538eece4dbf04b2e4bffbad50d5d00..b5cd6be99c8457030db1b23ac307fc2ff40e6e49 100644 +index a5fee753ce551826e5b974ca01aff1f541754f6b..f1a80d1bb25175c12345acbc27b82d96793e061d 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp -@@ -8554,7 +8554,8 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -8598,7 +8598,8 @@ nsresult nsContentUtils::SendMouseEvent( bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized, @@ -1005,7 +961,7 @@ index 1f9863a8bc538eece4dbf04b2e4bffbad50d5d00..b5cd6be99c8457030db1b23ac307fc2f nsPoint offset; nsCOMPtr widget = GetWidget(aPresShell, &offset); if (!widget) return NS_ERROR_FAILURE; -@@ -8562,6 +8563,7 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -8606,6 +8607,7 @@ nsresult nsContentUtils::SendMouseEvent( EventMessage msg; Maybe exitFrom; bool contextMenuKey = false; @@ -1013,7 +969,7 @@ index 1f9863a8bc538eece4dbf04b2e4bffbad50d5d00..b5cd6be99c8457030db1b23ac307fc2f if (aType.EqualsLiteral("mousedown")) { msg = eMouseDown; } else if (aType.EqualsLiteral("mouseup")) { -@@ -8586,6 +8588,12 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -8630,6 +8632,12 @@ nsresult nsContentUtils::SendMouseEvent( msg = eMouseHitTest; } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) { msg = eMouseExploreByTouch; @@ -1026,7 +982,7 @@ index 1f9863a8bc538eece4dbf04b2e4bffbad50d5d00..b5cd6be99c8457030db1b23ac307fc2f } else { return NS_ERROR_FAILURE; } -@@ -8594,12 +8602,21 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -8638,12 +8646,21 @@ nsresult nsContentUtils::SendMouseEvent( aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE; } @@ -1050,7 +1006,7 @@ index 1f9863a8bc538eece4dbf04b2e4bffbad50d5d00..b5cd6be99c8457030db1b23ac307fc2f event.pointerId = aIdentifier; event.mModifiers = GetWidgetModifiers(aModifiers); event.mButton = aButton; -@@ -8610,8 +8627,10 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -8654,8 +8671,10 @@ nsresult nsContentUtils::SendMouseEvent( event.mPressure = aPressure; event.mInputSource = aInputSourceArg; event.mClickCount = aClickCount; @@ -1062,10 +1018,10 @@ index 1f9863a8bc538eece4dbf04b2e4bffbad50d5d00..b5cd6be99c8457030db1b23ac307fc2f nsPresContext* presContext = aPresShell->GetPresContext(); if (!presContext) return NS_ERROR_FAILURE; diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h -index 180c7fe15d6acbd68c477a5677a397ef2acc3c6b..b66187e2063b6b7660ae0ef5a0a34b5655b23a97 100644 +index 36c1a4f4eeba0fa75d79715f79de0d705719e5c5..6ad5426612ef37d7326bafd771a4c2d6d9142cb5 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h -@@ -2947,7 +2947,8 @@ class nsContentUtils { +@@ -2963,7 +2963,8 @@ class nsContentUtils { int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, mozilla::PreventDefaultResult* aPreventDefault, @@ -1154,10 +1110,10 @@ index 63968c9b7a4e418e4c0de6e7a75fa215a36a9105..decf3ea3833ccdffd49a7aded2d600f9 MOZ_CAN_RUN_SCRIPT nsresult SendTouchEventCommon( diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp -index 1be0c80cd30f8696a9451af1f9d381591f1ba0c0..9f9af9649c2cd55188e085e011819cc5db0fb6dc 100644 +index 0d9e4df42f7efcbb0068bc162dfbb17003cd3802..55c0e9e8cfa434c5b8c7dbd27651bf94d5b9a2d1 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp -@@ -1672,6 +1672,10 @@ Maybe nsFocusManager::SetFocusInner(Element* aNewContent, +@@ -1673,6 +1673,10 @@ Maybe nsFocusManager::SetFocusInner(Element* aNewContent, (GetActiveBrowsingContext() == newRootBrowsingContext); } @@ -1168,7 +1124,7 @@ index 1be0c80cd30f8696a9451af1f9d381591f1ba0c0..9f9af9649c2cd55188e085e011819cc5 // Exit fullscreen if a website focuses another window if (StaticPrefs::full_screen_api_exit_on_windowRaise() && !isElementInActiveWindow && (aFlags & FLAG_RAISE)) { -@@ -2945,7 +2949,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow, +@@ -2946,7 +2950,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow, } } @@ -1180,18 +1136,9 @@ index 1be0c80cd30f8696a9451af1f9d381591f1ba0c0..9f9af9649c2cd55188e085e011819cc5 // 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 a784fe3c4bf9fe6a74bebca23fdce04524e0f473..100bae14efb8f4cc49238ce808e3f4e7fe1cd61c 100644 +index 97d967175822e4f62079c21152f96e5540dbb98f..c5bf0d155649e6715aa3af6ca29c237b1dab6ead 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp -@@ -2490,7 +2490,7 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, - &nsGlobalWindowInner::FireOnNewGlobalObject)); - } - -- if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { -+ if (newInnerWindow && mDoc) { - // We should probably notify. However if this is the, arguably bad, - // situation when we're creating a temporary non-chrome-about-blank - // document in a chrome docshell, don't notify just yet. Instead wait @@ -2509,10 +2509,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, }(); @@ -1234,10 +1181,10 @@ index a784fe3c4bf9fe6a74bebca23fdce04524e0f473..100bae14efb8f4cc49238ce808e3f4e7 void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) { diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h -index 8a891ca19a56ff0cdecab26e1d6bb78f32b91abd..c05023ca6a88e0caef5b709a4f8c2846894d5c3c 100644 +index 8337a7353fb8e97372f0b57bd0fd867506a9129f..e7dedd6d26125e481e1145337a0be6ab864c1e1b 100644 --- a/dom/base/nsGlobalWindowOuter.h +++ b/dom/base/nsGlobalWindowOuter.h -@@ -314,6 +314,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, +@@ -315,6 +315,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, // Outer windows only. void DispatchDOMWindowCreated(); @@ -1246,10 +1193,10 @@ index 8a891ca19a56ff0cdecab26e1d6bb78f32b91abd..c05023ca6a88e0caef5b709a4f8c2846 // Outer windows only. virtual void EnsureSizeAndPositionUpToDate() override; diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp -index 77016f314939bf6ac11b48db1f71d1d3a82d4e83..67440e2643eb3f098e8e790179634216da7f851f 100644 +index 369bb41fe459c8ef4c10c61c1530f52d51b3f6de..15ef3c6824cc70e756777612f0fd148e5678c795 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp -@@ -1358,6 +1358,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions, +@@ -1359,6 +1359,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions, mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv); } @@ -1312,10 +1259,10 @@ index 77016f314939bf6ac11b48db1f71d1d3a82d4e83..67440e2643eb3f098e8e790179634216 DOMQuad& aQuad, const GeometryNode& aFrom, const ConvertCoordinateOptions& aOptions, CallerType aCallerType, diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h -index 73cfa7eb511e1c453b2634fe5a2aa7a042ba0221..759909ce15a5aff520d0d8429ee399a8b4787f11 100644 +index 807deebaad78f531c59d18aac2c2833e38d13946..3754b573ef119854e2c41b51b0dd0a43d7c53315 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h -@@ -2212,6 +2212,10 @@ class nsINode : public mozilla::dom::EventTarget { +@@ -2229,6 +2229,10 @@ class nsINode : public mozilla::dom::EventTarget { nsTArray>& aResult, ErrorResult& aRv); @@ -1327,7 +1274,7 @@ index 73cfa7eb511e1c453b2634fe5a2aa7a042ba0221..759909ce15a5aff520d0d8429ee399a8 DOMQuad& aQuad, const TextOrElementOrDocument& aFrom, const ConvertCoordinateOptions& aOptions, CallerType aCallerType, diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp -index 66b0a09dda9e57f41643da11abb079896b9634d9..eb1dacdce7c8426de3f3cd34d2c22d1d13f49b5a 100644 +index bb280d9a15aed8b7d82a93214e82222e9b5a14ae..d64cd52877c1643148ce5912eec26bd1569acdac 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -177,6 +177,11 @@ bool nsJSUtils::GetScopeChainForElement( @@ -1355,7 +1302,7 @@ index 36e906061588aab50dee129cc46dd2e4d3e153f8..c3591f98d4df19b166fc5c99332e559b static bool DumpEnabled(); diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl -index db60c475931caa32110d12ba63bb56980a2b36cc..5d1d8fdceec7a73541799cbac367b173454ae6e8 100644 +index 2e8abd50793dd1392a876e53e99ad806a256f755..695b9e8ffff846d663b645e3ca6fc83a39f6f126 100644 --- a/dom/chrome-webidl/BrowsingContext.webidl +++ b/dom/chrome-webidl/BrowsingContext.webidl @@ -53,6 +53,24 @@ enum PrefersColorSchemeOverride { @@ -1383,7 +1330,7 @@ index db60c475931caa32110d12ba63bb56980a2b36cc..5d1d8fdceec7a73541799cbac367b173 /** * Allowed overrides of platform/pref default behaviour for touch events. */ -@@ -199,6 +217,12 @@ interface BrowsingContext { +@@ -205,6 +223,12 @@ interface BrowsingContext { // Color-scheme simulation, for DevTools. [SetterThrows] attribute PrefersColorSchemeOverride prefersColorSchemeOverride; @@ -1397,7 +1344,7 @@ index db60c475931caa32110d12ba63bb56980a2b36cc..5d1d8fdceec7a73541799cbac367b173 * 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 197146d71e9772af04e577663dbc0213c26a62cb..0e357893cdcf0d6b627bca803aa6041107079184 100644 +index cb9107deb1acfc6f9f3efe87144fcd9bbccd9231..5034c066db8e13dbd01b9bbe79ac2447135f3360 100644 --- a/dom/geolocation/Geolocation.cpp +++ b/dom/geolocation/Geolocation.cpp @@ -23,6 +23,7 @@ @@ -1408,7 +1355,7 @@ index 197146d71e9772af04e577663dbc0213c26a62cb..0e357893cdcf0d6b627bca803aa60411 #include "nsGlobalWindowInner.h" #include "mozilla/dom/Document.h" #include "nsINamed.h" -@@ -259,10 +260,8 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) { +@@ -256,10 +257,8 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) { return NS_OK; } @@ -1421,7 +1368,7 @@ index 197146d71e9772af04e577663dbc0213c26a62cb..0e357893cdcf0d6b627bca803aa60411 CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition(); if (lastPosition.position) { EpochTimeStamp cachedPositionTime_ms; -@@ -440,8 +439,7 @@ void nsGeolocationRequest::Shutdown() { +@@ -437,8 +436,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) { @@ -1431,7 +1378,7 @@ index 197146d71e9772af04e577663dbc0213c26a62cb..0e357893cdcf0d6b627bca803aa60411 if (gs) { gs->UpdateAccuracy(); } -@@ -730,8 +728,14 @@ void nsGeolocationService::StopDevice() { +@@ -727,8 +725,14 @@ void nsGeolocationService::StopDevice() { StaticRefPtr nsGeolocationService::sService; already_AddRefed @@ -1447,7 +1394,7 @@ index 197146d71e9772af04e577663dbc0213c26a62cb..0e357893cdcf0d6b627bca803aa60411 if (nsGeolocationService::sService) { result = nsGeolocationService::sService; -@@ -823,7 +827,9 @@ nsresult Geolocation::Init(nsPIDOMWindowInner* aContentDom) { +@@ -820,7 +824,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. @@ -1496,7 +1443,7 @@ index 7e1af00d05fbafa2d828e2c7e4dcc5c82d115f5b..e85af9718d064e4d2865bc944e9d4ba1 ~Geolocation(); diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp -index c419fb6936dac5cebde8dbaf548d8edd49ffc64a..f5a0cd548721c5d87be6dc719a0183b0a7eeccfe 100644 +index 09d57823aa37f596ac04261025862975f414bd7c..8cee0ae86ae1ed3aeda879cbf55f381a772dfa8b 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -58,6 +58,7 @@ @@ -1507,7 +1454,7 @@ index c419fb6936dac5cebde8dbaf548d8edd49ffc64a..f5a0cd548721c5d87be6dc719a0183b0 #include "nsIFormControlFrame.h" #include "nsITextControlFrame.h" #include "nsIFrame.h" -@@ -782,6 +783,12 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) { +@@ -783,6 +784,12 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) { return NS_ERROR_FAILURE; } @@ -1552,10 +1499,10 @@ index 4170a79023a2503831d080a6e65d5e143f34f241..3af08d6ea5f1cfbdc373774764a0c45f * touchstart, touchend, touchmove, and touchcancel * diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp -index 996ee2edde76bab0ea409e072b89160a5158d452..7833fe75af2a85666e72627bfd0dd7467a1b8a80 100644 +index 2e69547612aa1ef91d726ee54475a5f8e638e214..2b7aae36d1b4b7a7a7a7a6117104d3455e1768e3 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp -@@ -1668,6 +1668,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, +@@ -1647,6 +1647,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, if (postLayerization) { postLayerization->Register(); } @@ -1592,7 +1539,7 @@ index 5aa445d2e0a6169e57c44569974d557b3baf7064..671f71979b407f0ca17c66f13805e851 } diff --git a/dom/media/systemservices/video_engine/desktop_capture_impl.cc b/dom/media/systemservices/video_engine/desktop_capture_impl.cc -index 3f03789fa3948bbf2528975ce112efb7eb987c24..2194e4144de537edb9a765857cc37b0af42dd8fd 100644 +index 532605b813d4e4a7693e6c82f6792075ef2565de..d225ffaf3439739d989b21f858c1763942beee9e 100644 --- a/dom/media/systemservices/video_engine/desktop_capture_impl.cc +++ b/dom/media/systemservices/video_engine/desktop_capture_impl.cc @@ -135,11 +135,12 @@ int32_t ScreenDeviceInfoImpl::GetOrientation(const char* aDeviceUniqueIdUTF8, @@ -1611,7 +1558,7 @@ index 3f03789fa3948bbf2528975ce112efb7eb987c24..2194e4144de537edb9a765857cc37b0a } int32_t WindowDeviceInfoImpl::Init() { -@@ -405,7 +406,7 @@ static bool UsePipewire() { +@@ -412,7 +413,7 @@ static bool UsePipewire() { static std::unique_ptr CreateDesktopCapturerAndThread( CaptureDeviceType aDeviceType, DesktopCapturer::SourceId aSourceId, @@ -1620,7 +1567,7 @@ index 3f03789fa3948bbf2528975ce112efb7eb987c24..2194e4144de537edb9a765857cc37b0a DesktopCaptureOptions options = CreateDesktopCaptureOptions(); std::unique_ptr capturer; -@@ -455,8 +456,10 @@ static std::unique_ptr CreateDesktopCapturerAndThread( +@@ -462,8 +463,10 @@ static std::unique_ptr CreateDesktopCapturerAndThread( capturer->SelectSource(aSourceId); @@ -1633,7 +1580,7 @@ index 3f03789fa3948bbf2528975ce112efb7eb987c24..2194e4144de537edb9a765857cc37b0a } else if (aDeviceType == CaptureDeviceType::Browser) { // XXX We don't capture cursors, so avoid the extra indirection layer. We // could also pass null for the pMouseCursorMonitor. -@@ -473,7 +476,8 @@ static std::unique_ptr CreateDesktopCapturerAndThread( +@@ -480,7 +483,8 @@ static std::unique_ptr CreateDesktopCapturerAndThread( } DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId, @@ -1643,7 +1590,7 @@ index 3f03789fa3948bbf2528975ce112efb7eb987c24..2194e4144de537edb9a765857cc37b0a : mModuleId(aId), mTrackingId(mozilla::TrackingId(CaptureEngineToTrackingSourceStr([&] { switch (aType) { -@@ -490,6 +494,7 @@ DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId, +@@ -497,6 +501,7 @@ DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId, aId)), mDeviceUniqueId(aUniqueId), mDeviceType(aType), @@ -1651,7 +1598,7 @@ index 3f03789fa3948bbf2528975ce112efb7eb987c24..2194e4144de537edb9a765857cc37b0a mControlThread(mozilla::GetCurrentSerialEventTarget()), mNextFrameMinimumTime(Timestamp::Zero()), mCallbacks("DesktopCaptureImpl::mCallbacks") {} -@@ -514,6 +519,19 @@ void DesktopCaptureImpl::DeRegisterCaptureDataCallback( +@@ -521,6 +526,19 @@ void DesktopCaptureImpl::DeRegisterCaptureDataCallback( } } @@ -1671,7 +1618,7 @@ index 3f03789fa3948bbf2528975ce112efb7eb987c24..2194e4144de537edb9a765857cc37b0a int32_t DesktopCaptureImpl::StopCaptureIfAllClientsClose() { { auto callbacks = mCallbacks.Lock(); -@@ -546,7 +564,7 @@ int32_t DesktopCaptureImpl::StartCapture( +@@ -553,7 +571,7 @@ int32_t DesktopCaptureImpl::StartCapture( DesktopCapturer::SourceId sourceId = std::stoi(mDeviceUniqueId); std::unique_ptr capturer = CreateDesktopCapturerAndThread( @@ -1680,7 +1627,7 @@ index 3f03789fa3948bbf2528975ce112efb7eb987c24..2194e4144de537edb9a765857cc37b0a MOZ_ASSERT(!capturer == !mCaptureThread); if (!capturer) { -@@ -647,6 +665,15 @@ void DesktopCaptureImpl::OnCaptureResult(DesktopCapturer::Result aResult, +@@ -654,6 +672,15 @@ void DesktopCaptureImpl::OnCaptureResult(DesktopCapturer::Result aResult, frameInfo.height = aFrame->size().height(); frameInfo.videoType = VideoType::kARGB; @@ -1786,7 +1733,7 @@ index 64eadb140131ea807db8b55431630523342a88ce..371e093fef6e225302da6aaf6e1ede05 const nsCOMPtr mControlThread; // Set in StartCapture. diff --git a/dom/script/ScriptSettings.cpp b/dom/script/ScriptSettings.cpp -index 1f2d92bcb5d989bf9ecc044f8c51006f991b0007..9cf5dd885e658e0fe5e7ab75e7fc1f97a8d214b8 100644 +index 3b39538e51840cd9b1685b2efd2ff2e9ec83608a..c7bf4f2d53b58bbacb22b3ebebf6f3fc9b5e445f 100644 --- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -150,6 +150,30 @@ ScriptSettingsStackEntry::~ScriptSettingsStackEntry() { @@ -1877,10 +1824,10 @@ 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 9c49ad97054ec46cfc52082202d36bb2b53482fc..46d22e51cfeaded274e63b9673e0c3c83b517e7a 100644 +index 4fc3db17b202f768cadc7fa3921badc4374a9f7a..be58abf2b7906e7307dd8f106f5f8bd2206793bc 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp -@@ -986,7 +986,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) { +@@ -985,7 +985,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) { AssertIsOnMainThread(); nsTArray languages; @@ -1889,7 +1836,7 @@ index 9c49ad97054ec46cfc52082202d36bb2b53482fc..46d22e51cfeaded274e63b9673e0c3c8 RuntimeService* runtime = RuntimeService::GetService(); if (runtime) { -@@ -1173,8 +1173,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) { +@@ -1172,8 +1172,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) { } // The navigator overridden properties should have already been read. @@ -1899,7 +1846,7 @@ index 9c49ad97054ec46cfc52082202d36bb2b53482fc..46d22e51cfeaded274e63b9673e0c3c8 mNavigatorPropertiesLoaded = true; } -@@ -1778,6 +1777,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted( +@@ -1772,6 +1771,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted( } } @@ -1913,7 +1860,7 @@ index 9c49ad97054ec46cfc52082202d36bb2b53482fc..46d22e51cfeaded274e63b9673e0c3c8 template void RuntimeService::BroadcastAllWorkers(const Func& aFunc) { AssertIsOnMainThread(); -@@ -2295,6 +2301,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers( +@@ -2287,6 +2293,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers( } } @@ -1955,17 +1902,17 @@ index d10dabb5c5ff8e17851edf2bd2efc08e74584d8e..53c4070c5fde43b27fb8fbfdcf4c23d8 bool IsWorkerGlobal(JSObject* global); diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp -index f7a8cec1ef69778b4579b72c58adeaf91315299e..87c18442e643a980cb07d43b9b1005c90953770b 100644 +index ed8a07bcdc25f77a93079c8b9f113c59b9befaf9..2f09a961603f28a658f2d29321a389b7623482b9 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp -@@ -711,6 +711,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { +@@ -679,6 +679,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { } }; +class ResetDefaultLocaleRunnable final : public WorkerControlRunnable { + public: + explicit ResetDefaultLocaleRunnable(WorkerPrivate* aWorkerPrivate) -+ : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {} ++ : WorkerControlRunnable(aWorkerPrivate, WorkerThread) {} + + virtual bool WorkerRun(JSContext* aCx, + WorkerPrivate* aWorkerPrivate) override { @@ -1977,7 +1924,7 @@ index f7a8cec1ef69778b4579b72c58adeaf91315299e..87c18442e643a980cb07d43b9b1005c9 class UpdateLanguagesRunnable final : public WorkerRunnable { nsTArray mLanguages; -@@ -2006,6 +2018,16 @@ void WorkerPrivate::UpdateContextOptions( +@@ -1949,6 +1961,16 @@ void WorkerPrivate::UpdateContextOptions( } } @@ -1994,7 +1941,7 @@ index f7a8cec1ef69778b4579b72c58adeaf91315299e..87c18442e643a980cb07d43b9b1005c9 void WorkerPrivate::UpdateLanguages(const nsTArray& aLanguages) { AssertIsOnParentThread(); -@@ -5417,6 +5439,15 @@ void WorkerPrivate::UpdateContextOptionsInternal( +@@ -5450,6 +5472,15 @@ void WorkerPrivate::UpdateContextOptionsInternal( } } @@ -2011,10 +1958,10 @@ index f7a8cec1ef69778b4579b72c58adeaf91315299e..87c18442e643a980cb07d43b9b1005c9 const nsTArray& aLanguages) { WorkerGlobalScope* globalScope = GlobalScope(); diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h -index 6eb840b8e64b5a4db3c621599780561ef2fdaef4..59e27e6949dfda389516d64a7b4ac0f0e5a60148 100644 +index a134d4846f9ad0f273f61fe2c2daa10af7b3c7a6..38bb391cb542a560f93a0c302a1d942aad8135a6 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h -@@ -413,6 +413,8 @@ class WorkerPrivate final +@@ -417,6 +417,8 @@ class WorkerPrivate final void UpdateContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContextOptions); @@ -2023,7 +1970,7 @@ index 6eb840b8e64b5a4db3c621599780561ef2fdaef4..59e27e6949dfda389516d64a7b4ac0f0 void UpdateLanguagesInternal(const nsTArray& aLanguages); void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, -@@ -1032,6 +1034,8 @@ class WorkerPrivate final +@@ -1036,6 +1038,8 @@ class WorkerPrivate final void UpdateContextOptions(const JS::ContextOptions& aContextOptions); @@ -2072,10 +2019,10 @@ index 9d0423ef13958d5c443cc3531269603c4801c338..f0c4ba7c528d2be466e0f7669a1e37e8 * Set the default time zone. */ diff --git a/js/public/Date.h b/js/public/Date.h -index dd82415624e1f05eaad818d68b8588ffb1b64ab1..c48ab77757aff777658fd4e37db6bdea47bdff32 100644 +index 422ea0b2c0daafd4447d0d83500ea73545c99b76..7f03adc302112e0de3d842d7ce6bc0909b50f76a 100644 --- a/js/public/Date.h +++ b/js/public/Date.h -@@ -53,6 +53,8 @@ namespace JS { +@@ -54,6 +54,8 @@ namespace JS { */ extern JS_PUBLIC_API void ResetTimeZone(); @@ -2085,10 +2032,10 @@ index dd82415624e1f05eaad818d68b8588ffb1b64ab1..c48ab77757aff777658fd4e37db6bdea inline ClippedTime TimeClip(double time); diff --git a/js/src/debugger/Object.cpp b/js/src/debugger/Object.cpp -index 49525b426a9f8656a471192ccf62f47f555a90a4..f6c832af063326a5e9e7166662bb21bc4e41cdca 100644 +index 2cb72597fdf2a10fdc53bf03d6cc98d9bc16af58..b3ce8a99c37fc8c503206ea2b47b7e000fc4bf3b 100644 --- a/js/src/debugger/Object.cpp +++ b/js/src/debugger/Object.cpp -@@ -2413,7 +2413,11 @@ Maybe DebuggerObject::call(JSContext* cx, +@@ -2446,7 +2446,11 @@ Maybe DebuggerObject::call(JSContext* cx, invokeArgs[i].set(args2[i]); } @@ -2255,10 +2202,10 @@ index dac899f7558b26d6848da8b98ed8a93555c8751a..2a07d67fa1c2840b25085566e84dc3b2 // No boxes to return return; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp -index bb625a6dda2475164d3236ed03ee248bfc2f6d66..6e6d5aca3701c4eb2b69e74226cf6ed8b10b65cd 100644 +index 6e27e39f432d678376cec3bfd8491e5f20ebd893..a69211527a0a2411b6ef4a69de0183428114139d 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp -@@ -10925,7 +10925,9 @@ bool PresShell::ComputeActiveness() const { +@@ -10906,7 +10906,9 @@ bool PresShell::ComputeActiveness() const { if (!browserChild->IsVisible()) { MOZ_LOG(gLog, LogLevel::Debug, (" > BrowserChild %p is not visible", browserChild)); @@ -2270,10 +2217,10 @@ index bb625a6dda2475164d3236ed03ee248bfc2f6d66..6e6d5aca3701c4eb2b69e74226cf6ed8 // If the browser is visible but just due to be preserving layers diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h -index 1d5d60bb4e7cc0d93ff7e6662c9102bde59509c1..ee1436d6e06f13a4386314e8bb8e4e3998ae5a0c 100644 +index 327b3d587844c0ad81f5b9c2c622bf52315380bf..d5eefde23dfa3d5666f04af90b7dda1ffa38f6bd 100644 --- a/layout/style/GeckoBindings.h +++ b/layout/style/GeckoBindings.h -@@ -630,6 +630,7 @@ float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*); +@@ -632,6 +632,7 @@ float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*); bool Gecko_MediaFeatures_PrefersReducedMotion(const mozilla::dom::Document*); bool Gecko_MediaFeatures_PrefersReducedTransparency( const mozilla::dom::Document*); @@ -2282,7 +2229,7 @@ index 1d5d60bb4e7cc0d93ff7e6662c9102bde59509c1..ee1436d6e06f13a4386314e8bb8e4e39 const mozilla::dom::Document*); mozilla::StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme( diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp -index 5e4fe65abcc373e6c0fba40458677cebb085266b..9425984faca3579cb90e96ae46ed47e66c2dc664 100644 +index f10c779512c8d6a778ccd2fe98f069745a58adab..8534639ff3c256820846c6e014185783bb317efe 100644 --- a/layout/style/nsMediaFeatures.cpp +++ b/layout/style/nsMediaFeatures.cpp @@ -261,11 +261,11 @@ bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) { @@ -2302,36 +2249,21 @@ index 5e4fe65abcc373e6c0fba40458677cebb085266b..9425984faca3579cb90e96ae46ed47e6 } bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document* aDocument) { -diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js -index a38a760f9c5ce666ad8d51d46f7685c9ac45d7b3..ed4d6bf2512f2428781e83612643230a5974d80a 100644 ---- a/modules/libpref/init/all.js -+++ b/modules/libpref/init/all.js -@@ -3933,7 +3933,9 @@ pref("devtools.f12_enabled", true); - // doesn't provide a way to lock the pref - pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", false); - #else --pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", false, locked); -+// Playwright: DO NOT make preference locked so that we can overwrite it -+// later in our playwright.cfg file. -+pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", false); - #endif - - // Whether sites require the open-protocol-handler permission to open a diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp -index 164a1c1457ef21a6e1019caf9d1e710649e9f754..07fd3ecbabc103599204606024d8e0403345f02e 100644 +index 7e8b75f0156b059c19c9a2322087c2d79d28f961..d8899ca4ec66a1d1cafba5cb09205fa3b7d41384 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp -@@ -641,7 +641,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) - mUnstrippedURI(rhs.mUnstrippedURI), +@@ -647,7 +647,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) mInterceptionInfo(rhs.mInterceptionInfo), mHasInjectedCookieForCookieBannerHandling( -- rhs.mHasInjectedCookieForCookieBannerHandling) {} -+ rhs.mHasInjectedCookieForCookieBannerHandling), -+ mJugglerLoadIdentifier(rhs.mJugglerLoadIdentifier) {} + rhs.mHasInjectedCookieForCookieBannerHandling), +- mWasSchemelessInput(rhs.mWasSchemelessInput) { ++ mWasSchemelessInput(rhs.mWasSchemelessInput), ++ mJugglerLoadIdentifier(rhs.mJugglerLoadIdentifier) { + } LoadInfo::LoadInfo( - nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal, -@@ -2321,4 +2322,16 @@ LoadInfo::SetHasInjectedCookieForCookieBannerHandling( +@@ -2363,4 +2364,16 @@ LoadInfo::SetWasSchemelessInput(bool aWasSchemelessInput) { return NS_OK; } @@ -2349,23 +2281,23 @@ index 164a1c1457ef21a6e1019caf9d1e710649e9f754..07fd3ecbabc103599204606024d8e040 + } // namespace mozilla::net diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h -index f97d2389297ea1c4771ae2c7e55a0b3eade0743a..89c47840301480ffce5dd385bb5b0a34bfdd7390 100644 +index a8631b09b26708ca10f683f1a9dd6b8467d3fe8e..a0bd72113e3539d815d32382946581ee62f39b6c 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h -@@ -387,6 +387,8 @@ class LoadInfo final : public nsILoadInfo { - nsCOMPtr mInterceptionInfo; +@@ -401,6 +401,8 @@ class LoadInfo final : public nsILoadInfo { bool mHasInjectedCookieForCookieBannerHandling = false; + bool mWasSchemelessInput = false; + + 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 37b0b7bfe516ca69441e4cdd58861de9d595c692..6d3bb900624b6ad9e9449ce6f462a87dacfd4cb9 100644 +index 920e7623a7f912296fc23361f66ab35a30c35f1e..dfea0d0f7a72da9699615d7ff778e429e7ae40fb 100644 --- a/netwerk/base/TRRLoadInfo.cpp +++ b/netwerk/base/TRRLoadInfo.cpp -@@ -845,5 +845,16 @@ TRRLoadInfo::SetHasInjectedCookieForCookieBannerHandling( +@@ -861,5 +861,15 @@ TRRLoadInfo::SetWasSchemelessInput(bool aWasSchemelessInput) { return NS_ERROR_NOT_IMPLEMENTED; } @@ -2378,18 +2310,17 @@ index 37b0b7bfe516ca69441e4cdd58861de9d595c692..6d3bb900624b6ad9e9449ce6f462a87d +TRRLoadInfo::SetJugglerLoadIdentifier(uint64_t aResult) { + return NS_ERROR_NOT_IMPLEMENTED; +} -+ + } // namespace net } // namespace mozilla diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl -index 7f3422274f7c075fcd6486ae5b8f5cd073aa1ccc..b667ba1e7dcea7e5d31f27df211553929e069993 100644 +index ccceadc42ce12bec8e9878124904f0ba3914a030..56ec55af399e7b831558ca5d2a71513d9c919839 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl -@@ -1505,4 +1505,6 @@ interface nsILoadInfo : nsISupports - * handle a cookie banner. This is only done for top-level requests. +@@ -1531,4 +1531,6 @@ interface nsILoadInfo : nsISupports + * Whether the load has gone through the URL bar, where the fixup had to add * the protocol scheme. */ - [infallible] attribute boolean hasInjectedCookieForCookieBannerHandling; + [infallible] attribute boolean wasSchemelessInput; + + [infallible] attribute unsigned long long jugglerLoadIdentifier; }; @@ -2406,10 +2337,10 @@ index d72dc570dc82ff9d576942b9e7c23d8a74d68049..a5fcddc4b0e53a862e5a77120b4ccff8 /** * Set the status and reason for the forthcoming synthesized response. diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp -index ec7f54c1cc7a177e6487f4bc317ef28bfa34f348..8e34230df1183b408a8f6439965c0b48826aa974 100644 +index 995560e4818c69f89959e36164fa9eba39925037..3d92280cfd73c5b09926151aac36857775f48e30 100644 --- a/netwerk/ipc/DocumentLoadListener.cpp +++ b/netwerk/ipc/DocumentLoadListener.cpp -@@ -165,6 +165,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext, +@@ -167,6 +167,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext, loadInfo->SetHasValidUserGestureActivation( aLoadState->HasValidUserGestureActivation()); loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh()); @@ -2418,10 +2349,10 @@ index ec7f54c1cc7a177e6487f4bc317ef28bfa34f348..8e34230df1183b408a8f6439965c0b48 return loadInfo.forget(); } diff --git a/netwerk/protocol/http/InterceptedHttpChannel.cpp b/netwerk/protocol/http/InterceptedHttpChannel.cpp -index 03a2605c5d5591f7656ba4c4ff9a46f2e390c404..717f87b632995cb955fce5995604153ae4084561 100644 +index 4bee70faf21db77daf381f40a562840470f788f4..93986ccb252bca67f2a0b291c915d523f5dad9eb 100644 --- a/netwerk/protocol/http/InterceptedHttpChannel.cpp +++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp -@@ -729,6 +729,14 @@ NS_IMPL_ISUPPORTS(ResetInterceptionHeaderVisitor, nsIHttpHeaderVisitor) +@@ -727,6 +727,14 @@ NS_IMPL_ISUPPORTS(ResetInterceptionHeaderVisitor, nsIHttpHeaderVisitor) } // anonymous namespace @@ -2436,7 +2367,7 @@ index 03a2605c5d5591f7656ba4c4ff9a46f2e390c404..717f87b632995cb955fce5995604153a NS_IMETHODIMP InterceptedHttpChannel::ResetInterception(bool aBypass) { INTERCEPTED_LOG(("InterceptedHttpChannel::ResetInterception [%p] bypass: %s", -@@ -1072,11 +1080,18 @@ InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) { +@@ -1070,11 +1078,18 @@ InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) { GetCallback(mProgressSink); } @@ -2456,10 +2387,10 @@ index 03a2605c5d5591f7656ba4c4ff9a46f2e390c404..717f87b632995cb955fce5995604153a if (mPump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) { mPump->PeekStream(CallTypeSniffers, static_cast(this)); diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp -index b2423f754063dd42ad80a607dc7a39ddc2bf89b3..99ce1ebd141ff1d2f6982b2fa8ed485c658d3abe 100644 +index 125728fa73180bbe57bfc1f53379ec637f108eb7..5fd60bebebbfa0f2de99cb14762cf0d52dbdb0cb 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp -@@ -1375,6 +1375,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta( +@@ -1376,6 +1376,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta( void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -2544,7 +2475,7 @@ index 6dfd07d6b676a99993408921de8dea9d561f201d..e3c6794363cd6336effbeac83a179f37 readonly attribute boolean securityCheckDisabled; }; diff --git a/services/settings/Utils.sys.mjs b/services/settings/Utils.sys.mjs -index d9150337a18c0d41fab89c46a3792049df89bc84..0257ee0899355c315da7f632deec645738732663 100644 +index 73c83e526be1a3a252f995d0718e3975d50bffa7..4b05141c8ab8b084d977341127f36ea6d226aa9a 100644 --- a/services/settings/Utils.sys.mjs +++ b/services/settings/Utils.sys.mjs @@ -95,7 +95,7 @@ function _isUndefined(value) { @@ -2557,10 +2488,10 @@ index d9150337a18c0d41fab89c46a3792049df89bc84..0257ee0899355c315da7f632deec6457 : AppConstants.REMOTE_SETTINGS_SERVER_URL; }, diff --git a/servo/components/style/gecko/media_features.rs b/servo/components/style/gecko/media_features.rs -index 986c5fe7d8dc07ae71057cb1cd778566184cd137..bf83c7504b457383f02ec4d211c0659ad3ca415b 100644 +index 014248a26f7b070850edf1d7db0990fd8ef26a10..9846ae282d959c406f15052791aae3ec9351326f 100644 --- a/servo/components/style/gecko/media_features.rs +++ b/servo/components/style/gecko/media_features.rs -@@ -291,10 +291,15 @@ pub enum ForcedColors { +@@ -293,10 +293,15 @@ pub enum ForcedColors { /// https://drafts.csswg.org/mediaqueries-5/#forced-colors fn eval_forced_colors(context: &Context, query_value: Option) -> bool { @@ -2611,10 +2542,10 @@ index 61eda006f090e391b3c0f209e9400920f480c115..8fea8caf17913a2736884caca8f6087f if (provider.failed) { diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp -index 26a414b5df26e43f2e3fa4ef539190021a167b04..e8d33f28f2696a4ecee3af2668747c7bfb803a2e 100644 +index 3314cb813f6ceb67096eeda0864ad3b16c0616cb..5aac63649e186d624a9905a5d16513f8353f5515 100644 --- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp -@@ -370,7 +370,7 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) { +@@ -371,7 +371,7 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) { nsCOMPtr windowEnumerator; nsCOMPtr mediator( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); @@ -2639,10 +2570,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 9c8e6a919263899f95bc41b1ca419d5048e50b2a..4b3ad73393c24268b6df62823e56bcf4367cac21 100644 +index 144924a7a6b3d11c0de3992d1e1f99a04f7611b4..b7646200f7843d42ef3fb1919e5a1d3057eaf14c 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp +++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp -@@ -1882,7 +1882,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent( +@@ -1880,7 +1880,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent( // Open a minimal popup. *aIsPopupRequested = true; @@ -2656,10 +2587,10 @@ index 9c8e6a919263899f95bc41b1ca419d5048e50b2a..4b3ad73393c24268b6df62823e56bcf4 /** diff --git a/toolkit/mozapps/update/UpdateService.sys.mjs b/toolkit/mozapps/update/UpdateService.sys.mjs -index e79ef9a551dd4fd0854bbfae56d4d7ee105a70c7..814c53dcc65faf3441a66d253dbcd59363ee32ac 100644 +index fcb142e5d74ab40515fd6e3b7848a661771d459c..592d4b759958283b1c53c52a648f35f97110705b 100644 --- a/toolkit/mozapps/update/UpdateService.sys.mjs +++ b/toolkit/mozapps/update/UpdateService.sys.mjs -@@ -3816,6 +3816,8 @@ UpdateService.prototype = { +@@ -3853,6 +3853,8 @@ UpdateService.prototype = { }, get disabledForTesting() { @@ -2669,10 +2600,10 @@ index e79ef9a551dd4fd0854bbfae56d4d7ee105a70c7..814c53dcc65faf3441a66d253dbcd593 (Cu.isInAutomation || lazy.Marionette.running || diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild -index d2ccd8748228b04c84754f9a6dce2ca3bf991e47..d3a8ea1d9994f724cd52cecd4d2cd2851b81dee9 100644 +index 2e3721e8a5807936c02cce6c21240df3b4c92c97..ecd1ca11d96c14ba6db4b09d4d9c10ef757e690b 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild -@@ -154,6 +154,7 @@ if CONFIG['ENABLE_WEBDRIVER']: +@@ -155,6 +155,7 @@ if CONFIG['ENABLE_WEBDRIVER']: '/remote', '/testing/firefox-ui', '/testing/marionette', @@ -2716,7 +2647,7 @@ index 7eb9e1104682d4eb47060654f43a1efa8b2a6bb2..a8315d6decf654b5302bea5beeea3414 // Only run this code if LauncherProcessWin.h was included beforehand, thus // signalling that the hosting process should support launcher mode. diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp -index f779f0028e87a0fd1d7f633c9c23872f9d840cf6..29a2ec96eb52fd3b09994c63a0d5280437be2854 100644 +index d54be4a4d7697851cc5279a39a49b06c745b7630..abb455116db3011dc7f84df86631da5eb7030700 100644 --- a/uriloader/base/nsDocLoader.cpp +++ b/uriloader/base/nsDocLoader.cpp @@ -828,6 +828,13 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout, @@ -2946,10 +2877,10 @@ index 1c25e9d9a101233f71e92288a0f93125b81ac1c5..22cf67b0f6e3ddd2b3ed725a314ba6a9 } #endif diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h -index 5e20484e043e070dd8a6d7ee5ecab939435efc2c..f721212f147d01a8824e67c26216f249032254bf 100644 +index 7ad7d82cd68810d9557adaa31ad5c7cb80bfb079..7a3d0116ce924e1e26a1cc6976abe11ed6f4a62c 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h -@@ -204,6 +204,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, +@@ -258,6 +258,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, : mReason(eReal), mContextMenuTrigger(eNormal), mClickCount(0), @@ -2957,7 +2888,7 @@ index 5e20484e043e070dd8a6d7ee5ecab939435efc2c..f721212f147d01a8824e67c26216f249 mIgnoreRootScrollFrame(false), mClickEventPrevented(false) {} -@@ -215,6 +216,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, +@@ -269,6 +270,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, mReason(aReason), mContextMenuTrigger(eNormal), mClickCount(0), @@ -2965,7 +2896,7 @@ index 5e20484e043e070dd8a6d7ee5ecab939435efc2c..f721212f147d01a8824e67c26216f249 mIgnoreRootScrollFrame(false), mClickEventPrevented(false) {} -@@ -234,6 +236,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, +@@ -288,6 +290,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, mReason(aReason), mContextMenuTrigger(aContextMenuTrigger), mClickCount(0), @@ -2973,7 +2904,7 @@ index 5e20484e043e070dd8a6d7ee5ecab939435efc2c..f721212f147d01a8824e67c26216f249 mIgnoreRootScrollFrame(false), mClickEventPrevented(false) { if (aMessage == eContextMenu) { -@@ -282,6 +285,9 @@ class WidgetMouseEvent : public WidgetMouseEventBase, +@@ -336,6 +339,9 @@ class WidgetMouseEvent : public WidgetMouseEventBase, // Otherwise, this must be 0. uint32_t mClickCount; @@ -2983,7 +2914,7 @@ index 5e20484e043e070dd8a6d7ee5ecab939435efc2c..f721212f147d01a8824e67c26216f249 // Whether the event should ignore scroll frame bounds during dispatch. bool mIgnoreRootScrollFrame; -@@ -294,6 +300,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, +@@ -348,6 +354,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, mExitFrom = aEvent.mExitFrom; mClickCount = aEvent.mClickCount; @@ -3054,6 +2985,31 @@ index e4bdf715e2fb899e97a5bfeb2e147127460d6047..3554f919480278b7353617481c7ce805 break; } if (aEvent.IsMeta()) { +diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm +index f3a760476290953df2179fa44bd68dd171d291ab..6cd3f8178d260904905372c1afc7ecbcc568fbc8 100644 +--- a/widget/cocoa/nsCocoaUtils.mm ++++ b/widget/cocoa/nsCocoaUtils.mm +@@ -267,14 +267,17 @@ static float MenuBarScreenHeight() { + return nullptr; + } + +- nsCOMPtr hiddenWindowWidget; ++ nsCOMPtr mainWindowWidget; + if (NS_FAILED(baseHiddenWindow->GetMainWidget( +- getter_AddRefs(hiddenWindowWidget)))) { ++ getter_AddRefs(mainWindowWidget)))) { + NS_WARNING("Couldn't get nsIWidget from hidden window (nsIBaseWindow)"); + return nullptr; + } + +- return hiddenWindowWidget; ++ // In the case of headless mode, it's a HeadlessWidget while the callee expects a nsCocoaWindow ++ if (gfxPlatform::IsHeadless()) ++ return nullptr; ++ return mainWindowWidget; + } + + BOOL nsCocoaUtils::WasLaunchedAtLogin() { diff --git a/widget/headless/HeadlessCompositorWidget.cpp b/widget/headless/HeadlessCompositorWidget.cpp index bb4ee9175e66dc40de1871a7f91368fe309494a3..747625e3869882300bfbc18b184db5151dd90c1a 100644 --- a/widget/headless/HeadlessCompositorWidget.cpp @@ -3246,7 +3202,7 @@ index 9856991ef32f25f51942f8cd664a09bec2192c70..948947a421179e91c51005aeb83ed0d1 ~HeadlessWidget(); bool mEnabled; diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h -index e8b831233338630c3106fd9debeba128228d3e0c..60422b1a1734e1bdeba7b6083727e29f0e5e9f35 100644 +index a3d7aaf1b6c651fedbab7875c1a38647103d08ed..ac3b09e50216524cce90d96b013007f7b07472da 100644 --- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -234,6 +234,7 @@ struct ParamTraits { diff --git a/playwright/browser_patches/firefox/preferences/playwright.cfg b/playwright/browser_patches/firefox/preferences/playwright.cfg index 49976d7fd7..e5b7bdc953 100644 --- a/playwright/browser_patches/firefox/preferences/playwright.cfg +++ b/playwright/browser_patches/firefox/preferences/playwright.cfg @@ -6,6 +6,8 @@ pref("dom.input_events.security.minNumTicks", 0); pref("dom.input_events.security.minTimeElapsedInMS", 0); +pref("dom.iframe_lazy_loading.enabled", false); + pref("datareporting.policy.dataSubmissionEnabled", false); pref("datareporting.policy.dataSubmissionPolicyAccepted", false); pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true); @@ -97,9 +99,6 @@ pref("security.enterprise_roots.enabled", true); // See AppShutdown.cpp for more details on shutdown phases. pref("toolkit.shutdown.fastShutdownStage", 3); -// @see https://github.com/microsoft/playwright/issues/8178 -pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", true); - // Use light theme by default. pref("ui.systemUsesDarkTheme", 0); diff --git a/playwright/browser_patches/webkit/UPSTREAM_CONFIG.sh b/playwright/browser_patches/webkit/UPSTREAM_CONFIG.sh index aa9e856e37..1260c15690 100644 --- a/playwright/browser_patches/webkit/UPSTREAM_CONFIG.sh +++ b/playwright/browser_patches/webkit/UPSTREAM_CONFIG.sh @@ -1,3 +1,3 @@ REMOTE_URL="https://github.com/WebKit/WebKit.git" BASE_BRANCH="main" -BASE_REVISION="bc0bc692bc9e368bbd9d530322db73b374cd6268" +BASE_REVISION="3db3a794a844d2c7e4cda8fc6a7588f8e62ee85a" diff --git a/playwright/browser_patches/webkit/patches/bootstrap.diff b/playwright/browser_patches/webkit/patches/bootstrap.diff index e686675ca5..70ccfe70c5 100644 --- a/playwright/browser_patches/webkit/patches/bootstrap.diff +++ b/playwright/browser_patches/webkit/patches/bootstrap.diff @@ -239,10 +239,10 @@ index 80559d39722b74e325d513ea22054b0d399a4e8f..24977f9dfcfcdb29a20d178be608aca8 bool m_isPaused { false }; }; diff --git a/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp b/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp -index 1c40b312afeca605fc8a6aefd993749fce3f3676..b7fbac213249fd15233b60e11a38aae7b934cf7e 100644 +index 15d2592d36b402bf2210dc4970ff40957022e84a..99ac14dc75ff5296e1f6c42bdbf8c128f2d82254 100644 --- a/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp +++ b/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp -@@ -218,6 +218,14 @@ void JSGlobalObjectConsoleClient::screenshot(JSGlobalObject*, Ref&&) final; void recordEnd(JSC::JSGlobalObject*, Ref&&) final; void screenshot(JSC::JSGlobalObject*, Ref&&) final; @@ -270,10 +270,10 @@ index 6e573c4dfd1f356b76ef9b46dcee4254e9a28f27..8855604064f5130211baab6caa89318c void warnUnimplemented(const String& method); void internalAddMessage(MessageType, MessageLevel, JSC::JSGlobalObject*, Ref&&); diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp -index 4c0b84a3ce88997372b05e54761fbd1d16350c84..5a8da96c4772f998efb188043484c6ba26f73cf0 100644 +index 594584db96a3de67b92910f472f1403bc9b26ce2..8ba453f9ae8726ccfb26c55180371c20bed6962e 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp +++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp -@@ -191,9 +191,8 @@ void InspectorRuntimeAgent::callFunctionOn(const Protocol::Runtime::RemoteObject +@@ -194,9 +194,8 @@ void InspectorRuntimeAgent::callFunctionOn(const Protocol::Runtime::RemoteObject void InspectorRuntimeAgent::callFunctionOn(InjectedScript& injectedScript, const Protocol::Runtime::RemoteObjectId& objectId, const String& functionDeclaration, RefPtr&& arguments, std::optional&& doNotPauseOnExceptionsAndMuteConsole, std::optional&& returnByValue, std::optional&& generatePreview, std::optional&& /* emulateUserGesture */, std::optional&& awaitPromise, Ref&& callback) { ASSERT(!injectedScript.hasNoValue()); @@ -284,7 +284,7 @@ index 4c0b84a3ce88997372b05e54761fbd1d16350c84..5a8da96c4772f998efb188043484c6ba bool pauseAndMute = doNotPauseOnExceptionsAndMuteConsole.value_or(false); if (pauseAndMute) { temporarilyDisableExceptionBreakpoints.replace(); -@@ -212,6 +211,11 @@ void InspectorRuntimeAgent::callFunctionOn(InjectedScript& injectedScript, const +@@ -215,6 +214,11 @@ void InspectorRuntimeAgent::callFunctionOn(InjectedScript& injectedScript, const unmuteConsole(); } @@ -297,10 +297,10 @@ index 4c0b84a3ce88997372b05e54761fbd1d16350c84..5a8da96c4772f998efb188043484c6ba { Protocol::ErrorString errorString; diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h -index 5c3488200ab2df6dfc914ff780f05eba7ffd92a2..11e6a6a9b027f2e4ea904e796019ee2a698509cf 100644 +index 816633a6dfc75a1248f6edb44807e5d4f602568c..687fb7dadfad9357e15a27e0869fa145c46fb39a 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h +++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h -@@ -63,6 +63,7 @@ public: +@@ -64,6 +64,7 @@ public: Protocol::ErrorStringOr, std::optional /* wasThrown */, std::optional /* savedResultIndex */>> evaluate(const String& expression, const String& objectGroup, std::optional&& includeCommandLineAPI, std::optional&& doNotPauseOnExceptionsAndMuteConsole, std::optional&&, std::optional&& returnByValue, std::optional&& generatePreview, std::optional&& saveResult, std::optional&& emulateUserGesture) override; void awaitPromise(const Protocol::Runtime::RemoteObjectId&, std::optional&& returnByValue, std::optional&& generatePreview, std::optional&& saveResult, Ref&&) final; void callFunctionOn(const Protocol::Runtime::RemoteObjectId&, const String& functionDeclaration, RefPtr&& arguments, std::optional&& doNotPauseOnExceptionsAndMuteConsole, std::optional&& returnByValue, std::optional&& generatePreview, std::optional&& emulateUserGesture, std::optional&& awaitPromise, Ref&&) override; @@ -309,10 +309,10 @@ index 5c3488200ab2df6dfc914ff780f05eba7ffd92a2..11e6a6a9b027f2e4ea904e796019ee2a Protocol::ErrorStringOr> getPreview(const Protocol::Runtime::RemoteObjectId&) final; Protocol::ErrorStringOr>, RefPtr>>> getProperties(const Protocol::Runtime::RemoteObjectId&, std::optional&& ownProperties, std::optional&& fetchStart, std::optional&& fetchCount, std::optional&& generatePreview) final; diff --git a/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp -index 508eb02ec95c52408384a1e2b77648afd426dd9d..93d6757e170272cda8c346bf51578d2b5f8aafaa 100644 +index e47c6ca59f37fbf18ca8a393df72e0472363fabd..b393465540595220561ae00afb85408279710864 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp +++ b/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp -@@ -87,6 +87,34 @@ Protocol::ErrorStringOr InspectorTargetAgent::sendMessageToTarget(const St +@@ -90,6 +90,34 @@ Protocol::ErrorStringOr InspectorTargetAgent::sendMessageToTarget(const St return { }; } @@ -347,7 +347,7 @@ index 508eb02ec95c52408384a1e2b77648afd426dd9d..93d6757e170272cda8c346bf51578d2b void InspectorTargetAgent::sendMessageFromTargetToFrontend(const String& targetId, const String& message) { ASSERT_WITH_MESSAGE(m_targets.get(targetId), "Sending a message from an untracked target to the frontend."); -@@ -144,7 +172,17 @@ void InspectorTargetAgent::targetDestroyed(InspectorTarget& target) +@@ -147,7 +175,17 @@ void InspectorTargetAgent::targetDestroyed(InspectorTarget& target) if (!m_isConnected) return; @@ -367,10 +367,10 @@ index 508eb02ec95c52408384a1e2b77648afd426dd9d..93d6757e170272cda8c346bf51578d2b void InspectorTargetAgent::didCommitProvisionalTarget(const String& oldTargetID, const String& committedTargetID) diff --git a/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.h -index e81573fd0fffaaf6fd2af36635c78fcdf8608c69..c8cde6cfcde9612624f12e21bd9fa56b426bec7f 100644 +index 4edcbf5f4aee2eb8e5675a23b9db67e9d640ef7f..a32b0f3a5de49e58b8a35cec9202b7880e91a2f0 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.h +++ b/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.h -@@ -50,15 +50,20 @@ public: +@@ -51,15 +51,20 @@ public: Protocol::ErrorStringOr setPauseOnStart(bool) final; Protocol::ErrorStringOr resume(const String& targetId) final; Protocol::ErrorStringOr sendMessageToTarget(const String& targetId, const String& message) final; @@ -1659,10 +1659,18 @@ index 72c81757450ad5ebacd5fd20d2a16095514802ec..b7d8ab1e04d3850180079870468b28ef private: enum ArgumentRequirement { ArgumentRequired, ArgumentNotRequired }; diff --git a/Source/ThirdParty/libwebrtc/CMakeLists.txt b/Source/ThirdParty/libwebrtc/CMakeLists.txt -index 0c300bedc697024ca511e43d480f3b7205df3ed6..e54875b46c558903a6b6157833b82ec8ce0ac1f1 100644 +index c205e2646ba6504bf2d865efe27f37ce4299c3cc..57ef4fa64b8b118fa4a3f7b77eb7d3df98d7ab83 100644 --- a/Source/ThirdParty/libwebrtc/CMakeLists.txt +++ b/Source/ThirdParty/libwebrtc/CMakeLists.txt -@@ -529,6 +529,11 @@ set(webrtc_SOURCES +@@ -452,6 +452,7 @@ set(webrtc_SOURCES + Source/third_party/boringssl/src/crypto/x509/x_val.c + Source/third_party/boringssl/src/crypto/x509/x_x509a.c + Source/third_party/boringssl/src/crypto/x509/x_x509.c ++ + Source/third_party/boringssl/src/decrepit/bio/base64_bio.c + Source/third_party/boringssl/src/decrepit/blowfish/blowfish.c + Source/third_party/boringssl/src/decrepit/cast/cast.c +@@ -565,6 +566,11 @@ set(webrtc_SOURCES Source/third_party/boringssl/src/tool/transport_common.cc Source/third_party/boringssl/src/util/fipstools/acvp/modulewrapper/main.cc Source/third_party/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc @@ -1674,7 +1682,7 @@ index 0c300bedc697024ca511e43d480f3b7205df3ed6..e54875b46c558903a6b6157833b82ec8 Source/third_party/libyuv/source/compare.cc Source/third_party/libyuv/source/compare_common.cc Source/third_party/libyuv/source/compare_gcc.cc -@@ -2126,6 +2131,10 @@ set(webrtc_INCLUDE_DIRECTORIES PRIVATE +@@ -2198,6 +2204,10 @@ set(webrtc_INCLUDE_DIRECTORIES PRIVATE Source/third_party/libsrtp/config Source/third_party/libsrtp/crypto/include Source/third_party/libsrtp/include @@ -1699,13 +1707,13 @@ index 8f79bffda497f6144e9e82c23397a182a6745cf3..d22f4fd1b54d535288994c44a5bd6140 WARNING_CFLAGS = -Wno-deprecated-declarations $(inherited); diff --git a/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp b/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp -index eed4780db5dd82c4946ef9662f91ec259150b598..1cdfac1540b8dc06a5ad0dbc40b63087520c6f70 100644 +index bad4a4b2202135e367949695c5222e42739e003f..8f0ba977deb361225062445e6a950ee8bdf2345d 100644 --- a/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp +++ b/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp -@@ -386,3 +386,24 @@ __ZN3rtc7NetworkC1ENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEES5_RKNS_9I - __ZN6webrtc18callback_list_impl21CallbackListReceiversC1Ev - __ZNK6webrtc32webrtc_sequence_checker_internal19SequenceCheckerImpl19ExpectationToStringEv - __ZN3rtc16InterfaceAddressaSERKS0_ +@@ -394,3 +394,24 @@ __ZN6webrtc8RtpCodecD1Ev + __ZN7cricket16CreateVideoCodecERKN6webrtc14SdpVideoFormatE + __ZN7cricket5CodecD1Ev + __ZTVN3rtc17AsyncPacketSocketE +__ZN8mkvmuxer11SegmentInfo15set_writing_appEPKc +__ZN8mkvmuxer11SegmentInfo4InitEv +__ZN8mkvmuxer7Segment10OutputCuesEb @@ -1728,31 +1736,34 @@ index eed4780db5dd82c4946ef9662f91ec259150b598..1cdfac1540b8dc06a5ad0dbc40b63087 +_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 bcd9e02bc019e17799fe812d7d9a4c7c316b3456..909bbac68574129ea60af831f30de59edf3c28b8 100644 +index 625cb7fefc6a699d9e2f28c6acc1bc7681ea3984..cd6677a7ffd3321978427a9fc6fbed5179aebb60 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 -@@ -15,6 +15,7 @@ - +@@ -16,6 +16,7 @@ #include "absl/algorithm/container.h" #include "absl/memory/memory.h" -+#include "absl/types/optional.h" - #include "api/call/transport.h" + #include "absl/types/optional.h" ++#include "api/call/transport.h" #include "api/video/video_bitrate_allocation.h" #include "modules/rtp_rtcp/include/receive_statistics.h" -@@ -358,7 +359,7 @@ void RtcpTransceiverImpl::HandleReportBlocks( - Timestamp::Millis(now_ntp.ToMs() - rtc::kNtpJan1970Millisecs); - - for (const rtcp::ReportBlock& block : rtcp_report_blocks) { -- std::optional rtt; -+ absl::optional rtt; - if (block.last_sr() != 0) { - rtt = CompactNtpRttToTimeDelta( - receive_time_ntp - block.delay_since_last_sr() - block.last_sr()); + #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +diff --git a/Source/ThirdParty/libwebrtc/Source/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h b/Source/ThirdParty/libwebrtc/Source/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h +index 80709f1f43aa74428bb05b2a3ca4bb9d0631cd3d..1dde8c58f853cce5a6a2787c4bbcf381f0674582 100644 +--- a/Source/ThirdParty/libwebrtc/Source/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h ++++ b/Source/ThirdParty/libwebrtc/Source/webrtc/modules/rtp_rtcp/source/rtp_format_h264.h +@@ -16,6 +16,7 @@ + + #include + #include ++#include + #include + + #include "api/array_view.h" diff --git a/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj b/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj -index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b08fafe98 100644 +index c9fa28d80575ccee5c9ffbe7e1c72f438a2031dd..9d1ea54dfb66a92f1b1295e457666b14fe99ee0e 100644 --- a/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj +++ b/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj -@@ -28,6 +28,20 @@ +@@ -32,6 +32,20 @@ }; /* End PBXAggregateTarget section */ @@ -1773,7 +1784,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b /* 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, ); }; }; -@@ -5028,6 +5042,9 @@ +@@ -5104,6 +5118,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 */; }; @@ -1783,7 +1794,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ -@@ -5418,6 +5435,13 @@ +@@ -5550,6 +5567,13 @@ remoteGlobalIDString = DDF30D0527C5C003006A526F; remoteInfo = absl; }; @@ -1797,7 +1808,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ -@@ -10837,6 +10861,9 @@ +@@ -11084,6 +11108,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 = ""; }; @@ -1807,7 +1818,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b FB39D0D11200F0E300088E69 /* libwebrtc.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libwebrtc.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ -@@ -19415,6 +19442,7 @@ +@@ -19876,6 +19903,7 @@ isa = PBXGroup; children = ( CDFD2F9224C4B2F90048DAC3 /* common */, @@ -1815,7 +1826,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b CDEBB19224C0191800ADBD44 /* webm_parser */, ); path = libwebm; -@@ -19849,6 +19877,16 @@ +@@ -20302,6 +20330,16 @@ path = include; sourceTree = ""; }; @@ -1832,7 +1843,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b FB39D06E1200ED9200088E69 = { isa = PBXGroup; children = ( -@@ -22941,6 +22979,7 @@ +@@ -23504,6 +23542,7 @@ ); dependencies = ( 410B3827292B73E90003E515 /* PBXTargetDependency */, @@ -1840,15 +1851,15 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b DD2E76E827C6B69A00F2A74C /* PBXTargetDependency */, CDEBB4CC24C01AB400ADBD44 /* PBXTargetDependency */, 411ED040212E0811004320BA /* PBXTargetDependency */, -@@ -23016,6 +23055,7 @@ - 446359B62AEA108C00551EEE /* vp8_replay_fuzzer */, - 44C20E892AB39FA80046C6A8 /* vp9_dec_fuzzer */, +@@ -23583,6 +23622,7 @@ + 4460B89B2B155B2E00392062 /* vp9_depacketizer_fuzzer */, + 4460B8B92B155B6A00392062 /* vp9_qp_parser_fuzzer */, 444A6EF02AEADFC9005FE121 /* vp9_replay_fuzzer */, + F31720AC27FE215900EEE407 /* Copy libvpx headers */, ); }; /* End PBXProject section */ -@@ -23098,6 +23138,23 @@ +@@ -23666,6 +23706,23 @@ shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Scripts/create-symlink-to-altroot.sh\"\n"; }; @@ -1872,7 +1883,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ -@@ -24956,6 +25013,9 @@ +@@ -25612,6 +25669,9 @@ 5CDD865E1E43B8B500621E92 /* min_max_operations.c in Sources */, 4189395B242A71F5007FDC41 /* min_video_bitrate_experiment.cc in Sources */, 41B8D8FB28CB85CB00E5FA37 /* missing_mandatory_parameter_cause.cc in Sources */, @@ -1882,7 +1893,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b 4131C387234B957D0028A615 /* moving_average.cc in Sources */, 41FCBB1521B1F7AA00A5DF27 /* moving_average.cc in Sources */, 5CD286101E6A64C90094FDC8 /* moving_max.cc in Sources */, -@@ -25755,6 +25815,11 @@ +@@ -26454,6 +26514,11 @@ target = DDF30D0527C5C003006A526F /* absl */; targetProxy = DD2E76E727C6B69A00F2A74C /* PBXContainerItemProxy */; }; @@ -1894,7 +1905,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ -@@ -26204,6 +26269,27 @@ +@@ -26999,6 +27064,27 @@ }; name = Production; }; @@ -1922,7 +1933,7 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b FB39D0711200ED9200088E69 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5D7C59C71208C68B001C873E /* DebugRelease.xcconfig */; -@@ -26436,6 +26522,16 @@ +@@ -27271,6 +27357,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Production; }; @@ -1940,10 +1951,10 @@ index e05e49446506e8e7adff5854798471b6c9a72a76..b61126bd44324aecd9295affe1d02b9b isa = XCConfigurationList; buildConfigurations = ( diff --git a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml -index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c69224c99c 100644 +index e746383fdf0382c1a0527e25a7f9b6714d32fe9e..40811a978dc1a4a788bb957e411ace74bf69c640 100644 --- a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml +++ b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml -@@ -553,6 +553,7 @@ AriaTextRoleEnabled: +@@ -567,6 +567,7 @@ AriaTextRoleEnabled: default: false # FIXME: This is on by default in WebKit2 PLATFORM(COCOA). Perhaps we should consider turning it on for WebKitLegacy as well. @@ -1951,7 +1962,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 AsyncClipboardAPIEnabled: type: bool status: mature -@@ -563,7 +564,7 @@ AsyncClipboardAPIEnabled: +@@ -577,7 +578,7 @@ AsyncClipboardAPIEnabled: default: false WebKit: "PLATFORM(COCOA) || PLATFORM(GTK)" : true @@ -1960,7 +1971,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 WebCore: default: false -@@ -1887,9 +1888,10 @@ CrossOriginEmbedderPolicyEnabled: +@@ -1899,9 +1900,10 @@ CrossOriginEmbedderPolicyEnabled: WebCore: default: false @@ -1972,7 +1983,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 category: security humanReadableName: "Cross-Origin-Opener-Policy (COOP) header" humanReadableDescription: "Support for Cross-Origin-Opener-Policy (COOP) header" -@@ -1897,7 +1899,7 @@ CrossOriginOpenerPolicyEnabled: +@@ -1909,7 +1911,7 @@ CrossOriginOpenerPolicyEnabled: WebKitLegacy: default: false WebKit: @@ -1981,7 +1992,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 WebCore: default: false -@@ -1927,7 +1929,7 @@ CustomPasteboardDataEnabled: +@@ -1939,7 +1941,7 @@ CustomPasteboardDataEnabled: WebKitLegacy: default: false WebKit: @@ -1989,8 +2000,8 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 + "PLATFORM(COCOA) || PLATFORM(GTK) || PLATFORM(WPE) || PLATFORM(WIN)": true default: false - DNSPrefetchingEnabled: -@@ -1972,6 +1974,7 @@ DOMAudioSessionFullEnabled: + CustomStateSetEnabled: +@@ -1998,6 +2000,7 @@ DOMAudioSessionFullEnabled: WebCore: default: false @@ -1998,7 +2009,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 DOMPasteAccessRequestsEnabled: type: bool status: internal -@@ -1983,7 +1986,7 @@ DOMPasteAccessRequestsEnabled: +@@ -2009,7 +2012,7 @@ DOMPasteAccessRequestsEnabled: default: false WebKit: "PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(VISION)": true @@ -2007,7 +2018,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 WebCore: default: false -@@ -3339,6 +3342,7 @@ InspectorAttachmentSide: +@@ -3421,6 +3424,7 @@ InspectorAttachmentSide: WebKit: default: 0 @@ -2015,7 +2026,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 InspectorStartsAttached: type: bool status: embedder -@@ -3346,7 +3350,7 @@ InspectorStartsAttached: +@@ -3428,7 +3432,7 @@ InspectorStartsAttached: exposed: [ WebKit ] defaultValue: WebKit: @@ -2024,7 +2035,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 InspectorWindowFrame: type: String -@@ -3701,9 +3705,10 @@ LayoutViewportHeightExpansionFactor: +@@ -3783,9 +3787,10 @@ LayoutViewportHeightExpansionFactor: WebCore: default: 0 @@ -2036,7 +2047,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 category: html humanReadableName: "Lazy iframe loading" humanReadableDescription: "Enable lazy iframe loading support" -@@ -3711,9 +3716,9 @@ LazyIframeLoadingEnabled: +@@ -3793,9 +3798,9 @@ LazyIframeLoadingEnabled: WebKitLegacy: default: true WebKit: @@ -2048,7 +2059,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 LazyImageLoadingEnabled: type: bool -@@ -5049,6 +5054,19 @@ PluginsEnabled: +@@ -5116,6 +5121,19 @@ PluginsEnabled: WebCore: default: false @@ -2068,7 +2079,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 PopoverAttributeEnabled: type: bool status: stable -@@ -6715,6 +6733,7 @@ UseCGDisplayListsForDOMRendering: +@@ -6784,6 +6802,7 @@ UseCGDisplayListsForDOMRendering: WebKit: default: true @@ -2076,7 +2087,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 UseGPUProcessForCanvasRenderingEnabled: type: bool status: stable -@@ -6727,7 +6746,7 @@ UseGPUProcessForCanvasRenderingEnabled: +@@ -6796,7 +6815,7 @@ UseGPUProcessForCanvasRenderingEnabled: defaultValue: WebKit: "ENABLE(GPU_PROCESS_BY_DEFAULT)": true @@ -2085,7 +2096,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 default: false UseGPUProcessForDOMRenderingEnabled: -@@ -6769,6 +6788,7 @@ UseGPUProcessForMediaEnabled: +@@ -6838,6 +6857,7 @@ UseGPUProcessForMediaEnabled: "ENABLE(GPU_PROCESS_BY_DEFAULT)": true default: false @@ -2093,7 +2104,7 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 UseGPUProcessForWebGLEnabled: type: bool status: internal -@@ -6780,7 +6800,7 @@ UseGPUProcessForWebGLEnabled: +@@ -6849,7 +6869,7 @@ UseGPUProcessForWebGLEnabled: default: false WebKit: "ENABLE(GPU_PROCESS_BY_DEFAULT) && ENABLE(GPU_PROCESS_WEBGL_BY_DEFAULT)": true @@ -2103,10 +2114,10 @@ index bd465484eab340e7b8ef01030fdc245cae2f48b6..950a84ad3a0ad06bb4206f9e9a73f7c6 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 672f34c1d4a9b6af9453a91427c6a5fd311e3c8a..4861aeb7e723dbd2cfcbb2c55625fe61762d8dd9 100644 +index e6256854c1a5ae7d2654ae661f7b19536caad8f1..fecc53b2bae8eaba474700fa07700ffdb79f8e6e 100644 --- a/Source/WTF/wtf/PlatformEnable.h +++ b/Source/WTF/wtf/PlatformEnable.h -@@ -459,7 +459,7 @@ +@@ -421,7 +421,7 @@ // ORIENTATION_EVENTS should never get enabled on Desktop, only Mobile. #if !defined(ENABLE_ORIENTATION_EVENTS) @@ -2115,7 +2126,7 @@ index 672f34c1d4a9b6af9453a91427c6a5fd311e3c8a..4861aeb7e723dbd2cfcbb2c55625fe61 #endif #if !defined(ENABLE_OVERFLOW_SCROLLING_TOUCH) -@@ -576,7 +576,7 @@ +@@ -526,7 +526,7 @@ #endif #if !defined(ENABLE_TOUCH_EVENTS) @@ -2125,7 +2136,7 @@ index 672f34c1d4a9b6af9453a91427c6a5fd311e3c8a..4861aeb7e723dbd2cfcbb2c55625fe61 #if !defined(ENABLE_TOUCH_ACTION_REGIONS) diff --git a/Source/WTF/wtf/PlatformHave.h b/Source/WTF/wtf/PlatformHave.h -index e30d25bd8505fcf6ba86dbb3e87761286a068e51..ccd89e82ccd96f386fd03140e441731a18c8dbc8 100644 +index 0dddcaf546c5f468620a77d8627e8f34195f39f8..871364ba5d0315054b62712614fa6a0a94cc1021 100644 --- a/Source/WTF/wtf/PlatformHave.h +++ b/Source/WTF/wtf/PlatformHave.h @@ -415,7 +415,7 @@ @@ -2137,7 +2148,7 @@ index e30d25bd8505fcf6ba86dbb3e87761286a068e51..ccd89e82ccd96f386fd03140e441731a #define HAVE_OS_DARK_MODE_SUPPORT 1 #endif -@@ -1257,7 +1257,8 @@ +@@ -1262,7 +1262,8 @@ #endif #if PLATFORM(MAC) @@ -2147,11 +2158,26 @@ index e30d25bd8505fcf6ba86dbb3e87761286a068e51..ccd89e82ccd96f386fd03140e441731a #endif #if !defined(HAVE_LOCKDOWN_MODE_PDF_ADDITIONS) && \ +diff --git a/Source/WTF/wtf/unicode/UTF8Conversion.h b/Source/WTF/wtf/unicode/UTF8Conversion.h +index 8c27e4ca50e6208262966834dbd9f08214294c5f..40898c535e48536418eebf1ed151887c8b0348af 100644 +--- a/Source/WTF/wtf/unicode/UTF8Conversion.h ++++ b/Source/WTF/wtf/unicode/UTF8Conversion.h +@@ -28,6 +28,10 @@ + #include + #include + ++#ifdef Success ++#undef Success ++#endif ++ + namespace WTF { + namespace Unicode { + diff --git a/Source/WebCore/DerivedSources.make b/Source/WebCore/DerivedSources.make -index 043742495941e4cc10c811bbaa225357a9aaa9ea..7b30d5e2539c05408873ae700aa2df4fb59c196e 100644 +index a41e9c88262033a0a32fc2a93987edaa6d72ac0f..1ae3176d9f3b77846c8cda20b88fae0f06a4df17 100644 --- a/Source/WebCore/DerivedSources.make +++ b/Source/WebCore/DerivedSources.make -@@ -1130,6 +1130,10 @@ JS_BINDING_IDLS := \ +@@ -1134,6 +1134,10 @@ JS_BINDING_IDLS := \ $(WebCore)/dom/Slotable.idl \ $(WebCore)/dom/StaticRange.idl \ $(WebCore)/dom/StringCallback.idl \ @@ -2162,10 +2188,10 @@ index 043742495941e4cc10c811bbaa225357a9aaa9ea..7b30d5e2539c05408873ae700aa2df4f $(WebCore)/dom/Text.idl \ $(WebCore)/dom/TextDecoder.idl \ $(WebCore)/dom/TextDecoderStream.idl \ -@@ -1710,9 +1714,6 @@ ADDITIONAL_BINDING_IDLS = \ +@@ -1715,9 +1719,6 @@ JS_BINDING_IDLS := \ + ADDITIONAL_BINDING_IDLS = \ + DocumentTouch.idl \ GestureEvent.idl \ - Internals+Additions.idl \ - InternalsAdditions.idl \ - Touch.idl \ - TouchEvent.idl \ - TouchList.idl \ @@ -2224,22 +2250,22 @@ index 506ebb25fa290f27a75674a6fe5506fc311910d6..07d34c567b42aca08b188243c3f036f6 [self sendSpeechEndIfNeeded]; diff --git a/Source/WebCore/PlatformWPE.cmake b/Source/WebCore/PlatformWPE.cmake -index 99db9b2a0693bddab0b783b47746460cd0b7ffd9..74cbf2811a6f8dbcf631c8a218ad4a1330b8e98c 100644 +index 4021e603067e735cea485519838d1d3808ce3da2..b78dd7638f3a7e2b7946d5949f33be5dae2d4a2c 100644 --- a/Source/WebCore/PlatformWPE.cmake +++ b/Source/WebCore/PlatformWPE.cmake -@@ -48,6 +48,7 @@ list(APPEND WebCore_PRIVATE_FRAMEWORK_HEADERS +@@ -51,6 +51,7 @@ list(APPEND WebCore_PRIVATE_FRAMEWORK_HEADERS + platform/graphics/libwpe/PlatformDisplayLibWPE.h platform/graphics/wayland/PlatformDisplayWayland.h - platform/graphics/wayland/WlUniquePtr.h + platform/wpe/SelectionData.h ) set(CSS_VALUE_PLATFORM_DEFINES "HAVE_OS_DARK_MODE_SUPPORT=1") diff --git a/Source/WebCore/SourcesCocoa.txt b/Source/WebCore/SourcesCocoa.txt -index 2b1def2b2a7bc1083fd2611eb6fc180f39dcca0a..1724fc2550d0ce4690ceb7674f92999f1d9ff007 100644 +index c74ee2284ea358191e6236134dfca6da5faa5a8e..b826f423ce53840f1d649736ff1101995162c432 100644 --- a/Source/WebCore/SourcesCocoa.txt +++ b/Source/WebCore/SourcesCocoa.txt -@@ -700,3 +700,9 @@ platform/graphics/angle/GraphicsContextGLANGLE.cpp @no-unify +@@ -703,3 +703,9 @@ platform/graphics/angle/GraphicsContextGLANGLE.cpp @no-unify platform/graphics/cocoa/ANGLEUtilitiesCocoa.cpp @no-unify platform/graphics/cocoa/GraphicsContextGLCocoa.mm @no-unify platform/graphics/cv/GraphicsContextGLCVCocoa.cpp @no-unify @@ -2250,10 +2276,10 @@ index 2b1def2b2a7bc1083fd2611eb6fc180f39dcca0a..1724fc2550d0ce4690ceb7674f92999f +JSTouchList.cpp +// Playwright end diff --git a/Source/WebCore/SourcesGTK.txt b/Source/WebCore/SourcesGTK.txt -index 4f3db3c7900ed6873487be379cb9cde693d536e1..9cb0f67b8bd5b5d98bbb595dcf60b950fe140be9 100644 +index 267ffb27af6aced590e26be86f785021b3a1aca2..9772551c07d84d6bc5c6c1476280ccc5c6a86da5 100644 --- a/Source/WebCore/SourcesGTK.txt +++ b/Source/WebCore/SourcesGTK.txt -@@ -122,3 +122,10 @@ platform/text/hyphen/HyphenationLibHyphen.cpp +@@ -115,3 +115,10 @@ platform/text/hyphen/HyphenationLibHyphen.cpp platform/unix/LoggingUnix.cpp platform/xdg/MIMETypeRegistryXdg.cpp @@ -2265,7 +2291,7 @@ index 4f3db3c7900ed6873487be379cb9cde693d536e1..9cb0f67b8bd5b5d98bbb595dcf60b950 +JSSpeechSynthesisEventInit.cpp +// Playwright: end. diff --git a/Source/WebCore/SourcesWPE.txt b/Source/WebCore/SourcesWPE.txt -index 06c923a3227befac7680faf2cdb44abd657d6e5f..adcf594fc399b93772c1c962b283bef16d253186 100644 +index 827b1cf84cd3b2f27b54042e485ed39e30d54b4a..a06041fdc1899f044caef4a2af6967f62b7e2fb6 100644 --- a/Source/WebCore/SourcesWPE.txt +++ b/Source/WebCore/SourcesWPE.txt @@ -45,6 +45,8 @@ editing/libwpe/EditorLibWPE.cpp @@ -2277,7 +2303,7 @@ index 06c923a3227befac7680faf2cdb44abd657d6e5f..adcf594fc399b93772c1c962b283bef1 page/linux/ResourceUsageOverlayLinux.cpp page/linux/ResourceUsageThreadLinux.cpp -@@ -91,6 +93,17 @@ platform/text/LocaleICU.cpp +@@ -93,6 +95,17 @@ platform/text/LocaleICU.cpp platform/unix/LoggingUnix.cpp @@ -2296,10 +2322,10 @@ index 06c923a3227befac7680faf2cdb44abd657d6e5f..adcf594fc399b93772c1c962b283bef1 +JSSpeechSynthesisEventInit.cpp +// Playwright: end. diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj -index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43c9d03747 100644 +index bf607a957252ff51788fa16f01a207f2192287fe..a696415687156f947de3728f9b232f288e0e8d6f 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj -@@ -6144,6 +6144,13 @@ +@@ -6166,6 +6166,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, ); }; }; @@ -2313,7 +2339,7 @@ index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43 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, ); }; }; -@@ -19848,6 +19855,14 @@ +@@ -19882,6 +19889,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 = ""; }; @@ -2340,7 +2366,7 @@ index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43 F48570A42644C76D00C05F71 /* TranslationContextMenuInfo.h */, F4E1965F21F26E4E00285078 /* UndoItem.cpp */, 2ECDBAD521D8906300F00ECD /* UndoItem.h */, -@@ -33646,6 +33666,8 @@ +@@ -33656,6 +33676,8 @@ 29E4D8DF16B0940F00C84704 /* PlatformSpeechSynthesizer.h */, 1AD8F81A11CAB9E900E93E54 /* PlatformStrategies.cpp */, 1AD8F81911CAB9E900E93E54 /* PlatformStrategies.h */, @@ -2349,7 +2375,7 @@ index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43 0FD7C21D23CE41E30096D102 /* PlatformWheelEvent.cpp */, 935C476A09AC4D4F00A6AAB4 /* PlatformWheelEvent.h */, F491A66A2A9FEFA300F96146 /* PlatformWheelEvent.serialization.in */, -@@ -36243,6 +36265,7 @@ +@@ -36260,6 +36282,7 @@ AD6E71AB1668899D00320C13 /* DocumentSharedObjectPool.h */, 6BDB5DC1227BD3B800919770 /* DocumentStorageAccess.cpp */, 6BDB5DC0227BD3B800919770 /* DocumentStorageAccess.h */, @@ -2357,7 +2383,7 @@ index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43 7CE7FA5B1EF882300060C9D6 /* DocumentTouch.cpp */, 7CE7FA591EF882300060C9D6 /* DocumentTouch.h */, A8185F3209765765005826D9 /* DocumentType.cpp */, -@@ -40909,6 +40932,8 @@ +@@ -40943,6 +40966,8 @@ 1AD8F81B11CAB9E900E93E54 /* PlatformStrategies.h in Headers */, 0F7D07331884C56C00B4AF86 /* PlatformTextTrack.h in Headers */, 074E82BB18A69F0E007EF54C /* PlatformTimeRanges.h in Headers */, @@ -2366,7 +2392,7 @@ index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43 CDD08ABD277E542600EA3755 /* PlatformTrackConfiguration.h in Headers */, CD1F9B022700323D00617EB6 /* PlatformVideoColorPrimaries.h in Headers */, CD1F9B01270020B700617EB6 /* PlatformVideoColorSpace.h in Headers */, -@@ -42160,6 +42185,7 @@ +@@ -42203,6 +42228,7 @@ 0F54DD081881D5F5003EEDBB /* Touch.h in Headers */, 71B7EE0D21B5C6870031C1EF /* TouchAction.h in Headers */, 0F54DD091881D5F5003EEDBB /* TouchEvent.h in Headers */, @@ -2374,7 +2400,7 @@ index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43 0F54DD0A1881D5F5003EEDBB /* TouchList.h in Headers */, 070334D71459FFD5008D8D45 /* TrackBase.h in Headers */, BE88E0C21715CE2600658D98 /* TrackListBase.h in Headers */, -@@ -43275,6 +43301,8 @@ +@@ -43320,6 +43346,8 @@ 2D22830323A8470700364B7E /* CursorMac.mm in Sources */, 5CBD59592280E926002B22AA /* CustomHeaderFields.cpp in Sources */, 07E4BDBF2A3A5FAB000D5509 /* DictationCaretAnimator.cpp in Sources */, @@ -2383,7 +2409,7 @@ index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43 7CE6CBFD187F394900D46BF5 /* FormatConverter.cpp in Sources */, 4667EA3E2968D9DA00BAB1E2 /* GameControllerHapticEffect.mm in Sources */, 46FE73D32968E52000B8064C /* GameControllerHapticEngines.mm in Sources */, -@@ -43358,6 +43386,9 @@ +@@ -43404,6 +43432,9 @@ CE88EE262414467B007F29C2 /* TextAlternativeWithRange.mm in Sources */, BE39137129B267F500FA5D4F /* TextTransformCocoa.cpp in Sources */, 51DF6D800B92A18E00C2DC85 /* ThreadCheck.mm in Sources */, @@ -2394,7 +2420,7 @@ index 4fcc73780444062e38182cbeb43b8d41bb423b94..4034be971d8c0dd868521d38dd33ac43 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 92f1fc9f5f2fb42795b3e07c325711685d9587c8..bf0fb33e0002aae3c86af4228f1d386c75222384 100644 +index 29a931c5aa14d2df37abd6b68d294da493eb4bca..663039e443837032d531a9a2c462e76c31b3e989 100644 --- a/Source/WebCore/accessibility/AccessibilityObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityObject.cpp @@ -65,6 +65,7 @@ @@ -2405,7 +2431,7 @@ index 92f1fc9f5f2fb42795b3e07c325711685d9587c8..bf0fb33e0002aae3c86af4228f1d386c #include "LocalFrame.h" #include "LocalizedStrings.h" #include "MathMLNames.h" -@@ -3989,9 +3990,14 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const +@@ -4002,9 +4003,14 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const if (roleValue() == AccessibilityRole::ApplicationDialog) return AccessibilityObjectInclusion::IncludeObject; @@ -2423,10 +2449,10 @@ index 92f1fc9f5f2fb42795b3e07c325711685d9587c8..bf0fb33e0002aae3c86af4228f1d386c { AXComputedObjectAttributeCache* attributeCache = nullptr; diff --git a/Source/WebCore/bindings/js/WebCoreBuiltinNames.h b/Source/WebCore/bindings/js/WebCoreBuiltinNames.h -index 5c48a13f02c1489923abf2c57e495ff22a0d56fe..4c6fbfc670c65d4aca7f7f3bf688e8dbbb1f6fd5 100644 +index b8699c50be81e0c206fcb2ab38d2d1262fa403eb..93ff11832ba8061d5fbc9b611a2f0ac196da6666 100644 --- a/Source/WebCore/bindings/js/WebCoreBuiltinNames.h +++ b/Source/WebCore/bindings/js/WebCoreBuiltinNames.h -@@ -177,6 +177,8 @@ namespace WebCore { +@@ -178,6 +178,8 @@ namespace WebCore { macro(DecompressionStreamTransform) \ macro(DelayNode) \ macro(DeprecationReportBody) \ @@ -2436,10 +2462,10 @@ index 5c48a13f02c1489923abf2c57e495ff22a0d56fe..4c6fbfc670c65d4aca7f7f3bf688e8db macro(DynamicsCompressorNode) \ macro(ElementInternals) \ diff --git a/Source/WebCore/css/query/MediaQueryFeatures.cpp b/Source/WebCore/css/query/MediaQueryFeatures.cpp -index 98d40553e12efe44330ddc755636e535dc2e2cf7..103d80febe931e688f8c020e8a58d8aec467a66b 100644 +index 235897de42b9c10f1d1c174071297b02d1a23430..1c96136d997763166d2115e7eaddd38a7045d600 100644 --- a/Source/WebCore/css/query/MediaQueryFeatures.cpp +++ b/Source/WebCore/css/query/MediaQueryFeatures.cpp -@@ -370,7 +370,11 @@ const FeatureSchema& forcedColors() +@@ -366,7 +366,11 @@ const FeatureSchema& forcedColors() static MainThreadNeverDestroyed schema { "forced-colors"_s, FixedVector { CSSValueNone, CSSValueActive }, @@ -2452,7 +2478,7 @@ index 98d40553e12efe44330ddc755636e535dc2e2cf7..103d80febe931e688f8c020e8a58d8ae return MatchingIdentifiers { CSSValueNone }; } }; -@@ -549,6 +553,9 @@ const FeatureSchema& prefersReducedMotion() +@@ -545,6 +549,9 @@ const FeatureSchema& prefersReducedMotion() [](auto& context) { bool userPrefersReducedMotion = [&] { auto& frame = *context.document.frame(); @@ -2463,7 +2489,7 @@ index 98d40553e12efe44330ddc755636e535dc2e2cf7..103d80febe931e688f8c020e8a58d8ae case ForcedAccessibilityValue::On: return true; diff --git a/Source/WebCore/dom/DataTransfer.cpp b/Source/WebCore/dom/DataTransfer.cpp -index f002bd12fd70e17ebc9a2136096b274ff0cf893d..11f39bacb91643c489663bf003aff52177f00020 100644 +index 50986ff17761cd7276922cbad63cd75702796117..8ce0b3027264d029e557556ab3b877d0335e1f1f 100644 --- a/Source/WebCore/dom/DataTransfer.cpp +++ b/Source/WebCore/dom/DataTransfer.cpp @@ -510,6 +510,14 @@ Ref DataTransfer::createForDrag(const Document& document) @@ -2482,7 +2508,7 @@ index f002bd12fd70e17ebc9a2136096b274ff0cf893d..11f39bacb91643c489663bf003aff521 { auto dataTransfer = adoptRef(*new DataTransfer(StoreMode::ReadWrite, makeUnique(), Type::DragAndDropData)); diff --git a/Source/WebCore/dom/DataTransfer.h b/Source/WebCore/dom/DataTransfer.h -index a6d2e7f3152e48028fff00c8090527ba68a9802c..2b99ebd3b1d5fd26f72edd8602e29953c2227a8d 100644 +index 6a7e5d6ac767c376f821633ea61f8da1add36610..fdfe082f84f4ec9a74bbe97000cb5e621edbb303 100644 --- a/Source/WebCore/dom/DataTransfer.h +++ b/Source/WebCore/dom/DataTransfer.h @@ -91,6 +91,9 @@ public: @@ -2624,10 +2650,10 @@ index 69b66eae141ec206b8c51382e709230034d3bfb7..4a2ce3dd0b527e391de06635b083b4f6 + } // namespace WebCore diff --git a/Source/WebCore/dom/PointerEvent.h b/Source/WebCore/dom/PointerEvent.h -index e56223a885097ff60f8db1eef5cca4aa4ad0511d..60c5ad6b8795b2985249833a8d1143ebab64b700 100644 +index c67cf54a953a4792395132e15ab978bdc39eef9b..20640dbb12fd4b4118c5313bbd732adcdfe1fb3e 100644 --- a/Source/WebCore/dom/PointerEvent.h +++ b/Source/WebCore/dom/PointerEvent.h -@@ -33,6 +33,8 @@ +@@ -34,6 +34,8 @@ #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY) #include "PlatformTouchEventIOS.h" @@ -2636,7 +2662,7 @@ index e56223a885097ff60f8db1eef5cca4aa4ad0511d..60c5ad6b8795b2985249833a8d1143eb #endif #if ENABLE(TOUCH_EVENTS) && PLATFORM(WPE) -@@ -85,7 +87,7 @@ public: +@@ -86,7 +88,7 @@ public: static Ref create(const AtomString& type, MouseButton, const MouseEvent&, PointerID, const String& pointerType); static Ref create(const AtomString& type, PointerID, const String& pointerType, IsPrimary = IsPrimary::No); @@ -2645,7 +2671,7 @@ index e56223a885097ff60f8db1eef5cca4aa4ad0511d..60c5ad6b8795b2985249833a8d1143eb 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 -@@ -140,7 +142,7 @@ private: +@@ -141,7 +143,7 @@ private: PointerEvent(const AtomString&, Init&&); PointerEvent(const AtomString& type, MouseButton, const MouseEvent&, PointerID, const String& pointerType); PointerEvent(const AtomString& type, PointerID, const String& pointerType, IsPrimary); @@ -2682,7 +2708,7 @@ index 7813532cc52d582c42aebc979a1ecd1137765f08..c01cbd53ad2430a6ffab9a80fc73e74a #endif // USE(LIBWPE) diff --git a/Source/WebCore/html/FileInputType.cpp b/Source/WebCore/html/FileInputType.cpp -index 823c70952937214681611e01421d0f1791aaa79f..84873071ca3226d9372de4e7c5363110fb393ec1 100644 +index e8a73ca778ecf932b85a0431e60a404b6067979d..9c29e0e8c20ddbe9e41c0c4167fddb48a0ead404 100644 --- a/Source/WebCore/html/FileInputType.cpp +++ b/Source/WebCore/html/FileInputType.cpp @@ -37,6 +37,7 @@ @@ -2693,7 +2719,7 @@ index 823c70952937214681611e01421d0f1791aaa79f..84873071ca3226d9372de4e7c5363110 #include "LocalFrame.h" #include "LocalizedStrings.h" #include "MIMETypeRegistry.h" -@@ -209,6 +210,11 @@ void FileInputType::handleDOMActivateEvent(Event& event) +@@ -157,6 +158,11 @@ void FileInputType::handleDOMActivateEvent(Event& event) if (input.isDisabledFormControl()) return; @@ -2705,7 +2731,7 @@ index 823c70952937214681611e01421d0f1791aaa79f..84873071ca3226d9372de4e7c5363110 if (!UserGestureIndicator::processingUserGesture()) return; -@@ -387,7 +393,9 @@ void FileInputType::setFiles(RefPtr&& files, RequestIcon shouldRequest +@@ -344,7 +350,9 @@ void FileInputType::setFiles(RefPtr&& files, RequestIcon shouldRequest pathsChanged = true; else { for (unsigned i = 0; i < length; ++i) { @@ -2798,7 +2824,7 @@ index 3a981b5bf5ca0bbf4d1c9f0b125564742cd8cad9..f8fc2ca6700461627933f149c5837075 } // namespace WebCore diff --git a/Source/WebCore/inspector/InspectorInstrumentation.cpp b/Source/WebCore/inspector/InspectorInstrumentation.cpp -index 51cc7097811a72232a9415e64efda2b103e392cf..c622d7e4baf7c0c30b667fdef6a3c05445e6e8ad 100644 +index f6ce48a7a9bfc05f91c1d2988e40da968028c10f..c7b559a9d42146fa72db983faff8b09ffcccb1b8 100644 --- a/Source/WebCore/inspector/InspectorInstrumentation.cpp +++ b/Source/WebCore/inspector/InspectorInstrumentation.cpp @@ -599,6 +599,12 @@ void InspectorInstrumentation::applyUserAgentOverrideImpl(InstrumentingAgents& i @@ -2955,7 +2981,7 @@ index 51cc7097811a72232a9415e64efda2b103e392cf..c622d7e4baf7c0c30b667fdef6a3c054 { if (is(context)) diff --git a/Source/WebCore/inspector/InspectorInstrumentation.h b/Source/WebCore/inspector/InspectorInstrumentation.h -index f032b90453a44b40c03786f660f105d94ccfab65..51aebd842948754785c25e99980594d45658df48 100644 +index b779f1e8a0c35f5b89307161a4fe44e4ab1ce223..0354d0e64a78121fb2bf080f7b3b1e50c7738aaf 100644 --- a/Source/WebCore/inspector/InspectorInstrumentation.h +++ b/Source/WebCore/inspector/InspectorInstrumentation.h @@ -31,6 +31,7 @@ @@ -3223,7 +3249,7 @@ index f032b90453a44b40c03786f660f105d94ccfab65..51aebd842948754785c25e99980594d4 { return context ? instrumentingAgents(*context) : nullptr; diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp -index b4056c49e3b3362c5117213fc660e49cb2517431..29eef538962e724361bd1467f5ff8ad3fc3113b3 100644 +index c492e24ae4443e4f97ba18ca5cc57dcccff07752..75101ecd551022d6bb3111eec1f7c576962c84ce 100644 --- a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp +++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp @@ -61,10 +61,14 @@ @@ -3317,7 +3343,7 @@ index b4056c49e3b3362c5117213fc660e49cb2517431..29eef538962e724361bd1467f5ff8ad3 Document* InspectorDOMAgent::assertDocument(Protocol::ErrorString& errorString, Protocol::DOM::NodeId nodeId) { Node* node = assertNode(errorString, nodeId); -@@ -1532,16 +1567,7 @@ Protocol::ErrorStringOr InspectorDOMAgent::highlightNode(std::optional InspectorDOMAgent::highlightNode(std::optional InspectorDOMAgent::highlightNode(std::optional&& nodeId, const Protocol::Runtime::RemoteObjectId& objectId, Ref&& highlightInspectorObject, RefPtr&& gridOverlayInspectorObject, RefPtr&& flexOverlayInspectorObject, std::optional&& showRulers) { Protocol::ErrorString errorString; @@ -3335,7 +3361,7 @@ index b4056c49e3b3362c5117213fc660e49cb2517431..29eef538962e724361bd1467f5ff8ad3 if (!node) return makeUnexpected(errorString); -@@ -1796,15 +1822,155 @@ Protocol::ErrorStringOr InspectorDOMAgent::setInspectedNode(Protocol::DOM: +@@ -1795,15 +1821,155 @@ Protocol::ErrorStringOr InspectorDOMAgent::setInspectedNode(Protocol::DOM: return { }; } @@ -3494,7 +3520,7 @@ index b4056c49e3b3362c5117213fc660e49cb2517431..29eef538962e724361bd1467f5ff8ad3 if (!object) return makeUnexpected("Missing injected script for given nodeId"_s); -@@ -3058,7 +3224,7 @@ Protocol::ErrorStringOr InspectorDOMAgent::pushNodeByPath +@@ -3057,7 +3223,7 @@ Protocol::ErrorStringOr InspectorDOMAgent::pushNodeByPath return makeUnexpected("Missing node for given path"_s); } @@ -3503,7 +3529,7 @@ index b4056c49e3b3362c5117213fc660e49cb2517431..29eef538962e724361bd1467f5ff8ad3 { Document* document = &node->document(); if (auto* templateHost = document->templateDocumentHost()) -@@ -3067,12 +3233,18 @@ RefPtr InspectorDOMAgent::resolveNode(Node* nod +@@ -3066,12 +3232,18 @@ RefPtr InspectorDOMAgent::resolveNode(Node* nod if (!frame) return nullptr; @@ -3525,7 +3551,7 @@ index b4056c49e3b3362c5117213fc660e49cb2517431..29eef538962e724361bd1467f5ff8ad3 } Node* InspectorDOMAgent::scriptValueAsNode(JSC::JSValue value) -@@ -3095,4 +3267,57 @@ Protocol::ErrorStringOr InspectorDOMAgent::setAllowEditingUserAgentShadowT +@@ -3094,4 +3266,57 @@ Protocol::ErrorStringOr InspectorDOMAgent::setAllowEditingUserAgentShadowT return { }; } @@ -3584,7 +3610,7 @@ index b4056c49e3b3362c5117213fc660e49cb2517431..29eef538962e724361bd1467f5ff8ad3 + } // namespace WebCore diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.h b/Source/WebCore/inspector/agents/InspectorDOMAgent.h -index 1a37f64d732d700f38a5d5b51c2ca2645ad30eeb..ab544156dc0d711074b86f51e3c7e63abc5ee4a1 100644 +index 71c164d90be9ba0b0bfb390c05f5494a5a6e358c..f35b2714ed059f09ec696323dd75a587ccef350a 100644 --- a/Source/WebCore/inspector/agents/InspectorDOMAgent.h +++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.h @@ -57,6 +57,7 @@ namespace WebCore { @@ -3657,7 +3683,7 @@ index 1a37f64d732d700f38a5d5b51c2ca2645ad30eeb..ab544156dc0d711074b86f51e3c7e63a void discardBindings(); diff --git a/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp b/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp -index 30e72aa5670d45e97332f647ebddc1677ffc6b9e..18986563d46d1261c2684ffac320f70276cc9d6b 100644 +index 3415bf8f049b110cafe3817ea430d4e3ae0fed05..40ec78ae53136ca06b6f2f014d3596be3b8a8e8f 100644 --- a/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp +++ b/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp @@ -59,6 +59,7 @@ @@ -3696,7 +3722,7 @@ index 30e72aa5670d45e97332f647ebddc1677ffc6b9e..18986563d46d1261c2684ffac320f702 for (auto& entry : headers.get()) { auto stringValue = entry.value->asString(); if (!!stringValue) -@@ -1238,6 +1242,9 @@ Protocol::ErrorStringOr InspectorNetworkAgent::interceptWithRequest(const +@@ -1236,6 +1240,9 @@ Protocol::ErrorStringOr InspectorNetworkAgent::interceptWithRequest(const return makeUnexpected("Missing pending intercept request for given requestId"_s); auto& loader = *pendingRequest->m_loader; @@ -3706,7 +3732,7 @@ index 30e72aa5670d45e97332f647ebddc1677ffc6b9e..18986563d46d1261c2684ffac320f702 ResourceRequest request = loader.request(); if (!!url) request.setURL(URL({ }, url)); -@@ -1337,14 +1344,23 @@ Protocol::ErrorStringOr InspectorNetworkAgent::interceptRequestWithRespons +@@ -1335,14 +1342,23 @@ Protocol::ErrorStringOr InspectorNetworkAgent::interceptRequestWithRespons response.setHTTPStatusCode(status); response.setHTTPStatusText(AtomString { statusText }); HTTPHeaderMap explicitHeaders; @@ -3732,7 +3758,7 @@ index 30e72aa5670d45e97332f647ebddc1677ffc6b9e..18986563d46d1261c2684ffac320f702 if (loader->reachedTerminalState()) return; -@@ -1407,6 +1423,12 @@ Protocol::ErrorStringOr InspectorNetworkAgent::setEmulatedConditions(std:: +@@ -1405,6 +1421,12 @@ Protocol::ErrorStringOr InspectorNetworkAgent::setEmulatedConditions(std:: #endif // ENABLE(INSPECTOR_NETWORK_THROTTLING) @@ -3767,7 +3793,7 @@ index dc7e574ee6e9256a1f75ea838d20ca7f5e9190de..5dd4464256e0f5d652fa51fd611286dd // InspectorInstrumentation void willRecalculateStyle(); diff --git a/Source/WebCore/inspector/agents/InspectorPageAgent.cpp b/Source/WebCore/inspector/agents/InspectorPageAgent.cpp -index 1cb48a48ae6ab976e8938d7e03ddc7e3cf58be4c..dbf78f734691588907f047011c164993cda588d5 100644 +index 8a5e8a91b57984dd8e10bf06c960be0cfba313fe..95cf7ec1a11c0c14fbe3baa4f3f4a387938bae58 100644 --- a/Source/WebCore/inspector/agents/InspectorPageAgent.cpp +++ b/Source/WebCore/inspector/agents/InspectorPageAgent.cpp @@ -32,19 +32,26 @@ @@ -4103,7 +4129,7 @@ index 1cb48a48ae6ab976e8938d7e03ddc7e3cf58be4c..dbf78f734691588907f047011c164993 return; frame.script().evaluateIgnoringException(ScriptSourceCode(m_bootstrapScript, JSC::SourceTaintedOrigin::Untainted, URL { "web-inspector://bootstrap.js"_str })); -@@ -1078,6 +1189,51 @@ void InspectorPageAgent::didRecalculateStyle() +@@ -1075,6 +1186,51 @@ void InspectorPageAgent::didRecalculateStyle() m_overlay->update(); } @@ -4155,7 +4181,7 @@ index 1cb48a48ae6ab976e8938d7e03ddc7e3cf58be4c..dbf78f734691588907f047011c164993 Ref InspectorPageAgent::buildObjectForFrame(LocalFrame* frame) { ASSERT_ARG(frame, frame); -@@ -1175,6 +1331,12 @@ void InspectorPageAgent::applyUserAgentOverride(String& userAgent) +@@ -1172,6 +1328,12 @@ void InspectorPageAgent::applyUserAgentOverride(String& userAgent) userAgent = m_userAgentOverride; } @@ -4168,7 +4194,7 @@ index 1cb48a48ae6ab976e8938d7e03ddc7e3cf58be4c..dbf78f734691588907f047011c164993 void InspectorPageAgent::applyEmulatedMedia(AtomString& media) { if (!m_emulatedMedia.isEmpty()) -@@ -1202,11 +1364,13 @@ Protocol::ErrorStringOr InspectorPageAgent::snapshotNode(Protocol::DOM:: +@@ -1199,11 +1361,13 @@ Protocol::ErrorStringOr InspectorPageAgent::snapshotNode(Protocol::DOM:: return snapshot->toDataURL("image/png"_s, std::nullopt, PreserveResolution::Yes); } @@ -4183,7 +4209,7 @@ index 1cb48a48ae6ab976e8938d7e03ddc7e3cf58be4c..dbf78f734691588907f047011c164993 IntRect rectangle(x, y, width, height); auto* localMainFrame = dynamicDowncast(m_inspectedPage.mainFrame()); -@@ -1220,6 +1384,43 @@ Protocol::ErrorStringOr InspectorPageAgent::snapshotRect(int x, int y, i +@@ -1217,6 +1381,43 @@ Protocol::ErrorStringOr InspectorPageAgent::snapshotRect(int x, int y, i return snapshot->toDataURL("image/png"_s, std::nullopt, PreserveResolution::Yes); } @@ -4227,7 +4253,7 @@ index 1cb48a48ae6ab976e8938d7e03ddc7e3cf58be4c..dbf78f734691588907f047011c164993 #if ENABLE(WEB_ARCHIVE) && USE(CF) Protocol::ErrorStringOr InspectorPageAgent::archive() { -@@ -1236,7 +1437,6 @@ Protocol::ErrorStringOr InspectorPageAgent::archive() +@@ -1233,7 +1434,6 @@ Protocol::ErrorStringOr InspectorPageAgent::archive() } #endif @@ -4235,7 +4261,7 @@ index 1cb48a48ae6ab976e8938d7e03ddc7e3cf58be4c..dbf78f734691588907f047011c164993 Protocol::ErrorStringOr InspectorPageAgent::setScreenSizeOverride(std::optional&& width, std::optional&& height) { if (width.has_value() != height.has_value()) -@@ -1254,6 +1454,511 @@ Protocol::ErrorStringOr InspectorPageAgent::setScreenSizeOverride(std::opt +@@ -1251,6 +1451,513 @@ Protocol::ErrorStringOr InspectorPageAgent::setScreenSizeOverride(std::opt localMainFrame->setOverrideScreenSize(FloatSize(width.value_or(0), height.value_or(0))); return { }; } @@ -4389,6 +4415,8 @@ index 1cb48a48ae6ab976e8938d7e03ddc7e3cf58be4c..dbf78f734691588907f047011c164993 + return "LandmarkSearch"_s; + case AccessibilityRole::Legend: + return "Legend"_s; ++ case AccessibilityRole::LineBreak: ++ return "LineBreak"_s; + case AccessibilityRole::Link: + return "Link"_s; + case AccessibilityRole::List: @@ -5085,10 +5113,10 @@ index 7efc7c39d4ea689063c3371c9d9f5d25e433b3ae..c18f0b38ef9a22b594b60287d6c205b1 protected: static SameSiteInfo sameSiteInfo(const Document&, IsForDOMCookieAccess = IsForDOMCookieAccess::No); diff --git a/Source/WebCore/loader/DocumentLoader.cpp b/Source/WebCore/loader/DocumentLoader.cpp -index 4e0baece02ae7ec1c6860d94678b840824d8409f..e6cfe18c89c3efbcec174e64a594d9cc38534cef 100644 +index d58fd2b665ba04bed39c5c8e81f2c9bd5482ca32..811b4d7f4a6154c9f4a012a99eb167dab01b7239 100644 --- a/Source/WebCore/loader/DocumentLoader.cpp +++ b/Source/WebCore/loader/DocumentLoader.cpp -@@ -764,8 +764,10 @@ void DocumentLoader::willSendRequest(ResourceRequest&& newRequest, const Resourc +@@ -756,8 +756,10 @@ void DocumentLoader::willSendRequest(ResourceRequest&& newRequest, const Resourc if (!didReceiveRedirectResponse) return completionHandler(WTFMove(newRequest)); @@ -5099,7 +5127,7 @@ index 4e0baece02ae7ec1c6860d94678b840824d8409f..e6cfe18c89c3efbcec174e64a594d9cc switch (navigationPolicyDecision) { case NavigationPolicyDecision::IgnoreLoad: case NavigationPolicyDecision::LoadWillContinueInAnotherProcess: -@@ -1546,8 +1548,6 @@ void DocumentLoader::detachFromFrame(LoadWillContinueInAnotherProcess) +@@ -1530,8 +1532,6 @@ void DocumentLoader::detachFromFrame(LoadWillContinueInAnotherProcess) if (!m_frame) return; @@ -5109,10 +5137,10 @@ index 4e0baece02ae7ec1c6860d94678b840824d8409f..e6cfe18c89c3efbcec174e64a594d9cc } diff --git a/Source/WebCore/loader/DocumentLoader.h b/Source/WebCore/loader/DocumentLoader.h -index 68970ca45127aa96a96a1bb224a6a8b2f34e5724..6c3187aab3ec6cf6cdf21d8f3562783f2ae7ffdd 100644 +index 9675aea9a362d3d73056fec92de666355ed25095..25f53db7df6b2ea11ffa4e414f6b9b37102900cf 100644 --- a/Source/WebCore/loader/DocumentLoader.h +++ b/Source/WebCore/loader/DocumentLoader.h -@@ -188,9 +188,13 @@ public: +@@ -191,9 +191,13 @@ public: WEBCORE_EXPORT virtual void detachFromFrame(LoadWillContinueInAnotherProcess); @@ -5127,10 +5155,10 @@ index 68970ca45127aa96a96a1bb224a6a8b2f34e5724..6c3187aab3ec6cf6cdf21d8f3562783f DocumentWriter& writer() const { return m_writer; } diff --git a/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp -index 7bfcb7b610c7b61250bb342ad654e634976c0cda..cc2d93e2e3cea8964eb4e4572b926d58f9f7a10c 100644 +index 22a871bbfe4c86208273e97d9c6d323af10ec7b3..dba8f95a8369a23ab373c2e05b1e06360b34c203 100644 --- a/Source/WebCore/loader/FrameLoader.cpp +++ b/Source/WebCore/loader/FrameLoader.cpp -@@ -1238,6 +1238,7 @@ void FrameLoader::loadInSameDocument(URL url, RefPtr stat +@@ -1272,6 +1272,7 @@ void FrameLoader::loadInSameDocument(URL url, RefPtr stat } m_client->dispatchDidNavigateWithinPage(); @@ -5138,16 +5166,16 @@ index 7bfcb7b610c7b61250bb342ad654e634976c0cda..cc2d93e2e3cea8964eb4e4572b926d58 document->statePopped(stateObject ? stateObject.releaseNonNull() : SerializedScriptValue::nullValue()); m_client->dispatchDidPopStateWithinPage(); -@@ -1689,6 +1690,8 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t +@@ -1733,6 +1734,8 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t const String& httpMethod = loader->request().httpMethod(); if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, policyChecker().loadType(), newURL)) { + loader->replacedByFragmentNavigation(m_frame); + RefPtr oldDocumentLoader = m_documentLoader; - NavigationAction action { frame->protectedDocument().releaseNonNull(), loader->request(), InitiatedByMainFrame::Unknown, policyChecker().loadType(), isFormSubmission }; - action.setIsRequestFromClientOrUserInput(loader->isRequestFromClientOrUserInput()); -@@ -1721,7 +1724,9 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t + NavigationAction action { frame->protectedDocument().releaseNonNull(), loader->request(), InitiatedByMainFrame::Unknown, loader->isRequestFromClientOrUserInput(), policyChecker().loadType(), isFormSubmission }; + oldDocumentLoader->setTriggeringAction(WTFMove(action)); +@@ -1766,7 +1769,9 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t } RELEASE_ASSERT(!isBackForwardLoadType(policyChecker().loadType()) || history().provisionalItem()); @@ -5157,7 +5185,7 @@ index 7bfcb7b610c7b61250bb342ad654e634976c0cda..cc2d93e2e3cea8964eb4e4572b926d58 continueLoadAfterNavigationPolicy(request, formState.get(), navigationPolicyDecision, allowNavigationToInvalidURL); completionHandler(); }, PolicyDecisionMode::Asynchronous); -@@ -2960,14 +2965,19 @@ String FrameLoader::userAgent(const URL& url) const +@@ -3015,14 +3020,19 @@ String FrameLoader::userAgent(const URL& url) const String FrameLoader::navigatorPlatform() const { @@ -5179,7 +5207,7 @@ index 7bfcb7b610c7b61250bb342ad654e634976c0cda..cc2d93e2e3cea8964eb4e4572b926d58 } void FrameLoader::dispatchOnloadEvents() -@@ -3397,6 +3407,8 @@ void FrameLoader::receivedMainResourceError(const ResourceError& error) +@@ -3456,6 +3466,8 @@ void FrameLoader::receivedMainResourceError(const ResourceError& error) checkCompleted(); if (frame->page()) checkLoadComplete(); @@ -5188,7 +5216,7 @@ index 7bfcb7b610c7b61250bb342ad654e634976c0cda..cc2d93e2e3cea8964eb4e4572b926d58 } void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, const SecurityOrigin* requesterOrigin, bool shouldContinue) -@@ -4233,9 +4245,6 @@ String FrameLoader::referrer() const +@@ -4303,9 +4315,6 @@ String FrameLoader::referrer() const void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() { @@ -5198,7 +5226,7 @@ index 7bfcb7b610c7b61250bb342ad654e634976c0cda..cc2d93e2e3cea8964eb4e4572b926d58 Vector> worlds; ScriptController::getAllWorlds(worlds); for (auto& world : worlds) -@@ -4245,13 +4254,12 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() +@@ -4315,13 +4324,12 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world) { Ref frame = m_frame.get(); @@ -5207,12 +5235,12 @@ index 7bfcb7b610c7b61250bb342ad654e634976c0cda..cc2d93e2e3cea8964eb4e4572b926d58 - - m_client->dispatchDidClearWindowObjectInWorld(world); - -- if (CheckedPtr page = frame->page()) +- if (RefPtr page = frame->page()) - page->inspectorController().didClearWindowObjectInWorld(frame, world); + if (frame->windowProxy().existingJSWindowProxy(world)) { -+ if (frame->script().canExecuteScripts(ReasonForCallingCanExecuteScripts::NotAboutToExecuteScript)) ++ if (frame->checkedScript()->canExecuteScripts(ReasonForCallingCanExecuteScripts::NotAboutToExecuteScript)) + m_client->dispatchDidClearWindowObjectInWorld(world); -+ if (Page* page = frame->page()) ++ if (RefPtr page = frame->page()) + page->inspectorController().didClearWindowObjectInWorld(m_frame, world); + } @@ -5244,7 +5272,7 @@ index f356dd377950801b95ec0b9a6b4c93624fc0dcda..95e86435868b4e2937aa0dd799f9c234 } diff --git a/Source/WebCore/loader/PolicyChecker.cpp b/Source/WebCore/loader/PolicyChecker.cpp -index a0898687ef06dc03fd085e1913c0eeb6f9825a99..9c8d10a43a87b5b8d1d18ca8c0a01d1fd883a719 100644 +index a015590e612a3983c13b8a9426522d3567552d95..1d17db10c7e8d6d9d88f8f782479e557d5fe1059 100644 --- a/Source/WebCore/loader/PolicyChecker.cpp +++ b/Source/WebCore/loader/PolicyChecker.cpp @@ -44,6 +44,7 @@ @@ -5278,10 +5306,10 @@ index 273f7815c6fd1553b78a117c78eaa460085d1272..ccf6e96daebad01d5bfabe5062d29c3a 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 572b79810788c792af10b5e51cce7a5ff88bf401..2fc289c5af45e5d8ec55c93c7f1938a31fecb703 100644 +index 4e33cf542c68e9041869de1f918769b23a838a3c..406390d0b1eb231dcda7b07ba4356d9bee49ea95 100644 --- a/Source/WebCore/loader/cache/CachedResourceLoader.cpp +++ b/Source/WebCore/loader/cache/CachedResourceLoader.cpp -@@ -1021,8 +1021,11 @@ ResourceErrorOr> CachedResourceLoader::requ +@@ -1024,8 +1024,11 @@ ResourceErrorOr> CachedResourceLoader::requ request.updateReferrerPolicy(document() ? document()->referrerPolicy() : ReferrerPolicy::Default); @@ -5295,7 +5323,7 @@ index 572b79810788c792af10b5e51cce7a5ff88bf401..2fc289c5af45e5d8ec55c93c7f1938a3 auto& page = *frame.page(); -@@ -1629,8 +1632,9 @@ Vector> CachedResourceLoader::allCachedSVGImages() const +@@ -1628,8 +1631,9 @@ Vector> CachedResourceLoader::allCachedSVGImages() const ResourceErrorOr> CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest&& request) { @@ -5308,10 +5336,10 @@ index 572b79810788c792af10b5e51cce7a5ff88bf401..2fc289c5af45e5d8ec55c93c7f1938a3 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 4b2ec877f96a384b80709ee24d204cc12f59b178..33b94b530772de73d269a40055744d773a4b2087 100644 +index 62cb148ba012a5b20e819c8cf76728704c8e9ac7..8775e46006476095307cc5c29b9a5606a311a10a 100644 --- a/Source/WebCore/page/ChromeClient.h +++ b/Source/WebCore/page/ChromeClient.h -@@ -328,7 +328,7 @@ public: +@@ -334,7 +334,7 @@ public: #endif #if ENABLE(ORIENTATION_EVENTS) @@ -5321,10 +5349,10 @@ index 4b2ec877f96a384b80709ee24d204cc12f59b178..33b94b530772de73d269a40055744d77 #if ENABLE(INPUT_TYPE_COLOR) diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp -index 9a5b33498e58e12cd2702c6baa44276e5cb2cc52..f60bfa86739a58d87243f882ee7dbba280832542 100644 +index ac83388ad87a0fe6a433275945118dde08860b49..d571ac86e12849476177f3d170cf5ae1dba936b9 100644 --- a/Source/WebCore/page/EventHandler.cpp +++ b/Source/WebCore/page/EventHandler.cpp -@@ -4313,6 +4313,12 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr +@@ -4325,6 +4325,12 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr if (!document) return false; @@ -5337,7 +5365,7 @@ index 9a5b33498e58e12cd2702c6baa44276e5cb2cc52..f60bfa86739a58d87243f882ee7dbba2 dragState().dataTransfer = DataTransfer::createForDrag(*document); auto hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No; -@@ -4914,7 +4920,7 @@ HandleUserInputEventResult EventHandler::handleTouchEvent(const PlatformTouchEve +@@ -4926,7 +4932,7 @@ HandleUserInputEventResult EventHandler::handleTouchEvent(const PlatformTouchEve // Increment the platform touch id by 1 to avoid storing a key of 0 in the hashmap. unsigned touchPointTargetKey = point.id() + 1; @@ -5346,7 +5374,7 @@ index 9a5b33498e58e12cd2702c6baa44276e5cb2cc52..f60bfa86739a58d87243f882ee7dbba2 bool pointerCancelled = false; #endif RefPtr touchTarget; -@@ -4961,7 +4967,7 @@ HandleUserInputEventResult EventHandler::handleTouchEvent(const PlatformTouchEve +@@ -4973,7 +4979,7 @@ HandleUserInputEventResult EventHandler::handleTouchEvent(const PlatformTouchEve // we also remove it from the map. touchTarget = m_originatingTouchPointTargets.take(touchPointTargetKey); @@ -5355,7 +5383,7 @@ index 9a5b33498e58e12cd2702c6baa44276e5cb2cc52..f60bfa86739a58d87243f882ee7dbba2 HitTestResult result = hitTestResultAtPoint(pagePoint, hitType | HitTestRequest::Type::AllowChildFrameContent); pointerTarget = result.targetElement(); pointerCancelled = (pointerTarget != touchTarget); -@@ -4983,7 +4989,7 @@ HandleUserInputEventResult EventHandler::handleTouchEvent(const PlatformTouchEve +@@ -4995,7 +5001,7 @@ HandleUserInputEventResult EventHandler::handleTouchEvent(const PlatformTouchEve if (!targetFrame) continue; @@ -5445,7 +5473,7 @@ index 9be81a19a86aa3ae53aa975cbad81d928f97fa34..56b513e3e0cba167c6e52559f95f7ba4 if (stateObjectType == StateObjectType::Push) { frame->loader().history().pushState(WTFMove(data), title, fullURL.string()); diff --git a/Source/WebCore/page/LocalFrame.cpp b/Source/WebCore/page/LocalFrame.cpp -index d5e55947c84cce0f08b9b2973ad164cd0d04792b..bc4886923eb9a978b7059764647585ef61162aeb 100644 +index d3d8acff2b128e91d26ab0835b58ced9241ed076..50741661f800f60d911114b8742d0e775f4a6781 100644 --- a/Source/WebCore/page/LocalFrame.cpp +++ b/Source/WebCore/page/LocalFrame.cpp @@ -40,6 +40,7 @@ @@ -5464,7 +5492,7 @@ index d5e55947c84cce0f08b9b2973ad164cd0d04792b..bc4886923eb9a978b7059764647585ef #include "HTMLAttachmentElement.h" #include "HTMLFormControlElement.h" #include "HTMLFormElement.h" -@@ -75,6 +77,7 @@ +@@ -76,6 +78,7 @@ #include "Logging.h" #include "Navigator.h" #include "NodeList.h" @@ -5472,7 +5500,7 @@ index d5e55947c84cce0f08b9b2973ad164cd0d04792b..bc4886923eb9a978b7059764647585ef #include "NodeTraversal.h" #include "Page.h" #include "ProcessWarming.h" -@@ -178,6 +181,7 @@ LocalFrame::LocalFrame(Page& page, UniqueRef&& frameLoad +@@ -184,6 +187,7 @@ LocalFrame::LocalFrame(Page& page, UniqueRef&& frameLoad void LocalFrame::init() { @@ -5480,18 +5508,18 @@ index d5e55947c84cce0f08b9b2973ad164cd0d04792b..bc4886923eb9a978b7059764647585ef checkedLoader()->init(); } -@@ -406,7 +410,7 @@ void LocalFrame::orientationChanged() +@@ -418,7 +422,7 @@ void LocalFrame::orientationChanged() IntDegrees LocalFrame::orientation() const { - if (CheckedPtr page = this->page()) + if (RefPtr page = this->page()) - return page->chrome().client().deviceOrientation(); + return page->orientation(); return 0; } #endif // ENABLE(ORIENTATION_EVENTS) -@@ -1281,6 +1285,362 @@ void LocalFrame::frameWasDisconnectedFromOwner() const - protectedDocument()->detachFromFrame(); - } +@@ -1295,6 +1299,362 @@ void LocalFrame::didAccessWindowProxyPropertyViaOpener(WindowProxyProperty prope + + #endif +#if !PLATFORM(IOS_FAMILY) + @@ -5853,7 +5881,7 @@ index d5e55947c84cce0f08b9b2973ad164cd0d04792b..bc4886923eb9a978b7059764647585ef #undef FRAME_RELEASE_LOG_ERROR diff --git a/Source/WebCore/page/LocalFrame.h b/Source/WebCore/page/LocalFrame.h -index f69e87d269b6451a5767d9c8edb09488aec61569..c71dacc6ba37e45dac568d714b4dfc70c713d637 100644 +index 0bceafb01da63cfab2ce258915d4f28b98772495..02bdc83e85bc18e8747fa038263eb1837cdd2648 100644 --- a/Source/WebCore/page/LocalFrame.h +++ b/Source/WebCore/page/LocalFrame.h @@ -28,8 +28,10 @@ @@ -5875,7 +5903,7 @@ index f69e87d269b6451a5767d9c8edb09488aec61569..c71dacc6ba37e45dac568d714b4dfc70 class Editor; class Element; class EventHandler; -@@ -109,8 +110,8 @@ enum { +@@ -111,8 +112,8 @@ enum { }; enum OverflowScrollAction { DoNotPerformOverflowScroll, PerformOverflowScroll }; @@ -5885,7 +5913,7 @@ index f69e87d269b6451a5767d9c8edb09488aec61569..c71dacc6ba37e45dac568d714b4dfc70 class LocalFrame final : public Frame { public: -@@ -217,10 +218,6 @@ public: +@@ -218,10 +219,6 @@ public: WEBCORE_EXPORT DataDetectionResultsStorage& dataDetectionResults(); #endif @@ -5896,7 +5924,7 @@ index f69e87d269b6451a5767d9c8edb09488aec61569..c71dacc6ba37e45dac568d714b4dfc70 WEBCORE_EXPORT Node* deepestNodeAtLocation(const FloatPoint& viewportLocation); WEBCORE_EXPORT Node* nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, SecurityOrigin* = nullptr); WEBCORE_EXPORT Node* nodeRespondingToDoubleClickEvent(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation); -@@ -228,6 +225,10 @@ public: +@@ -229,6 +226,10 @@ public: WEBCORE_EXPORT Node* nodeRespondingToScrollWheelEvents(const FloatPoint& viewportLocation); WEBCORE_EXPORT Node* approximateNodeAtViewportLocationLegacy(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation); @@ -5907,7 +5935,7 @@ index f69e87d269b6451a5767d9c8edb09488aec61569..c71dacc6ba37e45dac568d714b4dfc70 WEBCORE_EXPORT NSArray *wordsInCurrentParagraph() const; WEBCORE_EXPORT CGRect renderRectForPoint(CGPoint, bool* isReplaced, float* fontSize) const; -@@ -295,6 +296,7 @@ public: +@@ -296,6 +297,7 @@ public: WEBCORE_EXPORT FloatSize screenSize() const; void setOverrideScreenSize(FloatSize&&); @@ -5915,7 +5943,7 @@ index f69e87d269b6451a5767d9c8edb09488aec61569..c71dacc6ba37e45dac568d714b4dfc70 void selfOnlyRef(); void selfOnlyDeref(); -@@ -340,7 +342,6 @@ private: +@@ -343,7 +345,6 @@ private: #if ENABLE(DATA_DETECTION) std::unique_ptr m_dataDetectionResults; #endif @@ -5923,7 +5951,7 @@ index f69e87d269b6451a5767d9c8edb09488aec61569..c71dacc6ba37e45dac568d714b4dfc70 void betterApproximateNode(const IntPoint& testPoint, const NodeQualifier&, Node*& best, Node* failedNode, IntPoint& bestPoint, IntRect& bestRect, const IntRect& testRect); bool hitTestResultAtViewportLocation(const FloatPoint& viewportLocation, HitTestResult&, IntPoint& center); -@@ -348,6 +349,7 @@ private: +@@ -351,6 +352,7 @@ private: enum class ShouldFindRootEditableElement : bool { No, Yes }; Node* qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, const NodeQualifier&, ShouldApproximate, ShouldFindRootEditableElement = ShouldFindRootEditableElement::Yes); @@ -5932,10 +5960,10 @@ index f69e87d269b6451a5767d9c8edb09488aec61569..c71dacc6ba37e45dac568d714b4dfc70 ViewportArguments m_viewportArguments; diff --git a/Source/WebCore/page/Page.cpp b/Source/WebCore/page/Page.cpp -index 3dcdd8d49ae63683286b1277775edc8819bcc4ca..3a1315ab496cb6370223ce0cae8dc3e6d063c847 100644 +index 5148f36b0bf6c7d41f58200ec0110716b3f1d72a..5e7c21ce8073d689f058e97d8293e1f28e08dc39 100644 --- a/Source/WebCore/page/Page.cpp +++ b/Source/WebCore/page/Page.cpp -@@ -542,6 +542,45 @@ void Page::setOverrideViewportArguments(const std::optional& +@@ -541,6 +541,45 @@ void Page::setOverrideViewportArguments(const std::optional& document->updateViewportArguments(); } @@ -5981,7 +6009,7 @@ index 3dcdd8d49ae63683286b1277775edc8819bcc4ca..3a1315ab496cb6370223ce0cae8dc3e6 ScrollingCoordinator* Page::scrollingCoordinator() { if (!m_scrollingCoordinator && m_settings->scrollingCoordinatorEnabled()) { -@@ -3795,6 +3834,26 @@ void Page::setUseDarkAppearanceOverride(std::optional valueOverride) +@@ -3747,6 +3786,26 @@ void Page::setUseDarkAppearanceOverride(std::optional valueOverride) #endif } @@ -6009,10 +6037,10 @@ index 3dcdd8d49ae63683286b1277775edc8819bcc4ca..3a1315ab496cb6370223ce0cae8dc3e6 { if (insets == m_fullscreenInsets) diff --git a/Source/WebCore/page/Page.h b/Source/WebCore/page/Page.h -index 4b71175319210a81fefba2112be1c068a923aade..0eb7a3d575c0d77a4e9ef95e9f8b05b10d33fe77 100644 +index 7deb38767a7b4d7aa8f5add9c32fe6e5b72a7f8e..de25ac2c7ff01ab2530e881335e7f5f238708f30 100644 --- a/Source/WebCore/page/Page.h +++ b/Source/WebCore/page/Page.h -@@ -309,6 +309,9 @@ public: +@@ -311,6 +311,9 @@ public: const std::optional& overrideViewportArguments() const { return m_overrideViewportArguments; } WEBCORE_EXPORT void setOverrideViewportArguments(const std::optional&); @@ -6022,7 +6050,7 @@ index 4b71175319210a81fefba2112be1c068a923aade..0eb7a3d575c0d77a4e9ef95e9f8b05b1 static void refreshPlugins(bool reload); WEBCORE_EXPORT PluginData& pluginData(); void clearPluginData(); -@@ -372,6 +375,10 @@ public: +@@ -369,6 +372,10 @@ public: #if ENABLE(DRAG_SUPPORT) DragController& dragController() { return m_dragController.get(); } const DragController& dragController() const { return m_dragController.get(); } @@ -6033,7 +6061,7 @@ index 4b71175319210a81fefba2112be1c068a923aade..0eb7a3d575c0d77a4e9ef95e9f8b05b1 #endif FocusController& focusController() const { return *m_focusController; } #if ENABLE(CONTEXT_MENUS) -@@ -547,6 +554,10 @@ public: +@@ -544,6 +551,10 @@ public: WEBCORE_EXPORT void effectiveAppearanceDidChange(bool useDarkAppearance, bool useElevatedUserInterfaceLevel); bool defaultUseDarkAppearance() const { return m_useDarkAppearance; } void setUseDarkAppearanceOverride(std::optional); @@ -6044,7 +6072,7 @@ index 4b71175319210a81fefba2112be1c068a923aade..0eb7a3d575c0d77a4e9ef95e9f8b05b1 #if ENABLE(TEXT_AUTOSIZING) float textAutosizingWidth() const { return m_textAutosizingWidth; } -@@ -990,6 +1001,11 @@ public: +@@ -985,6 +996,11 @@ public: WEBCORE_EXPORT void setInteractionRegionsEnabled(bool); #endif @@ -6056,7 +6084,7 @@ index 4b71175319210a81fefba2112be1c068a923aade..0eb7a3d575c0d77a4e9ef95e9f8b05b1 #if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS_FAMILY) DeviceOrientationUpdateProvider* deviceOrientationUpdateProvider() const { return m_deviceOrientationUpdateProvider.get(); } #endif -@@ -1140,6 +1156,9 @@ private: +@@ -1137,6 +1153,9 @@ private: #if ENABLE(DRAG_SUPPORT) UniqueRef m_dragController; @@ -6066,7 +6094,7 @@ index 4b71175319210a81fefba2112be1c068a923aade..0eb7a3d575c0d77a4e9ef95e9f8b05b1 #endif std::unique_ptr m_focusController; #if ENABLE(CONTEXT_MENUS) -@@ -1217,6 +1236,8 @@ private: +@@ -1211,6 +1230,8 @@ private: bool m_useElevatedUserInterfaceLevel { false }; bool m_useDarkAppearance { false }; std::optional m_useDarkAppearanceOverride; @@ -6075,7 +6103,7 @@ index 4b71175319210a81fefba2112be1c068a923aade..0eb7a3d575c0d77a4e9ef95e9f8b05b1 #if ENABLE(TEXT_AUTOSIZING) float m_textAutosizingWidth { 0 }; -@@ -1393,6 +1414,11 @@ private: +@@ -1385,6 +1406,11 @@ private: #endif std::optional m_overrideViewportArguments; @@ -6326,10 +6354,10 @@ index 0000000000000000000000000000000000000000..803239911006cfb3b03ea911c003f2d2 + +} diff --git a/Source/WebCore/platform/Cairo.cmake b/Source/WebCore/platform/Cairo.cmake -index e5f739288d77ed77c32fc538371637aea8370b7f..bc4c3cb723f733b8fd9683e15c91e13c89c3c426 100644 +index 29492dd39b08db28aad2bf2439eb3e2bbcf25ad7..2b603cb8440b1b5057c87fcbd6909c61bae4ceb8 100644 --- a/Source/WebCore/platform/Cairo.cmake +++ b/Source/WebCore/platform/Cairo.cmake -@@ -17,6 +17,7 @@ list(APPEND WebCore_PRIVATE_FRAMEWORK_HEADERS +@@ -14,6 +14,7 @@ list(APPEND WebCore_PRIVATE_FRAMEWORK_HEADERS platform/graphics/cairo/ImageBufferCairoBackend.h platform/graphics/cairo/ImageBufferCairoImageSurfaceBackend.h platform/graphics/cairo/ImageBufferCairoSurfaceBackend.h @@ -6384,10 +6412,10 @@ index 9b613ca69af779a1e52b6a66216b0905809262dc..cd48a3551ae8da7702154798fcc6ab4d IntSize dragImageSize(DragImageRef) { diff --git a/Source/WebCore/platform/Pasteboard.h b/Source/WebCore/platform/Pasteboard.h -index ac1b5e50c8f22dfb41862cfc4e94130f5238de39..589bec3a70f3fedd42ffca6428b17e186e0e38a9 100644 +index 2203effb3a0a745170680a23d0ff07f3a1a1cd1b..11c0fe944d603349087c942caff9081d819c40da 100644 --- a/Source/WebCore/platform/Pasteboard.h +++ b/Source/WebCore/platform/Pasteboard.h -@@ -44,7 +44,7 @@ OBJC_CLASS NSString; +@@ -45,7 +45,7 @@ OBJC_CLASS NSString; OBJC_CLASS NSArray; #endif @@ -6430,7 +6458,7 @@ index ac1b5e50c8f22dfb41862cfc4e94130f5238de39..589bec3a70f3fedd42ffca6428b17e18 #if PLATFORM(IOS_FAMILY) explicit Pasteboard(std::unique_ptr&&, int64_t changeCount); explicit Pasteboard(std::unique_ptr&&, const String& pasteboardName); -@@ -300,6 +311,7 @@ public: +@@ -306,6 +317,7 @@ public: COMPtr dataObject() const { return m_dataObject; } WEBCORE_EXPORT void setExternalDataObject(IDataObject*); const DragDataMap& dragDataMap() const { return m_dragDataMap; } @@ -6438,7 +6466,7 @@ index ac1b5e50c8f22dfb41862cfc4e94130f5238de39..589bec3a70f3fedd42ffca6428b17e18 void writeURLToWritableDataObject(const URL&, const String&); COMPtr writableDataObject() const { return m_writableDataObject; } void writeImageToDataObject(Element&, const URL&); // FIXME: Layering violation. -@@ -352,6 +364,10 @@ private: +@@ -358,6 +370,10 @@ private: int64_t m_changeCount { 0 }; #endif @@ -6449,7 +6477,7 @@ index ac1b5e50c8f22dfb41862cfc4e94130f5238de39..589bec3a70f3fedd42ffca6428b17e18 #if PLATFORM(COCOA) String m_pasteboardName; int64_t m_changeCount; -@@ -367,6 +383,7 @@ private: +@@ -373,6 +389,7 @@ private: COMPtr m_dataObject; COMPtr m_writableDataObject; DragDataMap m_dragDataMap; @@ -7238,7 +7266,7 @@ index ae439e30f1fb239d18e1164e8896dfb272c75673..eddcb9bda783fcdcbf9f924d4eaa6cc7 #endif // USE(LIBWPE) diff --git a/Source/WebCore/platform/libwpe/PlatformKeyboardEventLibWPE.cpp b/Source/WebCore/platform/libwpe/PlatformKeyboardEventLibWPE.cpp -index b7ddcc524def62c6be6bdf9cd5c8db3ed7368c99..0206c6adf63952ff843b46cc81203065f5f41e94 100644 +index e187936cbef017c080d1dfa14de439b3f5bc2cf8..270c237c8db2f6809719ecfd54a95306728676bc 100644 --- a/Source/WebCore/platform/libwpe/PlatformKeyboardEventLibWPE.cpp +++ b/Source/WebCore/platform/libwpe/PlatformKeyboardEventLibWPE.cpp @@ -30,8 +30,10 @@ @@ -7252,7 +7280,7 @@ index b7ddcc524def62c6be6bdf9cd5c8db3ed7368c99..0206c6adf63952ff843b46cc81203065 namespace WebCore { -@@ -1305,6 +1307,246 @@ int PlatformKeyboardEvent::windowsKeyCodeForWPEKeyCode(unsigned keycode) +@@ -1302,6 +1304,246 @@ int PlatformKeyboardEvent::windowsKeyCodeForWPEKeyCode(unsigned keycode) return 0; } @@ -7634,10 +7662,10 @@ index 35ade40b37f0c476815535541118f9246ed199cd..2bd1444f9a5e9a14ab3d6acbc020434e m_commonHeaders.append(CommonHeader { name, value }); } diff --git a/Source/WebCore/platform/network/NetworkStorageSession.h b/Source/WebCore/platform/network/NetworkStorageSession.h -index b4d2ca129612ba14c6afc92572fe58c2e0e033b4..a31433c497d14d565f1f4fe92e2c88df3d971347 100644 +index 3e9c9005a9b40eea1e1be5078a1fb11dc77c5dae..867b3403a652f0db75dc94ef34620cc92838da4b 100644 --- a/Source/WebCore/platform/network/NetworkStorageSession.h +++ b/Source/WebCore/platform/network/NetworkStorageSession.h -@@ -159,6 +159,8 @@ public: +@@ -160,6 +160,8 @@ public: NetworkingContext* context() const; #endif @@ -8461,7 +8489,7 @@ index 0000000000000000000000000000000000000000..cf2b51f6f02837a1106f4d999f2f130e + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderTextControl.cpp b/Source/WebCore/rendering/RenderTextControl.cpp -index 06932aaafe53227c12c680f479f18de2ef943dc2..2817b2915640cf0ffc4f79e90729805082caefb2 100644 +index 4d46548757a4537e1cf55dde8c777b5817b706ff..bbc7771d987c74581f73ca47dae617cc28717a78 100644 --- a/Source/WebCore/rendering/RenderTextControl.cpp +++ b/Source/WebCore/rendering/RenderTextControl.cpp @@ -210,13 +210,13 @@ void RenderTextControl::layoutExcludedChildren(bool relayoutChildren) @@ -8518,7 +8546,7 @@ index 1d8488e0d36288e09cd5662bd7f770ade95dfee3..dee07f87b47d62d4ef8ede45824bdb2f WorkerOrWorkletGlobalScope& m_globalScope; }; diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp -index 4b1ad56a8545bf97a0dd4e8d3cdec60525df0fc8..3c50334f1033183de466956d4e6e305f031195fb 100644 +index 48e8fe16cab093a72bf5c9aa966c38072ce9d8af..c0ad132eb60e3c057d83d81b52f0b23ba03f3ce5 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp @@ -97,6 +97,8 @@ @@ -8530,7 +8558,7 @@ index 4b1ad56a8545bf97a0dd4e8d3cdec60525df0fc8..3c50334f1033183de466956d4e6e305f #endif #if ENABLE(APPLE_PAY_REMOTE_UI) -@@ -1067,6 +1069,14 @@ void NetworkConnectionToWebProcess::clearPageSpecificData(PageIdentifier pageID) +@@ -1045,6 +1047,14 @@ void NetworkConnectionToWebProcess::clearPageSpecificData(PageIdentifier pageID) storageSession->clearPageSpecificDataForResourceLoadStatistics(pageID); } @@ -8546,10 +8574,10 @@ index 4b1ad56a8545bf97a0dd4e8d3cdec60525df0fc8..3c50334f1033183de466956d4e6e305f { if (auto* storageSession = networkProcess().storageSession(m_sessionID)) diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h -index b4bc09699705059954d2c6f9e867d10ea390d519..9949d08f3233615641af9b8b8ed0836082d2df35 100644 +index 465fb60c248beae91fb70f9e66826da4a9613136..def77f359298ec7c70426b7f9bdbd5b05661e114 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h -@@ -339,6 +339,8 @@ private: +@@ -332,6 +332,8 @@ private: void clearPageSpecificData(WebCore::PageIdentifier); @@ -8559,7 +8587,7 @@ index b4bc09699705059954d2c6f9e867d10ea390d519..9949d08f3233615641af9b8b8ed08360 void logUserInteraction(RegistrableDomain&&); diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in -index 869d0f0f0bbe73364efe9022508fab74053a5afd..52c963bfc2f810d2cfca1a1a01c466a0cb479fbc 100644 +index ddf34779b28f8b8dfe7e10e2a9acff502b1b5f88..c85d2b4875c7f581648fd610033e6dfc745ecb17 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in @@ -71,6 +71,8 @@ messages -> NetworkConnectionToWebProcess LegacyReceiver { @@ -8572,10 +8600,10 @@ index 869d0f0f0bbe73364efe9022508fab74053a5afd..52c963bfc2f810d2cfca1a1a01c466a0 LogUserInteraction(WebCore::RegistrableDomain domain) ResourceLoadStatisticsUpdated(Vector statistics) -> () diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.cpp b/Source/WebKit/NetworkProcess/NetworkProcess.cpp -index 646d36119c6bf082f2cf7cedba71d2bc8961cf5a..b23b3db6f6b0954e8be1f8ffedcce54a8e69ec08 100644 +index fe61606cc777befb0b0f6ffc83372103643cb661..af37e6fca0f70c2315468696f2281fc39596b751 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.cpp +++ b/Source/WebKit/NetworkProcess/NetworkProcess.cpp -@@ -622,6 +622,12 @@ void NetworkProcess::registrableDomainsExemptFromWebsiteDataDeletion(PAL::Sessio +@@ -629,6 +629,12 @@ void NetworkProcess::registrableDomainsExemptFromWebsiteDataDeletion(PAL::Sessio completionHandler({ }); } @@ -8589,7 +8617,7 @@ index 646d36119c6bf082f2cf7cedba71d2bc8961cf5a..b23b3db6f6b0954e8be1f8ffedcce54a { if (auto* session = networkSession(sessionID)) { diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.h b/Source/WebKit/NetworkProcess/NetworkProcess.h -index 93288ab85429a458b4a7158531265a639f46cdcf..4a8517c9102803d9004b7558146289b8befeba6f 100644 +index ddf94ef00e89b0b0e9895845b336706e88bd7322..5f20b670ba01ce91a3071d5905e385a962bde581 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.h +++ b/Source/WebKit/NetworkProcess/NetworkProcess.h @@ -33,6 +33,7 @@ @@ -8600,7 +8628,7 @@ index 93288ab85429a458b4a7158531265a639f46cdcf..4a8517c9102803d9004b7558146289b8 #include "WebPageProxyIdentifier.h" #include "WebResourceLoadStatisticsStore.h" #include "WebsiteData.h" -@@ -81,6 +82,7 @@ class SessionID; +@@ -85,6 +86,7 @@ class SessionID; namespace WebCore { class CertificateInfo; @@ -8608,7 +8636,7 @@ index 93288ab85429a458b4a7158531265a639f46cdcf..4a8517c9102803d9004b7558146289b8 class CurlProxySettings; class ProtectionSpace; class NetworkStorageSession; -@@ -217,6 +219,9 @@ public: +@@ -212,6 +214,9 @@ public: void registrableDomainsWithLastAccessedTime(PAL::SessionID, CompletionHandler>)>&&); void registrableDomainsExemptFromWebsiteDataDeletion(PAL::SessionID, CompletionHandler)>&&); @@ -8619,12 +8647,12 @@ index 93288ab85429a458b4a7158531265a639f46cdcf..4a8517c9102803d9004b7558146289b8 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 f25091cbaa770577d91587dade186ed48b6cdd85..b041386dd2ad05a1bb015b6ac2c92490aafbcc15 100644 +index 383d6584d89215481fbe62aea5ec91017c82273e..f1021d7d16854e2a05ee7109b1794e9de0172722 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.messages.in +++ b/Source/WebKit/NetworkProcess/NetworkProcess.messages.in -@@ -83,6 +83,8 @@ messages -> NetworkProcess LegacyReceiver { +@@ -79,6 +79,8 @@ messages -> NetworkProcess LegacyReceiver { + SetInspectionForServiceWorkersAllowed(PAL::SessionID sessionID, bool inspectable) - #endif + SetIgnoreCertificateErrors(PAL::SessionID sessionID, bool ignoreTLSErrors) + @@ -8632,20 +8660,20 @@ index f25091cbaa770577d91587dade186ed48b6cdd85..b041386dd2ad05a1bb015b6ac2c92490 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 9d3bea122c7ccd7279efd5a8929105c172728f55..ee2ffae0df4b4f667e67fae42939c265a3425781 100644 +index a0c2cc8fa79a788191899e95a87a81ad1875db44..17b25d9e2bd3befa75f4e7f044cb924d6c2fd552 100644 --- a/Source/WebKit/NetworkProcess/NetworkSession.h +++ b/Source/WebKit/NetworkProcess/NetworkSession.h -@@ -204,6 +204,9 @@ public: +@@ -200,6 +200,9 @@ public: void lowMemoryHandler(WTF::Critical); + void setIgnoreCertificateErrors(bool ignore) { m_ignoreCertificateErrors = ignore; } + bool ignoreCertificateErrors() { return m_ignoreCertificateErrors; } + - #if ENABLE(SERVICE_WORKER) void removeSoftUpdateLoader(ServiceWorkerSoftUpdateLoader* loader) { m_softUpdateLoaders.remove(loader); } void addNavigationPreloaderTask(ServiceWorkerFetchTask&); -@@ -317,6 +320,7 @@ protected: + ServiceWorkerFetchTask* navigationPreloaderTaskFromFetchIdentifier(WebCore::FetchIdentifier); +@@ -307,6 +310,7 @@ protected: bool m_privateClickMeasurementDebugModeEnabled { false }; std::optional m_ephemeralMeasurement; bool m_isRunningEphemeralMeasurementTest { false }; @@ -8654,7 +8682,7 @@ index 9d3bea122c7ccd7279efd5a8929105c172728f55..ee2ffae0df4b4f667e67fae42939c265 HashSet> m_keptAliveLoads; diff --git a/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm b/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm -index b503a62c1763d89def8e3d223ebae8e06d2691c2..222c0efa62a4b5f3fcad8a9915a1d7a6b3ae5c49 100644 +index 22ca12f45d138184823b09da60a22e615a89792f..eb655b51432a3ad71b7e7635beaa7c632627eff0 100644 --- a/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm +++ b/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm @@ -752,6 +752,8 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didRece @@ -8965,10 +8993,10 @@ index 3fa6072886e6d34d53c63fffb131d51a197540fd..72d919c0043fb524109aed043897195a } diff --git a/Source/WebKit/PlatformGTK.cmake b/Source/WebKit/PlatformGTK.cmake -index a66d97107bcd82080d0abdd1388812bc78e92b8a..b95453016bee222330e91c9dc6c35f001b602e00 100644 +index 496706f5c2b7c28903ea00dcd7294c6151b9b02f..f8c5e9b5f3c64af8a4ab9ba21a59e5fe4923f067 100644 --- a/Source/WebKit/PlatformGTK.cmake +++ b/Source/WebKit/PlatformGTK.cmake -@@ -316,6 +316,9 @@ list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES +@@ -317,6 +317,9 @@ list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES ${GSTREAMER_PBUTILS_INCLUDE_DIRS} ${GTK_INCLUDE_DIRS} ${LIBSOUP_INCLUDE_DIRS} @@ -8978,7 +9006,7 @@ index a66d97107bcd82080d0abdd1388812bc78e92b8a..b95453016bee222330e91c9dc6c35f00 ) list(APPEND WebKit_INTERFACE_INCLUDE_DIRECTORIES -@@ -371,6 +374,9 @@ if (USE_LIBWEBRTC) +@@ -356,6 +359,9 @@ if (USE_LIBWEBRTC) list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES "${THIRDPARTY_DIR}/libwebrtc/Source/" "${THIRDPARTY_DIR}/libwebrtc/Source/webrtc" @@ -8988,7 +9016,7 @@ index a66d97107bcd82080d0abdd1388812bc78e92b8a..b95453016bee222330e91c9dc6c35f00 ) endif () -@@ -414,6 +420,12 @@ else () +@@ -399,6 +405,12 @@ else () set(WebKitGTK_ENUM_HEADER_TEMPLATE ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitEnumTypesGtk3.h.in) endif () @@ -9002,10 +9030,10 @@ index a66d97107bcd82080d0abdd1388812bc78e92b8a..b95453016bee222330e91c9dc6c35f00 set(WebKitGTK_ENUM_GENERATION_HEADERS ${WebKitGTK_INSTALLED_HEADERS}) list(REMOVE_ITEM WebKitGTK_ENUM_GENERATION_HEADERS ${WebKitGTK_DERIVED_SOURCES_DIR}/webkit/WebKitEnumTypes.h) diff --git a/Source/WebKit/PlatformWPE.cmake b/Source/WebKit/PlatformWPE.cmake -index 960995e2b58396ad9aab82576a01600453c13241..1376224cadb398b84c305a319b4b71919cc152f5 100644 +index 9e4e02b99e5080712f6fc290d0c70109ec571b58..23eb76a28bd23ebfd5277b1622c5300db66ccaaf 100644 --- a/Source/WebKit/PlatformWPE.cmake +++ b/Source/WebKit/PlatformWPE.cmake -@@ -198,6 +198,7 @@ set(WPE_API_HEADER_TEMPLATES +@@ -210,6 +210,7 @@ set(WPE_API_HEADER_TEMPLATES ${WEBKIT_DIR}/UIProcess/API/glib/WebKitWindowProperties.h.in ${WEBKIT_DIR}/UIProcess/API/glib/WebKitWebsitePolicies.h.in ${WEBKIT_DIR}/UIProcess/API/glib/webkit.h.in @@ -9013,7 +9041,7 @@ index 960995e2b58396ad9aab82576a01600453c13241..1376224cadb398b84c305a319b4b7191 ) if (ENABLE_2022_GLIB_API) -@@ -399,8 +400,17 @@ list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES +@@ -414,8 +415,17 @@ list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES ${GIO_UNIX_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} ${LIBSOUP_INCLUDE_DIRS} @@ -9032,10 +9060,10 @@ index 960995e2b58396ad9aab82576a01600453c13241..1376224cadb398b84c305a319b4b7191 Cairo::Cairo Freetype::Freetype diff --git a/Source/WebKit/PlatformWin.cmake b/Source/WebKit/PlatformWin.cmake -index c530389b879f03806702007d0f142ee94f149e6b..97244d8abda433481f882fc3cd32aa69006466f4 100644 +index 903aca234b3b5b2cc537cb4a4dca0992c9b306f6..851c76d09e5e2e47060721b748c60def118f8167 100644 --- a/Source/WebKit/PlatformWin.cmake +++ b/Source/WebKit/PlatformWin.cmake -@@ -92,8 +92,12 @@ list(APPEND WebKit_SOURCES +@@ -89,8 +89,12 @@ list(APPEND WebKit_SOURCES UIProcess/wc/DrawingAreaProxyWC.cpp @@ -9048,7 +9076,7 @@ index c530389b879f03806702007d0f142ee94f149e6b..97244d8abda433481f882fc3cd32aa69 UIProcess/win/WebPageProxyWin.cpp UIProcess/win/WebPopupMenuProxyWin.cpp UIProcess/win/WebProcessPoolWin.cpp -@@ -114,6 +118,7 @@ list(APPEND WebKit_SOURCES +@@ -111,6 +115,7 @@ list(APPEND WebKit_SOURCES WebProcess/WebCoreSupport/curl/WebFrameNetworkingContext.cpp WebProcess/WebCoreSupport/win/WebPopupMenuWin.cpp @@ -9056,7 +9084,7 @@ index c530389b879f03806702007d0f142ee94f149e6b..97244d8abda433481f882fc3cd32aa69 WebProcess/WebPage/AcceleratedSurface.cpp -@@ -180,8 +185,84 @@ list(APPEND WebKit_SERIALIZATION_IN_FILES +@@ -177,8 +182,81 @@ list(APPEND WebKit_SERIALIZATION_IN_FILES list(APPEND WebKit_PRIVATE_LIBRARIES comctl32 @@ -9080,7 +9108,6 @@ index c530389b879f03806702007d0f142ee94f149e6b..97244d8abda433481f882fc3cd32aa69 + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/compare.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/compare_common.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/compare_gcc.cc" -+ "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/compare_mmi.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/compare_msa.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/compare_neon64.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/compare_neon.cc" @@ -9101,7 +9128,6 @@ index c530389b879f03806702007d0f142ee94f149e6b..97244d8abda433481f882fc3cd32aa69 + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/rotate.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/rotate_common.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/rotate_gcc.cc" -+ "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/rotate_mmi.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/rotate_msa.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/rotate_neon64.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/rotate_neon.cc" @@ -9109,7 +9135,6 @@ index c530389b879f03806702007d0f142ee94f149e6b..97244d8abda433481f882fc3cd32aa69 + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/row_any.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/row_common.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/row_gcc.cc" -+ "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/row_mmi.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/row_msa.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/row_neon64.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/row_neon.cc" @@ -9119,10 +9144,10 @@ index c530389b879f03806702007d0f142ee94f149e6b..97244d8abda433481f882fc3cd32aa69 + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_common.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_gcc.cc" -+ "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_mmi.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_msa.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_neon64.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_neon.cc" ++ "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_rvv.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_uv.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/scale_win.cc" + "${THIRDPARTY_DIR}/libwebrtc/Source/third_party/libyuv/source/video_common.cc" @@ -9154,7 +9179,7 @@ index caf67e1dece5b727e43eba780e70814f8fdb0f63..740150d2589d6e16a516daa3bf6ef899 #include #include diff --git a/Source/WebKit/Shared/NativeWebKeyboardEvent.h b/Source/WebKit/Shared/NativeWebKeyboardEvent.h -index 7164af8d347828ba80bcb52fd6ca814401157f2b..9ba0e32717d9b4cb01f3f43898aa402ad0bd6913 100644 +index 17cb42104f3fe7e78388cdb1acd78efb34022f8d..c824a8c7ab5c4717773bff23c03156e744d192c0 100644 --- a/Source/WebKit/Shared/NativeWebKeyboardEvent.h +++ b/Source/WebKit/Shared/NativeWebKeyboardEvent.h @@ -33,6 +33,7 @@ @@ -9165,7 +9190,7 @@ index 7164af8d347828ba80bcb52fd6ca814401157f2b..9ba0e32717d9b4cb01f3f43898aa402a #endif #if PLATFORM(GTK) -@@ -66,19 +67,35 @@ public: +@@ -70,22 +71,38 @@ public: #if USE(APPKIT) // FIXME: Share iOS's HandledByInputMethod enum here instead of passing a boolean. NativeWebKeyboardEvent(NSEvent *, bool handledByInputMethod, bool replacesSoftSpace, const Vector&); @@ -9192,6 +9217,9 @@ index 7164af8d347828ba80bcb52fd6ca814401157f2b..9ba0e32717d9b4cb01f3f43898aa402a + : WebKeyboardEvent(type, text, unmodifiedText, key, code, keyIdentifier, windowsVirtualKeyCode, nativeVirtualKeyCode, isAutoRepeat, isKeypad, isSystemKey, modifiers, timestamp) + { + } + #if PLATFORM(WPE) && ENABLE(WPE_PLATFORM) + NativeWebKeyboardEvent(WPEEvent*, const String&, bool isAutoRepeat); + #endif #elif PLATFORM(WIN) NativeWebKeyboardEvent(HWND, UINT message, WPARAM, LPARAM, Vector&& pendingCharEvents); + NativeWebKeyboardEvent(WebEventType type, const String& text, const String& unmodifiedText, const String& key, const String& code, const String& keyIdentifier, int windowsVirtualKeyCode, int nativeVirtualKeyCode, bool isAutoRepeat, bool isKeypad, bool isSystemKey, OptionSet modifiers, WallTime timestamp) @@ -9202,7 +9230,7 @@ index 7164af8d347828ba80bcb52fd6ca814401157f2b..9ba0e32717d9b4cb01f3f43898aa402a #if USE(APPKIT) diff --git a/Source/WebKit/Shared/NativeWebMouseEvent.h b/Source/WebKit/Shared/NativeWebMouseEvent.h -index c586d2775021a9e164dc36b1732e1a2dadc986a0..189a9bec7af066fee5d84fa3ce1ed145de01080f 100644 +index d51a12fba25ccd4b8bdd2e4a37bf9f1268034617..e36921913825f18522551c7847efae89d0679cc6 100644 --- a/Source/WebKit/Shared/NativeWebMouseEvent.h +++ b/Source/WebKit/Shared/NativeWebMouseEvent.h @@ -31,6 +31,7 @@ @@ -9213,7 +9241,7 @@ index c586d2775021a9e164dc36b1732e1a2dadc986a0..189a9bec7af066fee5d84fa3ce1ed145 #endif #if PLATFORM(GTK) -@@ -79,6 +80,11 @@ public: +@@ -86,6 +87,11 @@ public: NativeWebMouseEvent(HWND, UINT message, WPARAM, LPARAM, bool); #endif @@ -9226,10 +9254,10 @@ index c586d2775021a9e164dc36b1732e1a2dadc986a0..189a9bec7af066fee5d84fa3ce1ed145 NSEvent* nativeEvent() const { return m_nativeEvent.get(); } #elif PLATFORM(GTK) diff --git a/Source/WebKit/Shared/NativeWebWheelEvent.h b/Source/WebKit/Shared/NativeWebWheelEvent.h -index 072a1f359bd50060da6a347fa06b72d89086825d..ca1b11ccc6241dfc3948910115df966b742d1af8 100644 +index bd941fd1cc5ddbae5d9fbe59976defd8fac3550b..b707e8e1739d95573270848d4e7f7719f4663c41 100644 --- a/Source/WebKit/Shared/NativeWebWheelEvent.h +++ b/Source/WebKit/Shared/NativeWebWheelEvent.h -@@ -65,7 +65,8 @@ public: +@@ -73,7 +73,8 @@ public: #elif PLATFORM(WIN) NativeWebWheelEvent(HWND, UINT message, WPARAM, LPARAM); #endif @@ -9253,7 +9281,7 @@ index 72ad2880160a374e8fa663e561d59becf9d2f36d..372ae6953199245fe4fc55a49813c7ca #endif }; diff --git a/Source/WebKit/Shared/WebCoreArgumentCoders.cpp b/Source/WebKit/Shared/WebCoreArgumentCoders.cpp -index 08ff2201710e5544ebbd02f4d9cb9d25208ebe11..e15d79e4c87f4956f7276d03edfeffbac43da0f8 100644 +index 11b208fe5a0ae0ae83d32bb7b7322517199436e5..745410a5e44445b7ab2362f91556bed8fb470240 100644 --- a/Source/WebKit/Shared/WebCoreArgumentCoders.cpp +++ b/Source/WebKit/Shared/WebCoreArgumentCoders.cpp @@ -192,6 +192,10 @@ @@ -9268,10 +9296,10 @@ index 08ff2201710e5544ebbd02f4d9cb9d25208ebe11..e15d79e4c87f4956f7276d03edfeffba namespace IPC { diff --git a/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in b/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in -index e9e076535ccc3ab4b4c1a6ed491d167fa66b0f87..7beddce3b119cd7c4e4520afd6fc5bf8e4112485 100644 +index 82192403c221da47285a47ef1346581eb24a47d3..b09791570afe961e840bbcac04e9a87e5b113061 100644 --- a/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in +++ b/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in -@@ -2484,6 +2484,9 @@ class WebCore::AuthenticationChallenge { +@@ -2480,6 +2480,9 @@ class WebCore::AuthenticationChallenge { class WebCore::DragData { #if PLATFORM(COCOA) String pasteboardName(); @@ -9281,7 +9309,7 @@ index e9e076535ccc3ab4b4c1a6ed491d167fa66b0f87..7beddce3b119cd7c4e4520afd6fc5bf8 #endif WebCore::IntPoint clientPosition(); WebCore::IntPoint globalPosition(); -@@ -3040,6 +3043,7 @@ header: +@@ -3044,6 +3047,7 @@ header: String httpStatusText; String httpVersion; WebCore::HTTPHeaderMap httpHeaderFields; @@ -9372,7 +9400,7 @@ index b80bcb39473ecec86be5671f38698130bd9acbf3..d886cbac5f4c073e14e12f257fa92041 { } diff --git a/Source/WebKit/Shared/WebKeyboardEvent.h b/Source/WebKit/Shared/WebKeyboardEvent.h -index 976edc95bef9fde10d1e875fce2e00d3732c0456..f2f49d9badd67317c5c1f9aa248e8a1cb6a2f66d 100644 +index 2734461c33f7f9a57933afbc1098029d905522ec..1eb7f2cff4f5fe70aa576be4d4f9b0ea6ea5ddf4 100644 --- a/Source/WebKit/Shared/WebKeyboardEvent.h +++ b/Source/WebKit/Shared/WebKeyboardEvent.h @@ -42,14 +42,18 @@ public: @@ -9407,7 +9435,7 @@ index a38fc7fde1d5f1a1fd04ae1f84eb59c1501deec5..d3669c3d3bad91468fbbeeaa328c3610 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 b71d3c999fb3e7875e5b1e287b5b87f1b97e85f0..aedde190cc609a48a9f1051cd7cf2b570b22a4b3 100644 +index 60a4b766939b2531e0a5188144367cb669385ab2..8fef454523ce464e909b0246c17df55c96d8a428 100644 --- a/Source/WebKit/Shared/WebPageCreationParameters.h +++ b/Source/WebKit/Shared/WebPageCreationParameters.h @@ -280,6 +280,8 @@ struct WebPageCreationParameters { @@ -9420,7 +9448,7 @@ index b71d3c999fb3e7875e5b1e287b5b87f1b97e85f0..aedde190cc609a48a9f1051cd7cf2b57 bool allowsDeprecatedSynchronousXMLHttpRequestDuringUnload { false }; #endif diff --git a/Source/WebKit/Shared/WebPageCreationParameters.serialization.in b/Source/WebKit/Shared/WebPageCreationParameters.serialization.in -index 4e87205e89a1fba4e8b2fefb254c4fc77552644a..de7076e0970f6c24ab388c59635ff70f5b6b2ac8 100644 +index bafcdb2a6592c8796dce837c615d4fe82a22e904..e9c76f7ddf81a5e9367848a141e0463710d12d5e 100644 --- a/Source/WebKit/Shared/WebPageCreationParameters.serialization.in +++ b/Source/WebKit/Shared/WebPageCreationParameters.serialization.in @@ -218,6 +218,8 @@ headers: "ArgumentCoders.h" @@ -9702,10 +9730,10 @@ index 665b9d6a9de903ee9ad6dc53e15ab421b6cb769f..2b129963074d2ceec1c05f3a637c5e1c #endif // ENABLE(TOUCH_EVENTS) diff --git a/Source/WebKit/Sources.txt b/Source/WebKit/Sources.txt -index 1790ef11dd6466bcdae4bf577fcc2403e2d5e8f6..b67201a17f528823345db0d8b0a835218e80daef 100644 +index 25f789a4315b2fc9fbb05f718d5c489643d0047b..29ae191c6887a2be9fdc8b3773908b3f9546935c 100644 --- a/Source/WebKit/Sources.txt +++ b/Source/WebKit/Sources.txt -@@ -376,6 +376,7 @@ Shared/XR/XRDeviceProxy.cpp +@@ -377,6 +377,7 @@ Shared/XR/XRDeviceProxy.cpp UIProcess/AuxiliaryProcessProxy.cpp UIProcess/BackgroundProcessResponsivenessTimer.cpp UIProcess/BrowsingContextGroup.cpp @@ -9713,7 +9741,7 @@ index 1790ef11dd6466bcdae4bf577fcc2403e2d5e8f6..b67201a17f528823345db0d8b0a83521 UIProcess/DeviceIdHashSaltStorage.cpp UIProcess/DisplayLink.cpp UIProcess/DisplayLinkProcessProxyClient.cpp -@@ -383,16 +384,20 @@ UIProcess/DrawingAreaProxy.cpp +@@ -384,16 +385,20 @@ UIProcess/DrawingAreaProxy.cpp UIProcess/FrameLoadState.cpp UIProcess/GeolocationPermissionRequestManagerProxy.cpp UIProcess/GeolocationPermissionRequestProxy.cpp @@ -9734,7 +9762,7 @@ index 1790ef11dd6466bcdae4bf577fcc2403e2d5e8f6..b67201a17f528823345db0d8b0a83521 UIProcess/RemotePageDrawingAreaProxy.cpp UIProcess/RemotePageProxy.cpp UIProcess/ResponsivenessTimer.cpp -@@ -436,6 +441,8 @@ UIProcess/WebOpenPanelResultListenerProxy.cpp +@@ -437,6 +442,8 @@ UIProcess/WebOpenPanelResultListenerProxy.cpp UIProcess/WebPageDiagnosticLoggingClient.cpp UIProcess/WebPageGroup.cpp UIProcess/WebPageInjectedBundleClient.cpp @@ -9743,7 +9771,7 @@ index 1790ef11dd6466bcdae4bf577fcc2403e2d5e8f6..b67201a17f528823345db0d8b0a83521 UIProcess/WebPageProxy.cpp UIProcess/WebPageProxyMessageReceiverRegistration.cpp UIProcess/WebPasteboardProxy.cpp -@@ -567,7 +574,11 @@ UIProcess/Inspector/WebInspectorUtilities.cpp +@@ -568,7 +575,11 @@ UIProcess/Inspector/WebInspectorUtilities.cpp UIProcess/Inspector/WebPageDebuggable.cpp UIProcess/Inspector/WebPageInspectorController.cpp @@ -9756,10 +9784,10 @@ index 1790ef11dd6466bcdae4bf577fcc2403e2d5e8f6..b67201a17f528823345db0d8b0a83521 UIProcess/Media/AudioSessionRoutingArbitratorProxy.cpp UIProcess/Media/MediaUsageManager.cpp diff --git a/Source/WebKit/SourcesCocoa.txt b/Source/WebKit/SourcesCocoa.txt -index 65a44447eae5a192a74ae4a05be7da2d68d99d28..ac0eb6d33feb5e601a4c58cc36243b1b9f8b56ca 100644 +index 0cbd2b853fc4ce6725cc2e8e6044cf669117e82e..cb7356b9fe365699d571b63d110febb479bf4d36 100644 --- a/Source/WebKit/SourcesCocoa.txt +++ b/Source/WebKit/SourcesCocoa.txt -@@ -261,6 +261,7 @@ UIProcess/API/Cocoa/_WKArchiveExclusionRule.mm +@@ -260,6 +260,7 @@ UIProcess/API/Cocoa/_WKArchiveExclusionRule.mm UIProcess/API/Cocoa/_WKAttachment.mm UIProcess/API/Cocoa/_WKAutomationSession.mm UIProcess/API/Cocoa/_WKAutomationSessionConfiguration.mm @@ -9767,7 +9795,7 @@ index 65a44447eae5a192a74ae4a05be7da2d68d99d28..ac0eb6d33feb5e601a4c58cc36243b1b UIProcess/API/Cocoa/_WKContentRuleListAction.mm UIProcess/API/Cocoa/_WKContextMenuElementInfo.mm UIProcess/API/Cocoa/_WKCustomHeaderFields.mm @no-unify -@@ -437,6 +438,7 @@ UIProcess/Inspector/ios/WKInspectorHighlightView.mm +@@ -436,6 +437,7 @@ UIProcess/Inspector/ios/WKInspectorHighlightView.mm UIProcess/Inspector/ios/WKInspectorNodeSearchGestureRecognizer.mm UIProcess/Inspector/mac/RemoteWebInspectorUIProxyMac.mm @@ -9776,10 +9804,10 @@ index 65a44447eae5a192a74ae4a05be7da2d68d99d28..ac0eb6d33feb5e601a4c58cc36243b1b UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm UIProcess/Inspector/mac/WKInspectorViewController.mm diff --git a/Source/WebKit/SourcesGTK.txt b/Source/WebKit/SourcesGTK.txt -index 37f7f79dfbdb9913fe96b0393f6a2547501f5c6e..f34cf6031976d89f73f872ba4029b79349e119de 100644 +index 027c12f9721800dc7b01ec4283e7df5e29071cf5..79330b09ad3a78fa5328e771b0e9d6bbe38da943 100644 --- a/Source/WebKit/SourcesGTK.txt +++ b/Source/WebKit/SourcesGTK.txt -@@ -136,6 +136,7 @@ UIProcess/API/glib/WebKitAutomationSession.cpp @no-unify +@@ -135,6 +135,7 @@ UIProcess/API/glib/WebKitAutomationSession.cpp @no-unify UIProcess/API/glib/WebKitBackForwardList.cpp @no-unify UIProcess/API/glib/WebKitBackForwardListItem.cpp @no-unify UIProcess/API/glib/WebKitClipboardPermissionRequest.cpp @no-unify @@ -9787,7 +9815,7 @@ index 37f7f79dfbdb9913fe96b0393f6a2547501f5c6e..f34cf6031976d89f73f872ba4029b793 UIProcess/API/glib/WebKitContextMenuClient.cpp @no-unify UIProcess/API/glib/WebKitCookieManager.cpp @no-unify UIProcess/API/glib/WebKitCredential.cpp @no-unify -@@ -264,6 +265,7 @@ UIProcess/glib/DisplayLinkGLib.cpp +@@ -263,6 +264,7 @@ UIProcess/glib/DisplayLinkGLib.cpp UIProcess/glib/DisplayVBlankMonitor.cpp UIProcess/glib/DisplayVBlankMonitorDRM.cpp UIProcess/glib/DisplayVBlankMonitorTimer.cpp @@ -9795,7 +9823,7 @@ index 37f7f79dfbdb9913fe96b0393f6a2547501f5c6e..f34cf6031976d89f73f872ba4029b793 UIProcess/glib/WebPageProxyGLib.cpp UIProcess/glib/WebProcessPoolGLib.cpp UIProcess/glib/WebProcessProxyGLib.cpp -@@ -280,6 +282,7 @@ UIProcess/gtk/ClipboardGtk4.cpp @no-unify +@@ -277,6 +279,7 @@ UIProcess/gtk/ClipboardGtk4.cpp @no-unify UIProcess/gtk/WebDateTimePickerGtk.cpp UIProcess/gtk/GtkSettingsManager.cpp UIProcess/gtk/HardwareAccelerationManager.cpp @@ -9803,7 +9831,7 @@ index 37f7f79dfbdb9913fe96b0393f6a2547501f5c6e..f34cf6031976d89f73f872ba4029b793 UIProcess/gtk/KeyBindingTranslator.cpp UIProcess/gtk/PointerLockManager.cpp @no-unify UIProcess/gtk/PointerLockManagerWayland.cpp @no-unify -@@ -292,6 +295,8 @@ UIProcess/gtk/ViewGestureControllerGtk.cpp +@@ -289,6 +292,8 @@ UIProcess/gtk/ViewGestureControllerGtk.cpp UIProcess/gtk/WebColorPickerGtk.cpp UIProcess/gtk/WebContextMenuProxyGtk.cpp UIProcess/gtk/WebDataListSuggestionsDropdownGtk.cpp @@ -9813,7 +9841,7 @@ index 37f7f79dfbdb9913fe96b0393f6a2547501f5c6e..f34cf6031976d89f73f872ba4029b793 UIProcess/gtk/WebPasteboardProxyGtk.cpp UIProcess/gtk/WebPopupMenuProxyGtk.cpp diff --git a/Source/WebKit/SourcesWPE.txt b/Source/WebKit/SourcesWPE.txt -index 92cad8cba5e36ffac473217f9120319bd7c839e7..fb42fe7eafe2c49891e31e1d456fef186b247064 100644 +index 3adaea12d66c3c266b2199495273ff0180f97fb7..ec853296463255f0755e86a52a59a2f6c9f2b28d 100644 --- a/Source/WebKit/SourcesWPE.txt +++ b/Source/WebKit/SourcesWPE.txt @@ -91,6 +91,7 @@ Shared/glib/ProcessExecutablePathGLib.cpp @@ -9824,7 +9852,7 @@ index 92cad8cba5e36ffac473217f9120319bd7c839e7..fb42fe7eafe2c49891e31e1d456fef18 Shared/libwpe/NativeWebKeyboardEventLibWPE.cpp Shared/libwpe/NativeWebMouseEventLibWPE.cpp Shared/libwpe/NativeWebTouchEventLibWPE.cpp -@@ -130,6 +131,7 @@ UIProcess/API/glib/WebKitAuthenticationRequest.cpp @no-unify +@@ -138,6 +139,7 @@ UIProcess/API/glib/WebKitAuthenticationRequest.cpp @no-unify UIProcess/API/glib/WebKitAutomationSession.cpp @no-unify UIProcess/API/glib/WebKitBackForwardList.cpp @no-unify UIProcess/API/glib/WebKitBackForwardListItem.cpp @no-unify @@ -9832,7 +9860,7 @@ index 92cad8cba5e36ffac473217f9120319bd7c839e7..fb42fe7eafe2c49891e31e1d456fef18 UIProcess/API/glib/WebKitContextMenuClient.cpp @no-unify UIProcess/API/glib/WebKitCookieManager.cpp @no-unify UIProcess/API/glib/WebKitCredential.cpp @no-unify -@@ -163,6 +165,7 @@ UIProcess/API/glib/WebKitOptionMenu.cpp @no-unify +@@ -171,6 +173,7 @@ UIProcess/API/glib/WebKitOptionMenu.cpp @no-unify UIProcess/API/glib/WebKitOptionMenuItem.cpp @no-unify UIProcess/API/glib/WebKitPermissionRequest.cpp @no-unify UIProcess/API/glib/WebKitPermissionStateQuery.cpp @no-unify @@ -9840,7 +9868,7 @@ index 92cad8cba5e36ffac473217f9120319bd7c839e7..fb42fe7eafe2c49891e31e1d456fef18 UIProcess/API/glib/WebKitPolicyDecision.cpp @no-unify UIProcess/API/glib/WebKitPrivate.cpp @no-unify UIProcess/API/glib/WebKitProtocolHandler.cpp @no-unify -@@ -199,6 +202,7 @@ UIProcess/API/soup/HTTPCookieStoreSoup.cpp +@@ -207,6 +210,7 @@ UIProcess/API/soup/HTTPCookieStoreSoup.cpp UIProcess/API/wpe/InputMethodFilterWPE.cpp @no-unify UIProcess/API/wpe/PageClientImpl.cpp @no-unify UIProcess/API/wpe/WebKitColor.cpp @no-unify @@ -9848,7 +9876,7 @@ index 92cad8cba5e36ffac473217f9120319bd7c839e7..fb42fe7eafe2c49891e31e1d456fef18 UIProcess/API/wpe/WebKitInputMethodContextWPE.cpp @no-unify UIProcess/API/wpe/WebKitPopupMenu.cpp @no-unify UIProcess/API/wpe/WebKitRectangle.cpp @no-unify -@@ -223,6 +227,7 @@ UIProcess/glib/DisplayLinkGLib.cpp +@@ -231,6 +235,7 @@ UIProcess/glib/DisplayLinkGLib.cpp UIProcess/glib/DisplayVBlankMonitor.cpp UIProcess/glib/DisplayVBlankMonitorDRM.cpp UIProcess/glib/DisplayVBlankMonitorTimer.cpp @@ -9856,10 +9884,10 @@ index 92cad8cba5e36ffac473217f9120319bd7c839e7..fb42fe7eafe2c49891e31e1d456fef18 UIProcess/glib/WebPageProxyGLib.cpp UIProcess/glib/WebProcessPoolGLib.cpp UIProcess/glib/WebProcessProxyGLib.cpp -@@ -251,6 +256,11 @@ UIProcess/linux/MemoryPressureMonitor.cpp - +@@ -260,6 +265,11 @@ UIProcess/linux/MemoryPressureMonitor.cpp UIProcess/soup/WebProcessPoolSoup.cpp + UIProcess/wpe/AcceleratedBackingStoreDMABuf.cpp +UIProcess/wpe/InspectorTargetProxyWPE.cpp +UIProcess/wpe/WebColorPickerWPE.cpp +UIProcess/wpe/WebDateTimePickerWPE.cpp @@ -9868,7 +9896,7 @@ index 92cad8cba5e36ffac473217f9120319bd7c839e7..fb42fe7eafe2c49891e31e1d456fef18 UIProcess/wpe/WebPageProxyWPE.cpp WebProcess/GPU/graphics/gbm/RemoteGraphicsContextGLProxyGBM.cpp -@@ -273,6 +283,8 @@ WebProcess/WebCoreSupport/glib/WebEditorClientGLib.cpp +@@ -282,6 +292,8 @@ WebProcess/WebCoreSupport/glib/WebEditorClientGLib.cpp WebProcess/WebCoreSupport/soup/WebFrameNetworkingContext.cpp @@ -9925,10 +9953,10 @@ index dbbfea6be4b6f1ae3bd2070dc9b8e79fdbf28ff3..b7dd65cb00d64f67805597ba7a66f1a6 bool m_shouldTakeUIBackgroundAssertion { true }; bool m_shouldCaptureDisplayInUIProcess { DEFAULT_CAPTURE_DISPLAY_IN_UI_PROCESS }; diff --git a/Source/WebKit/UIProcess/API/APIUIClient.h b/Source/WebKit/UIProcess/API/APIUIClient.h -index 92813cde60765506eb09eb76831c8f194720cf03..eb2076c9cbbd2c113691598af5e548dcd96c1502 100644 +index 2705d0c8de8abefe9ee5b144b7bc7337d8c97bc0..79e0c1e16d3d839a3c38ebf79cbc931f0227c6b5 100644 --- a/Source/WebKit/UIProcess/API/APIUIClient.h +++ b/Source/WebKit/UIProcess/API/APIUIClient.h -@@ -112,6 +112,7 @@ public: +@@ -113,6 +113,7 @@ public: virtual void runJavaScriptAlert(WebKit::WebPageProxy&, const WTF::String&, WebKit::WebFrameProxy*, WebKit::FrameInfoData&&, Function&& completionHandler) { completionHandler(); } virtual void runJavaScriptConfirm(WebKit::WebPageProxy&, const WTF::String&, WebKit::WebFrameProxy*, WebKit::FrameInfoData&&, Function&& completionHandler) { completionHandler(false); } virtual void runJavaScriptPrompt(WebKit::WebPageProxy&, const WTF::String&, const WTF::String&, WebKit::WebFrameProxy*, WebKit::FrameInfoData&&, Function&& completionHandler) { completionHandler(WTF::String()); } @@ -9980,10 +10008,10 @@ index 026121d114c5fcad84c1396be8d692625beaa3bd..edd6e5cae033124c589959a42522fde0 } #endif diff --git a/Source/WebKit/UIProcess/API/C/WKPage.cpp b/Source/WebKit/UIProcess/API/C/WKPage.cpp -index 8a6927f9f665f2d9fa7b9d0a813dd945a329d724..5c5610e1157bb654b9226dc7013ca1cc806d3005 100644 +index 7cdd74be483252b47bd681dbab7c6810e2861f08..f89fc5a069b1ab132d5a45a744ca6802302fb2af 100644 --- a/Source/WebKit/UIProcess/API/C/WKPage.cpp +++ b/Source/WebKit/UIProcess/API/C/WKPage.cpp -@@ -1784,6 +1784,13 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient +@@ -1785,6 +1785,13 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient completionHandler(String()); } @@ -9997,7 +10025,7 @@ index 8a6927f9f665f2d9fa7b9d0a813dd945a329d724..5c5610e1157bb654b9226dc7013ca1cc void setStatusText(WebPageProxy* page, const String& text) final { if (!m_client.setStatusText) -@@ -1813,6 +1820,8 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient +@@ -1814,6 +1821,8 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient { if (!m_client.didNotHandleKeyEvent) return; @@ -10007,7 +10035,7 @@ index 8a6927f9f665f2d9fa7b9d0a813dd945a329d724..5c5610e1157bb654b9226dc7013ca1cc } diff --git a/Source/WebKit/UIProcess/API/C/WKPageUIClient.h b/Source/WebKit/UIProcess/API/C/WKPageUIClient.h -index 65d8ab73430840b02682ce879d1db18b9597d6b0..ea89752f4b90018ea1f008e0d89f9a2a80d1a8e1 100644 +index 1484f064ec89ee8c25c35df9f0a4462896699415..0622f4d5fc9144b9059395d9d0730a4ae00b7497 100644 --- a/Source/WebKit/UIProcess/API/C/WKPageUIClient.h +++ b/Source/WebKit/UIProcess/API/C/WKPageUIClient.h @@ -98,6 +98,7 @@ typedef void (*WKPageRunBeforeUnloadConfirmPanelCallback)(WKPageRef page, WKStri @@ -10018,7 +10046,7 @@ index 65d8ab73430840b02682ce879d1db18b9597d6b0..ea89752f4b90018ea1f008e0d89f9a2a typedef void (*WKPageRequestStorageAccessConfirmCallback)(WKPageRef page, WKFrameRef frame, WKStringRef requestingDomain, WKStringRef currentDomain, WKPageRequestStorageAccessConfirmResultListenerRef listener, const void *clientInfo); typedef void (*WKPageTakeFocusCallback)(WKPageRef page, WKFocusDirection direction, const void *clientInfo); typedef void (*WKPageFocusCallback)(WKPageRef page, const void *clientInfo); -@@ -1365,6 +1366,7 @@ typedef struct WKPageUIClientV14 { +@@ -1364,6 +1365,7 @@ typedef struct WKPageUIClientV14 { // Version 14. WKPageRunWebAuthenticationPanelCallback runWebAuthenticationPanel; @@ -10026,7 +10054,7 @@ index 65d8ab73430840b02682ce879d1db18b9597d6b0..ea89752f4b90018ea1f008e0d89f9a2a } WKPageUIClientV14; typedef struct WKPageUIClientV15 { -@@ -1472,6 +1474,7 @@ typedef struct WKPageUIClientV15 { +@@ -1471,6 +1473,7 @@ typedef struct WKPageUIClientV15 { // Version 14. WKPageRunWebAuthenticationPanelCallback runWebAuthenticationPanel; @@ -10034,7 +10062,7 @@ index 65d8ab73430840b02682ce879d1db18b9597d6b0..ea89752f4b90018ea1f008e0d89f9a2a // Version 15. WKPageDecidePolicyForSpeechRecognitionPermissionRequestCallback decidePolicyForSpeechRecognitionPermissionRequest; -@@ -1583,6 +1586,7 @@ typedef struct WKPageUIClientV16 { +@@ -1582,6 +1585,7 @@ typedef struct WKPageUIClientV16 { // Version 14. WKPageRunWebAuthenticationPanelCallback runWebAuthenticationPanel; @@ -10042,7 +10070,7 @@ index 65d8ab73430840b02682ce879d1db18b9597d6b0..ea89752f4b90018ea1f008e0d89f9a2a // Version 15. WKPageDecidePolicyForSpeechRecognitionPermissionRequestCallback decidePolicyForSpeechRecognitionPermissionRequest; -@@ -1697,6 +1701,7 @@ typedef struct WKPageUIClientV17 { +@@ -1696,6 +1700,7 @@ typedef struct WKPageUIClientV17 { // Version 14. WKPageRunWebAuthenticationPanelCallback runWebAuthenticationPanel; @@ -10050,7 +10078,7 @@ index 65d8ab73430840b02682ce879d1db18b9597d6b0..ea89752f4b90018ea1f008e0d89f9a2a // Version 15. WKPageDecidePolicyForSpeechRecognitionPermissionRequestCallback decidePolicyForSpeechRecognitionPermissionRequest; -@@ -1813,6 +1818,7 @@ typedef struct WKPageUIClientV18 { +@@ -1810,6 +1815,7 @@ typedef struct WKPageUIClientV18 { // Version 14. WKPageRunWebAuthenticationPanelCallback runWebAuthenticationPanel; @@ -10058,7 +10086,7 @@ index 65d8ab73430840b02682ce879d1db18b9597d6b0..ea89752f4b90018ea1f008e0d89f9a2a // Version 15. WKPageDecidePolicyForSpeechRecognitionPermissionRequestCallback decidePolicyForSpeechRecognitionPermissionRequest; -@@ -1932,6 +1938,7 @@ typedef struct WKPageUIClientV19 { +@@ -1926,6 +1932,7 @@ typedef struct WKPageUIClientV19 { // Version 14. WKPageRunWebAuthenticationPanelCallback runWebAuthenticationPanel; @@ -10067,7 +10095,7 @@ index 65d8ab73430840b02682ce879d1db18b9597d6b0..ea89752f4b90018ea1f008e0d89f9a2a // Version 15. WKPageDecidePolicyForSpeechRecognitionPermissionRequestCallback decidePolicyForSpeechRecognitionPermissionRequest; diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm b/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm -index 039731566b16ebdf9ae4db1b2f64d809075adda8..bf09734942cea6caa5faa6ddf3d8036d2df55ccd 100644 +index 06f4f8c01e1cb79cadc97b2b027e863598d4c80b..b0c7b5a2e955be3c017c07282b43ad916013bb60 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm @@ -706,6 +706,16 @@ - (void)_setMediaCaptureRequiresSecureConnection:(BOOL)requiresSecureConnection @@ -10088,7 +10116,7 @@ index 039731566b16ebdf9ae4db1b2f64d809075adda8..bf09734942cea6caa5faa6ddf3d8036d { return _preferences->inactiveMediaCaptureSteamRepromptIntervalInMinutes(); diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h -index 00dae895eda9db80aac60bd927bac1f68756e9d5..11131d66ff21266ac52bc873d497a8625082be35 100644 +index bfbccd96e33ba6be40a5e48e193a89642d654ecd..c3882525c854709a5d9e753cebbd2d6d225f1f66 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h +++ b/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h @@ -121,6 +121,7 @@ typedef NS_ENUM(NSInteger, _WKPitchCorrectionAlgorithm) { @@ -10138,7 +10166,7 @@ index 4f5956098f0e83c2e9c421c97056b6718b124a3c..1eb51dd70dc6ef1b7e95a09118aa816b NS_ASSUME_NONNULL_END diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm -index 2e52c12e36fde4dc6eb16dea399c4a201ba522e3..52a262a9da3fd7c9ea67f2ee8830e469f487a11d 100644 +index fbf31f22087d243e4cf5a5c262eb068ae9f02b29..8f1379d479a2556879d17f090618225ce08872a7 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm @@ -50,6 +50,7 @@ @@ -10147,9 +10175,9 @@ index 2e52c12e36fde4dc6eb16dea399c4a201ba522e3..52a262a9da3fd7c9ea67f2ee8830e469 #import "_WKWebsiteDataStoreDelegate.h" +#import #import + #import #import - #import -@@ -382,6 +383,11 @@ - (void)removeDataOfTypes:(NSSet *)dataTypes modifiedSince:(NSDate *)date comple +@@ -405,6 +406,11 @@ - (void)removeDataOfTypes:(NSSet *)dataTypes modifiedSince:(NSDate *)date comple }); } @@ -10560,10 +10588,10 @@ index 0000000000000000000000000000000000000000..e0b1da48465c850f541532ed961d1b77 +WebKit::WebPageProxy* webkitBrowserInspectorCreateNewPageInContext(WebKitWebContext*); +void webkitBrowserInspectorQuitApplication(); diff --git a/Source/WebKit/UIProcess/API/glib/WebKitUIClient.cpp b/Source/WebKit/UIProcess/API/glib/WebKitUIClient.cpp -index d75c5ee48b2cedc8df984841fd52478b0620078c..d9cd7bf5749ff192eb938b12b4c2d82bb73e6026 100644 +index d0c42fa8f30c14c0ff3f37341fc163f387585b85..fb0ba2db94d9f193e1ad217bfdfd8059286cc21c 100644 --- a/Source/WebKit/UIProcess/API/glib/WebKitUIClient.cpp +++ b/Source/WebKit/UIProcess/API/glib/WebKitUIClient.cpp -@@ -93,6 +93,10 @@ private: +@@ -94,6 +94,10 @@ private: page.makeViewBlankIfUnpaintedSinceLastLoadCommit(); webkitWebViewRunJavaScriptPrompt(m_webView, message.utf8(), defaultValue.utf8(), WTFMove(completionHandler)); } @@ -10644,7 +10672,7 @@ index e994309b097c1b140abfa4373fd2fafee46c05ec..6e0cc677a3bf33683ae8c89d12a48191 #endif +int webkitWebContextExistingCount(); diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp b/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp -index e88f6bcb93750dc47e616758d91eb8c5ae7de85a..ffbe6424d8433ee617ed63f37f8f6f09f9ad9eef 100644 +index 28f1f70a5caf1b2736d0995c6fefe31ef0029a5b..abc294f091d8315bb9bbdc8f51efd8e47498300b 100644 --- a/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp +++ b/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp @@ -34,6 +34,7 @@ @@ -10679,7 +10707,7 @@ index e88f6bcb93750dc47e616758d91eb8c5ae7de85a..ffbe6424d8433ee617ed63f37f8f6f09 DECIDE_POLICY, PERMISSION_REQUEST, -@@ -489,6 +491,9 @@ GRefPtr WebKitWebViewClient::showOptionMenu(WebKitPopupMenu& p +@@ -495,6 +497,9 @@ GRefPtr WebKitWebViewClient::showOptionMenu(WebKitPopupMenu& p void WebKitWebViewClient::frameDisplayed(WKWPE::View&) { @@ -10689,7 +10717,7 @@ index e88f6bcb93750dc47e616758d91eb8c5ae7de85a..ffbe6424d8433ee617ed63f37f8f6f09 { SetForScope inFrameDisplayedGuard(m_webView->priv->inFrameDisplayed, true); for (const auto& callback : m_webView->priv->frameDisplayedCallbacks) { -@@ -505,6 +510,11 @@ void WebKitWebViewClient::frameDisplayed(WKWPE::View&) +@@ -511,6 +516,11 @@ void WebKitWebViewClient::frameDisplayed(WKWPE::View&) } } @@ -10701,7 +10729,7 @@ index e88f6bcb93750dc47e616758d91eb8c5ae7de85a..ffbe6424d8433ee617ed63f37f8f6f09 void WebKitWebViewClient::willStartLoad(WKWPE::View&) { webkitWebViewWillStartLoad(m_webView); -@@ -591,7 +601,7 @@ static gboolean webkitWebViewDecidePolicy(WebKitWebView*, WebKitPolicyDecision* +@@ -597,7 +607,7 @@ static gboolean webkitWebViewDecidePolicy(WebKitWebView*, WebKitPolicyDecision* static gboolean webkitWebViewPermissionRequest(WebKitWebView*, WebKitPermissionRequest* request) { @@ -10710,7 +10738,7 @@ index e88f6bcb93750dc47e616758d91eb8c5ae7de85a..ffbe6424d8433ee617ed63f37f8f6f09 if (WEBKIT_IS_POINTER_LOCK_PERMISSION_REQUEST(request)) { webkit_permission_request_allow(request); return TRUE; -@@ -1851,6 +1861,15 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass) +@@ -1904,6 +1914,15 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass) G_TYPE_BOOLEAN, 1, WEBKIT_TYPE_SCRIPT_DIALOG); @@ -10726,7 +10754,7 @@ index e88f6bcb93750dc47e616758d91eb8c5ae7de85a..ffbe6424d8433ee617ed63f37f8f6f09 /** * WebKitWebView::decide-policy: * @web_view: the #WebKitWebView on which the signal is emitted -@@ -2649,6 +2668,23 @@ void webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView* webView, const +@@ -2706,6 +2725,23 @@ void webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView* webView, const webkit_script_dialog_unref(webView->priv->currentScriptDialog); } @@ -10775,7 +10803,7 @@ index 805f9f638c1630b5e9310494ae2970262de001cc..add3e80896c2e82bdd12cee15c8014bf #include <@API_INCLUDE_PREFIX@/WebKitClipboardPermissionRequest.h> #include <@API_INCLUDE_PREFIX@/WebKitColorChooserRequest.h> diff --git a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp -index 0c0dda4c205cf57f6468226d26f10b7524d7637c..319c2ffe8bdd94fb9a618e05f5619147589e3a84 100644 +index f3fd0687050371f28230a348765d9e45b42127fe..c75d6ffe185378159305ab5caf44284b4ba38ed3 100644 --- a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp @@ -257,6 +257,8 @@ void PageClientImpl::doneWithKeyEvent(const NativeWebKeyboardEvent& event, bool @@ -10800,7 +10828,7 @@ index 0c0dda4c205cf57f6468226d26f10b7524d7637c..319c2ffe8bdd94fb9a618e05f5619147 void PageClientImpl::didChangeContentSize(const IntSize& size) diff --git a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h -index 588447de7e900946c4ab47664bf9fc86809cb641..45858ff872378b288b1173a300cd11a5858ddd77 100644 +index f4478ecd25baf61176a6325d563e44985c4a4418..171f922bb867dff5fe02b5116e895d8f0fb3e2a0 100644 --- a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h +++ b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h @@ -105,7 +105,7 @@ private: @@ -10913,7 +10941,7 @@ index 496079da90993ac37689b060b69ecd4a67c2b6a8..af30181ca922f16c0f6e245c70e5ce7d G_BEGIN_DECLS diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp -index 86d4fd3a8ce403cdd71b286f82debe7d134f0763..8e2b155018d12a3be84b00585e4607dc71af56af 100644 +index 0ac555f8a18a55c0aab84b59753057616c477998..3938e923c698d8a407ee494b30437e14c965b6fd 100644 --- a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp +++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp @@ -2853,6 +2853,11 @@ void webkitWebViewBaseResetClickCounter(WebKitWebViewBase* webkitWebViewBase) @@ -10944,7 +10972,7 @@ index 86d4fd3a8ce403cdd71b286f82debe7d134f0763..8e2b155018d12a3be84b00585e4607dc #if !USE(GTK4) diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h -index fec76e55f1680da5b248552db6c96134742bdcba..dfb0b07c2e2447b9b4ee3841b90726b245cfbdd3 100644 +index 19ec058363303ae2cd82189e072a04f70a49d061..f364b0c8a51f228235c3af83384067903e628d06 100644 --- a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h +++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h @@ -27,6 +27,7 @@ @@ -10964,14 +10992,14 @@ index fec76e55f1680da5b248552db6c96134742bdcba..dfb0b07c2e2447b9b4ee3841b90726b2 void webkitWebViewBaseSetEnableBackForwardNavigationGesture(WebKitWebViewBase*, bool enabled); WebKit::ViewGestureController* webkitWebViewBaseViewGestureController(WebKitWebViewBase*); -@@ -141,3 +142,5 @@ void webkitWebViewBaseToplevelWindowStateChanged(WebKitWebViewBase*, uint32_t, u +@@ -137,3 +138,5 @@ void webkitWebViewBaseToplevelWindowStateChanged(WebKitWebViewBase*, uint32_t, u void webkitWebViewBaseToplevelWindowMonitorChanged(WebKitWebViewBase*, GdkMonitor*); void webkitWebViewBaseCallAfterNextPresentationUpdate(WebKitWebViewBase*, CompletionHandler&&); + +WebKit::AcceleratedBackingStore* webkitWebViewBaseGetAcceleratedBackingStore(WebKitWebViewBase*); diff --git a/Source/WebKit/UIProcess/API/wpe/APIViewClient.h b/Source/WebKit/UIProcess/API/wpe/APIViewClient.h -index 5506eb798d7df211e975b88f293428d7a00e8f65..41b4a0169a4479a0f2131227aadc7350e976d6a1 100644 +index 26d1790017e528f26ae04dac635678d5494bfd04..48dbe50eb05628307264a350350ac19f0acb3ae3 100644 --- a/Source/WebKit/UIProcess/API/wpe/APIViewClient.h +++ b/Source/WebKit/UIProcess/API/wpe/APIViewClient.h @@ -26,6 +26,7 @@ @@ -10993,10 +11021,10 @@ index 5506eb798d7df211e975b88f293428d7a00e8f65..41b4a0169a4479a0f2131227aadc7350 virtual void didChangePageID(WKWPE::View&) { } virtual void didReceiveUserMessage(WKWPE::View&, WebKit::UserMessage&&, CompletionHandler&& completionHandler) { completionHandler(WebKit::UserMessage()); } diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp -index f927aeea12d50643000bcce90ce2d31ab5c17072..9fd3b35bb6d6831c0e9e7ec8e12d99ace3c40062 100644 +index d56de4b5b13b2d522532cfef6691083aabe3361c..6eb9204cecaad4f832478bbf8f01d0f94e940f3d 100644 --- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp -@@ -33,8 +33,11 @@ +@@ -33,9 +33,13 @@ #include "NativeWebWheelEvent.h" #include "TouchGestureController.h" #include "WPEWebView.h" @@ -11006,9 +11034,11 @@ index f927aeea12d50643000bcce90ce2d31ab5c17072..9fd3b35bb6d6831c0e9e7ec8e12d99ac #include "WebContextMenuProxyWPE.h" +#include "WebKitDataListSuggestionsDropdown.h" #include "WebKitPopupMenu.h" ++#include "WebColorPicker.h" #include + #include #include -@@ -190,7 +193,7 @@ WebCore::IntPoint PageClientImpl::accessibilityScreenToRootView(const WebCore::I +@@ -203,7 +207,7 @@ WebCore::IntPoint PageClientImpl::accessibilityScreenToRootView(const WebCore::I WebCore::IntRect PageClientImpl::rootViewToAccessibilityScreen(const WebCore::IntRect& rect) { @@ -11017,7 +11047,7 @@ index f927aeea12d50643000bcce90ce2d31ab5c17072..9fd3b35bb6d6831c0e9e7ec8e12d99ac } void PageClientImpl::doneWithKeyEvent(const NativeWebKeyboardEvent&, bool) -@@ -427,9 +430,55 @@ void PageClientImpl::selectionDidChange() +@@ -465,9 +469,55 @@ void PageClientImpl::selectionDidChange() m_view.selectionDidChange(); } @@ -11074,10 +11104,10 @@ index f927aeea12d50643000bcce90ce2d31ab5c17072..9fd3b35bb6d6831c0e9e7ec8e12d99ac + } // namespace WebKit diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h -index d14abff9ca046b2c3a1423c4a47341210b7198d1..f717239df9de9097bfa31193b817e26b3495f162 100644 +index a9d4dce37f48a796beebfd6fe830a5f487464eac..9c0b4bda1834a74b49744a3c28032815dadffbe8 100644 --- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h +++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h -@@ -161,9 +161,21 @@ private: +@@ -165,9 +165,21 @@ private: void didChangeWebPageID() const override; void selectionDidChange() override; @@ -11100,10 +11130,10 @@ index d14abff9ca046b2c3a1423c4a47341210b7198d1..f717239df9de9097bfa31193b817e26b }; diff --git a/Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp b/Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp -index 762e771e09ca5f40afbd32bd35930c6217272ddc..97698110ac1652e902135427552eaf6cfb03cb3e 100644 +index b451b164d5b4db602227eccf847a1d970ff18b53..d39b0e69d71370f3faf391b25bc993fcfcee8649 100644 --- a/Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp +++ b/Source/WebKit/UIProcess/API/wpe/WPEWebView.cpp -@@ -76,7 +76,9 @@ View::View(struct wpe_view_backend* backend, const API::PageConfiguration& baseC +@@ -92,7 +92,9 @@ View::View(struct wpe_view_backend* backend, WPEDisplay* display, const API::Pag if (preferences) { preferences->setAcceleratedCompositingEnabled(true); preferences->setForceCompositingMode(true); @@ -11394,7 +11424,7 @@ index e4b92ace1531090ae38a7aec3d3d4febf19aee84..43690f9ef4969a39084501613bfc00a7 + +cairo_surface_t* webkitWebViewBackendTakeScreenshot(WebKitWebViewBackend*); diff --git a/Source/WebKit/UIProcess/API/wpe/WebKitWebViewClient.h b/Source/WebKit/UIProcess/API/wpe/WebKitWebViewClient.h -index 8a8356b94e0632118a24bb9adf5a1fe72f10fb8d..f332ffe5e633dce8e8a7f0f2a411ca2905cd21a7 100644 +index 720c88818bdb4cde3cb58e95785454754f6c1396..7f702c0b922e13128522d2bb1ace6a233812a7d4 100644 --- a/Source/WebKit/UIProcess/API/wpe/WebKitWebViewClient.h +++ b/Source/WebKit/UIProcess/API/wpe/WebKitWebViewClient.h @@ -50,6 +50,9 @@ private: @@ -11408,10 +11438,10 @@ index 8a8356b94e0632118a24bb9adf5a1fe72f10fb8d..f332ffe5e633dce8e8a7f0f2a411ca29 void didChangePageID(WKWPE::View&) override; void didReceiveUserMessage(WKWPE::View&, WebKit::UserMessage&&, CompletionHandler&&) override; diff --git a/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp b/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp -index de794bd3c1e9a598ae76b326d6171cebf4289fa9..5db786bccc7db3be4ce82fac3e3d2f5ccd5a07a6 100644 +index 366d7094ee559f5d802fd8ae3fc8a7ebb1cd4e3d..bae518ccad18ef63895d27de30238ada41397a63 100644 --- a/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp +++ b/Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp -@@ -144,7 +144,11 @@ void AuxiliaryProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& lau +@@ -154,7 +154,11 @@ void AuxiliaryProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& lau launchOptions.processCmdPrefix = String::fromUTF8(processCmdPrefix); #endif // ENABLE(DEVELOPER_MODE) && (PLATFORM(GTK) || PLATFORM(WPE)) @@ -11424,10 +11454,10 @@ index de794bd3c1e9a598ae76b326d6171cebf4289fa9..5db786bccc7db3be4ce82fac3e3d2f5c platformGetLaunchOptions(launchOptions); } diff --git a/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h b/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h -index c951a2baaf6819908b2d18e276b048f2aa69c86c..12ac9e139ee9aad1e4e6502e01b03e5d16888113 100644 +index da9e76273c950d14cb6a4fdded0b8602869ff138..6bee432a0eeb10a0779ee1c6d11a8f1a8e1b17d0 100644 --- a/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h +++ b/Source/WebKit/UIProcess/AuxiliaryProcessProxy.h -@@ -221,13 +221,16 @@ protected: +@@ -230,13 +230,16 @@ protected: static RefPtr fetchAudioComponentServerRegistrations(); #endif @@ -11445,18 +11475,6 @@ index c951a2baaf6819908b2d18e276b048f2aa69c86c..12ac9e139ee9aad1e4e6502e01b03e5d void platformStartConnectionTerminationWatchdog(); ResponsivenessTimer m_responsivenessTimer; -diff --git a/Source/WebKit/UIProcess/BackingStore.h b/Source/WebKit/UIProcess/BackingStore.h -index 3bbb4b30ca3d78007ac700a033b7e4b695c557dd..22b94307bdb0adcf8c8f1f6440328804933ca21e 100644 ---- a/Source/WebKit/UIProcess/BackingStore.h -+++ b/Source/WebKit/UIProcess/BackingStore.h -@@ -51,6 +51,7 @@ public: - - #if USE(CAIRO) - typedef cairo_t* PlatformGraphicsContext; -+ cairo_surface_t* surface() const; - #endif - - void paint(PlatformGraphicsContext, const WebCore::IntRect&); diff --git a/Source/WebKit/UIProcess/BrowserInspectorPipe.cpp b/Source/WebKit/UIProcess/BrowserInspectorPipe.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cfb57a48ce387b79613b757e2eb4de2c378aac30 @@ -11590,18 +11608,18 @@ 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 ca367804b622de941ab3792edcf79d17f3a19193..6249e19713f3b13cca3e1081a3469ef5eef2f698 100644 +index f0247c0950af133e8f98b1eee872e55b53f83735..cd6c138502861228dfb19d9177396266a89cea6e 100644 --- a/Source/WebKit/UIProcess/Cocoa/UIDelegate.h +++ b/Source/WebKit/UIProcess/Cocoa/UIDelegate.h -@@ -95,6 +95,7 @@ private: +@@ -96,6 +96,7 @@ private: void runJavaScriptAlert(WebPageProxy&, const WTF::String&, WebFrameProxy*, FrameInfoData&&, Function&& completionHandler) final; void runJavaScriptConfirm(WebPageProxy&, const WTF::String&, WebFrameProxy*, FrameInfoData&&, Function&& completionHandler) final; void runJavaScriptPrompt(WebPageProxy&, const WTF::String&, const WTF::String&, WebFrameProxy*, FrameInfoData&&, Function&&) final; + void handleJavaScriptDialog(WebKit::WebPageProxy&, bool accept, const WTF::String&) final; void presentStorageAccessConfirmDialog(const WTF::String& requestingDomain, const WTF::String& currentDomain, CompletionHandler&&); - void requestStorageAccessConfirm(WebPageProxy&, WebFrameProxy*, const WebCore::RegistrableDomain& requestingDomain, const WebCore::RegistrableDomain& currentDomain, CompletionHandler&&) final; + void requestStorageAccessConfirm(WebPageProxy&, WebFrameProxy*, const WebCore::RegistrableDomain& requestingDomain, const WebCore::RegistrableDomain& currentDomain, std::optional&&, CompletionHandler&&) final; void decidePolicyForGeolocationPermissionRequest(WebPageProxy&, WebFrameProxy&, const FrameInfoData&, Function&) final; -@@ -205,6 +206,7 @@ private: +@@ -206,6 +207,7 @@ private: bool webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler : 1; bool webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler : 1; bool webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler : 1; @@ -11610,7 +11628,7 @@ index ca367804b622de941ab3792edcf79d17f3a19193..6249e19713f3b13cca3e1081a3469ef5 bool webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler : 1; bool webViewRequestGeolocationPermissionForFrameDecisionHandler : 1; diff --git a/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm b/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm -index be7f2e3a3dcdf9f22ff1a950b07f03488cbc1c3f..8148730b798729df7be190673f84a3c6e98c08bd 100644 +index c7e0d4dc60a0788898550f365db73e56bbf29b71..fd146a6bc8e8699745cdf42114dd0e672d870a5a 100644 --- a/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm +++ b/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm @@ -117,6 +117,7 @@ void UIDelegate::setDelegate(id delegate) @@ -11621,7 +11639,7 @@ index be7f2e3a3dcdf9f22ff1a950b07f03488cbc1c3f..8148730b798729df7be190673f84a3c6 m_delegateMethods.webViewRequestStorageAccessPanelUnderFirstPartyCompletionHandler = [delegate respondsToSelector:@selector(_webView:requestStorageAccessPanelForDomain:underCurrentDomain:completionHandler:)]; m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(_webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:completionHandler:)]; m_delegateMethods.webViewRequestGeolocationPermissionForOriginDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestGeolocationPermissionForOrigin:initiatedByFrame:decisionHandler:)]; -@@ -437,6 +438,15 @@ void UIDelegate::UIClient::runJavaScriptPrompt(WebPageProxy& page, const WTF::St +@@ -436,6 +437,15 @@ void UIDelegate::UIClient::runJavaScriptPrompt(WebPageProxy& page, const WTF::St }).get()]; } @@ -11634,11 +11652,11 @@ index be7f2e3a3dcdf9f22ff1a950b07f03488cbc1c3f..8148730b798729df7be190673f84a3c6 + [delegate webView:m_uiDelegate->m_webView.get().get() handleJavaScriptDialog:accept value:value]; +} + - void UIDelegate::UIClient::requestStorageAccessConfirm(WebPageProxy& webPageProxy, WebFrameProxy*, const WebCore::RegistrableDomain& requestingDomain, const WebCore::RegistrableDomain& currentDomain, CompletionHandler&& completionHandler) + void UIDelegate::UIClient::requestStorageAccessConfirm(WebPageProxy& webPageProxy, WebFrameProxy*, const WebCore::RegistrableDomain& requestingDomain, const WebCore::RegistrableDomain& currentDomain, std::optional&& organizationStorageAccessPromptQuirk, CompletionHandler&& completionHandler) { if (!m_uiDelegate) diff --git a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm -index 3c58aeaad9a258806eb6979bd40eb59f298ac190..05c638cdbad087fe8bbdd0ed9fe84b13e27feea1 100644 +index 6e1f94997e239818ab598513619ebca04a7d9939..729d8d078d18240aabca71aabab54ca325d93ca9 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm @@ -38,6 +38,7 @@ @@ -11739,10 +11757,10 @@ index 3c58aeaad9a258806eb6979bd40eb59f298ac190..05c638cdbad087fe8bbdd0ed9fe84b13 #if ENABLE(ATTACHMENT_ELEMENT) diff --git a/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm -index fa8a3ce8b0585f9e81308dcea0a9f6721b82e31d..2fc423491020f88ff19f4934699568f33150f141 100644 +index d28a3b778536b32488d5b0a2ba7fa3675459fd3d..a39b7c0f4f678f745c634a3cea801bf3f3178b4f 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm -@@ -404,7 +404,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END +@@ -402,7 +402,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END auto screenProperties = WebCore::collectScreenProperties(); parameters.screenProperties = WTFMove(screenProperties); #if PLATFORM(MAC) @@ -11751,7 +11769,7 @@ index fa8a3ce8b0585f9e81308dcea0a9f6721b82e31d..2fc423491020f88ff19f4934699568f3 #endif #if (PLATFORM(IOS) || PLATFORM(VISION)) && HAVE(AGX_COMPILER_SERVICE) -@@ -741,8 +741,8 @@ void WebProcessPool::registerNotificationObservers() +@@ -753,8 +753,8 @@ void WebProcessPool::registerNotificationObservers() }]; m_scrollerStyleNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSPreferredScrollerStyleDidChangeNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *notification) { @@ -11763,7 +11781,7 @@ index fa8a3ce8b0585f9e81308dcea0a9f6721b82e31d..2fc423491020f88ff19f4934699568f3 m_activationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidBecomeActiveNotification object:NSApp queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *notification) { diff --git a/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.cpp b/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.cpp -index ff5329fb9b917a9bc53e8b017516099a10b6925e..8f57d208a81c81d255a68ca7130059e956d5b3db 100644 +index 567d62f16b9be2e1dff1110031ee7e2b213af790..c3ff92f3e256e62050d38dad05565429585d7ea0 100644 --- a/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.cpp +++ b/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.cpp @@ -33,14 +33,17 @@ @@ -11784,8 +11802,8 @@ index ff5329fb9b917a9bc53e8b017516099a10b6925e..8f57d208a81c81d255a68ca7130059e9 #include #endif -@@ -48,6 +51,13 @@ - #include +@@ -52,6 +55,13 @@ + #include "BackingStore.h" #endif +#if PLATFORM(WIN) @@ -11798,8 +11816,8 @@ index ff5329fb9b917a9bc53e8b017516099a10b6925e..8f57d208a81c81d255a68ca7130059e9 namespace WebKit { using namespace WebCore; -@@ -161,6 +171,11 @@ void DrawingAreaProxyCoordinatedGraphics::deviceScaleFactorDidChange() - protectedWebPageProxy()->send(Messages::DrawingArea::SetDeviceScaleFactor(m_webPageProxy->deviceScaleFactor()), m_identifier); +@@ -164,6 +174,11 @@ void DrawingAreaProxyCoordinatedGraphics::deviceScaleFactorDidChange() + send(Messages::DrawingArea::SetDeviceScaleFactor(m_webPageProxy->deviceScaleFactor())); } +void DrawingAreaProxyCoordinatedGraphics::waitForSizeUpdate(Function&& callback) @@ -11810,7 +11828,7 @@ index ff5329fb9b917a9bc53e8b017516099a10b6925e..8f57d208a81c81d255a68ca7130059e9 void DrawingAreaProxyCoordinatedGraphics::setBackingStoreIsDiscardable(bool isBackingStoreDiscardable) { #if !PLATFORM(WPE) -@@ -222,6 +237,45 @@ void DrawingAreaProxyCoordinatedGraphics::updateAcceleratedCompositingMode(uint6 +@@ -225,6 +240,45 @@ void DrawingAreaProxyCoordinatedGraphics::updateAcceleratedCompositingMode(uint6 updateAcceleratedCompositingMode(layerTreeContext); } @@ -11856,7 +11874,7 @@ index ff5329fb9b917a9bc53e8b017516099a10b6925e..8f57d208a81c81d255a68ca7130059e9 bool DrawingAreaProxyCoordinatedGraphics::alwaysUseCompositing() const { return m_webPageProxy->preferences().acceleratedCompositingEnabled() && m_webPageProxy->preferences().forceCompositingMode(); -@@ -276,6 +330,11 @@ void DrawingAreaProxyCoordinatedGraphics::didUpdateGeometry() +@@ -279,6 +333,11 @@ void DrawingAreaProxyCoordinatedGraphics::didUpdateGeometry() // we need to resend the new size here. if (m_lastSentSize != m_size) sendUpdateGeometry(); @@ -11869,18 +11887,18 @@ index ff5329fb9b917a9bc53e8b017516099a10b6925e..8f57d208a81c81d255a68ca7130059e9 #if !PLATFORM(WPE) diff --git a/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.h b/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.h -index 16ce79d7f60b4f8df74ed74ad11502d269462a44..301e74438fd199c2021dd3108d17c56b5cb18d09 100644 +index 34d2107f37fbd76fbf7b995145b2ee2b0dd18b4c..7908b85755d6ac48d02dd7a1860ff8c350f24f8d 100644 --- a/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.h +++ b/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.h -@@ -30,6 +30,7 @@ - #include "BackingStore.h" +@@ -29,6 +29,7 @@ + #include "DrawingAreaProxy.h" #include "LayerTreeContext.h" +#include #include - namespace WebCore { -@@ -49,6 +50,10 @@ public: + #if !PLATFORM(WPE) +@@ -54,6 +55,10 @@ public: bool isInAcceleratedCompositingMode() const { return !m_layerTreeContext.isEmpty(); } const LayerTreeContext& layerTreeContext() const { return m_layerTreeContext; } @@ -11891,7 +11909,7 @@ index 16ce79d7f60b4f8df74ed74ad11502d269462a44..301e74438fd199c2021dd3108d17c56b void dispatchAfterEnsuringDrawing(CompletionHandler&&); -@@ -72,6 +77,9 @@ private: +@@ -77,6 +82,9 @@ private: void enterAcceleratedCompositingMode(uint64_t backingStoreStateID, const LayerTreeContext&) override; void exitAcceleratedCompositingMode(uint64_t backingStoreStateID, UpdateInfo&&) override; void updateAcceleratedCompositingMode(uint64_t backingStoreStateID, const LayerTreeContext&) override; @@ -11901,7 +11919,7 @@ index 16ce79d7f60b4f8df74ed74ad11502d269462a44..301e74438fd199c2021dd3108d17c56b bool shouldSendWheelEventsToEventDispatcher() const override { return true; } -@@ -114,6 +122,7 @@ private: +@@ -119,6 +127,7 @@ private: // The last size we sent to the web process. WebCore::IntSize m_lastSentSize; @@ -11909,7 +11927,7 @@ index 16ce79d7f60b4f8df74ed74ad11502d269462a44..301e74438fd199c2021dd3108d17c56b #if !PLATFORM(WPE) bool m_isBackingStoreDiscardable { true }; -@@ -122,6 +131,10 @@ private: +@@ -127,6 +136,10 @@ private: RunLoop::Timer m_discardBackingStoreTimer; #endif std::unique_ptr m_drawingMonitor; @@ -11921,7 +11939,7 @@ index 16ce79d7f60b4f8df74ed74ad11502d269462a44..301e74438fd199c2021dd3108d17c56b } // namespace WebKit diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp b/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp -index c6eeb8dca2ad5eb2b8a385cea8bf33795b15fd3a..14c9ef83023b1303d1c05fa73aaa67618e0502f8 100644 +index aefa745dee839ab9a86c72d398f69fe3e43e156d..8cc96adac7a6f789fbb716f87165b0776c74f603 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp @@ -41,8 +41,10 @@ @@ -11960,7 +11978,7 @@ index c6eeb8dca2ad5eb2b8a385cea8bf33795b15fd3a..14c9ef83023b1303d1c05fa73aaa6761 m_downloadProxyMap.downloadFinished(*this); }); } else -@@ -166,6 +174,21 @@ void DownloadProxy::decideDestinationWithSuggestedFilename(const WebCore::Resour +@@ -163,6 +171,21 @@ void DownloadProxy::decideDestinationWithSuggestedFilename(const WebCore::Resour suggestedFilename = m_suggestedFilename; suggestedFilename = MIMETypeRegistry::appendFileExtensionIfNecessary(suggestedFilename, response.mimeType()); @@ -11982,7 +12000,7 @@ index c6eeb8dca2ad5eb2b8a385cea8bf33795b15fd3a..14c9ef83023b1303d1c05fa73aaa6761 m_client->decideDestinationWithSuggestedFilename(*this, response, ResourceResponseBase::sanitizeSuggestedFilename(suggestedFilename), [this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)] (AllowOverwrite allowOverwrite, String destination) mutable { SandboxExtension::Handle sandboxExtensionHandle; if (!destination.isNull()) { -@@ -214,6 +237,8 @@ void DownloadProxy::didFinish() +@@ -211,6 +234,8 @@ void DownloadProxy::didFinish() updateQuarantinePropertiesIfPossible(); #endif m_client->didFinish(*this); @@ -11991,7 +12009,7 @@ index c6eeb8dca2ad5eb2b8a385cea8bf33795b15fd3a..14c9ef83023b1303d1c05fa73aaa6761 // This can cause the DownloadProxy object to be deleted. m_downloadProxyMap.downloadFinished(*this); -@@ -224,6 +249,8 @@ void DownloadProxy::didFail(const ResourceError& error, const IPC::DataReference +@@ -221,6 +246,8 @@ void DownloadProxy::didFail(const ResourceError& error, const IPC::DataReference m_legacyResumeData = createData(resumeData); m_client->didFail(*this, error, m_legacyResumeData.get()); @@ -12001,7 +12019,7 @@ index c6eeb8dca2ad5eb2b8a385cea8bf33795b15fd3a..14c9ef83023b1303d1c05fa73aaa6761 // This can cause the DownloadProxy object to be deleted. m_downloadProxyMap.downloadFinished(*this); diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxy.h b/Source/WebKit/UIProcess/Downloads/DownloadProxy.h -index 9b69cad753b5b2e3844caac57b44c067507e68e7..1e898d7311a2cb8cb6d9a4042f91f41c552dcea3 100644 +index 6ec99825d6c232c391986aed9099d0595857ce75..818341cf6549ffb68209c1472cc648395873dd6c 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxy.h +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxy.h @@ -144,6 +144,7 @@ private: @@ -12013,10 +12031,10 @@ index 9b69cad753b5b2e3844caac57b44c067507e68e7..1e898d7311a2cb8cb6d9a4042f91f41c } // namespace WebKit diff --git a/Source/WebKit/UIProcess/DrawingAreaProxy.h b/Source/WebKit/UIProcess/DrawingAreaProxy.h -index f740edbcc1e243089bb9ad48b03d58cf1587bd8d..24c271c24298bcd6145ddd5efba9fbb792753bd4 100644 +index ea2e74070b5708a271a2327ca3c21c4273b2e126..feeb9af65099fa7cf1c4b28a22c9efc305afa0e8 100644 --- a/Source/WebKit/UIProcess/DrawingAreaProxy.h +++ b/Source/WebKit/UIProcess/DrawingAreaProxy.h -@@ -87,6 +87,7 @@ public: +@@ -88,6 +88,7 @@ public: const WebCore::IntSize& size() const { return m_size; } bool setSize(const WebCore::IntSize&, const WebCore::IntSize& scrollOffset = { }); @@ -12024,7 +12042,7 @@ index f740edbcc1e243089bb9ad48b03d58cf1587bd8d..24c271c24298bcd6145ddd5efba9fbb7 virtual void minimumSizeForAutoLayoutDidChange() { } virtual void sizeToContentAutoSizeMaximumSizeDidChange() { } -@@ -162,6 +163,10 @@ private: +@@ -170,6 +171,10 @@ private: virtual void update(uint64_t /* backingStoreStateID */, UpdateInfo&&) { } virtual void exitAcceleratedCompositingMode(uint64_t /* backingStoreStateID */, UpdateInfo&&) { } #endif @@ -13481,7 +13499,7 @@ index a2239cec8e18850f35f7f88a9c4ebadc62bf4023..79f3ff84327dc075ec96983e04db4b10 } // namespace WebKit diff --git a/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp b/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp -index 6d229b943fe69cd258b32b1e38c5716715a4cd4b..77a7bacae6c09488b2c44dd1fd758679532c1a24 100644 +index 6d229b943fe69cd258b32b1e38c5716715a4cd4b..7294731b4ff75a0cec1dd010cc7facaf140b6a8a 100644 --- a/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp +++ b/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp @@ -26,13 +26,21 @@ @@ -13532,7 +13550,7 @@ index 6d229b943fe69cd258b32b1e38c5716715a4cd4b..77a7bacae6c09488b2c44dd1fd758679 } -Ref WebPageInspectorController::protectedInspectedPage() -+CheckedRef WebPageInspectorController::protectedInspectedPage() ++WeakRef WebPageInspectorController::protectedInspectedPage() { - return m_inspectedPage.get(); + return m_inspectedPage; @@ -13808,7 +13826,7 @@ index 6d229b943fe69cd258b32b1e38c5716715a4cd4b..77a7bacae6c09488b2c44dd1fd758679 + } // namespace WebKit diff --git a/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h b/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h -index 0a44e45c3d208942ba1bbf0624ae241ca7bbd973..7e891a1fe3e2588e0aa286c4cbb2064967379fcb 100644 +index c6aafe0e9339c8ac02dc133754ddc23e1cb522ff..91473e6a0b4a37a15959f966fc75134c15e1112d 100644 --- a/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h +++ b/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h @@ -26,6 +26,7 @@ @@ -13925,7 +13943,7 @@ index 0a44e45c3d208942ba1bbf0624ae241ca7bbd973..7e891a1fe3e2588e0aa286c4cbb20649 private: - Ref protectedInspectedPage(); -+ CheckedRef protectedInspectedPage(); ++ WeakRef protectedInspectedPage(); WebPageAgentContext webPageAgentContext(); void createLazyAgents(); @@ -13935,7 +13953,7 @@ index 0a44e45c3d208942ba1bbf0624ae241ca7bbd973..7e891a1fe3e2588e0aa286c4cbb20649 Ref m_frontendRouter; Ref m_backendDispatcher; @@ -97,11 +161,17 @@ private: - CheckedRef m_inspectedPage; + WeakRef m_inspectedPage; Inspector::InspectorTargetAgent* m_targetAgent { nullptr }; + WebPageInspectorEmulationAgent* m_emulationAgent { nullptr }; @@ -15453,10 +15471,10 @@ index 3fe0abcfe36bef7ca45bed5661a737ed2bfe56d0..510656948af01ec65d4543c805e9667a #include "RemoteMediaSessionCoordinatorProxyMessages.h" #include "WebPageProxy.h" diff --git a/Source/WebKit/UIProcess/PageClient.h b/Source/WebKit/UIProcess/PageClient.h -index f906572268a24f05062d0a844b783a3ed00e1679..4467af8a12f62b8f69e50c90570935be01d003b8 100644 +index 6350a51ecd961ef328e22eb2decc1ac84c331f77..7371bed49a39310ec37cb63424f73b01a28a04b1 100644 --- a/Source/WebKit/UIProcess/PageClient.h +++ b/Source/WebKit/UIProcess/PageClient.h -@@ -84,6 +84,10 @@ OBJC_CLASS WKView; +@@ -85,6 +85,10 @@ OBJC_CLASS WKView; #endif #endif @@ -15467,7 +15485,7 @@ index f906572268a24f05062d0a844b783a3ed00e1679..4467af8a12f62b8f69e50c90570935be namespace API { class Attachment; class HitTestResult; -@@ -332,7 +336,16 @@ public: +@@ -333,7 +337,16 @@ public: virtual void selectionDidChange() = 0; #endif @@ -15925,7 +15943,7 @@ index 0000000000000000000000000000000000000000..6d04f9290135069359ce6bf872654648 + +#endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.h b/Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.h -index f8c06bc779b6be310018840147a044b7c34fed3c..0c35907c6d0e0d2c3bba1f40b2ef59fb52f9761f 100644 +index 0b2f336b881a6d79904f709845f99bc4a32a0d6e..544093eeca0794981f05630ee2b0a43644ba57f0 100644 --- a/Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.h +++ b/Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.h @@ -39,6 +39,11 @@ @@ -15941,7 +15959,7 @@ index f8c06bc779b6be310018840147a044b7c34fed3c..0c35907c6d0e0d2c3bba1f40b2ef59fb namespace WebCore { class PlatformWheelEvent; diff --git a/Source/WebKit/UIProcess/RemotePageProxy.cpp b/Source/WebKit/UIProcess/RemotePageProxy.cpp -index 58e3200ece686308bb282f913aa074ed2d18df92..4e29489d67e3c5486b669f291d36e82138fe970b 100644 +index ff400b793ac18f3e427ef37c366e28560083836b..c8844c536950fdf58d0fb6733896766b053cdbd2 100644 --- a/Source/WebKit/UIProcess/RemotePageProxy.cpp +++ b/Source/WebKit/UIProcess/RemotePageProxy.cpp @@ -41,6 +41,7 @@ @@ -15977,7 +15995,7 @@ index e7d6621532fcc73212cc9130a7cafbb13f458d1d..ec931c8f19d2a61c0fa7d78b52df7f3f WebPageProxy* page() const { return m_page.get(); } diff --git a/Source/WebKit/UIProcess/WebFrameProxy.cpp b/Source/WebKit/UIProcess/WebFrameProxy.cpp -index 582a8a4e432af9b896b4c1b8885de8dcb717252a..930b5ff816a60ba3db82ad3dc0d0c3f0e17a8b5c 100644 +index dbe7167fd280aed7abb66335f9174f5555106752..a78d14744a751247140b92a6b58ae82aefa3c0dd 100644 --- a/Source/WebKit/UIProcess/WebFrameProxy.cpp +++ b/Source/WebKit/UIProcess/WebFrameProxy.cpp @@ -30,6 +30,7 @@ @@ -16683,10 +16701,10 @@ index 0000000000000000000000000000000000000000..3e87bf40ced2301f4fb145c6cb31f2cf + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp -index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f5254d5537a 100644 +index 77e64447dd28a4221729d81576fc1340010c6a95..f26696fb69532cd6fa081a7721e9aa34bf933435 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.cpp +++ b/Source/WebKit/UIProcess/WebPageProxy.cpp -@@ -175,12 +175,14 @@ +@@ -176,12 +176,14 @@ #include #include #include @@ -16701,16 +16719,17 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 #include #include #include -@@ -199,17 +201,20 @@ - #include +@@ -201,6 +203,7 @@ #include #include + #include +#include #include #include #include - #include +@@ -208,11 +211,13 @@ #include + #include #include +#include #include @@ -16722,7 +16741,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 #include #include #include -@@ -281,6 +286,9 @@ +@@ -285,6 +290,9 @@ #include "AcceleratedBackingStoreDMABuf.h" #endif #include "GtkSettingsManager.h" @@ -16732,7 +16751,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 #include #endif -@@ -390,6 +398,8 @@ static constexpr Seconds tryCloseTimeoutDelay = 50_ms; +@@ -394,6 +402,8 @@ static constexpr Seconds tryCloseTimeoutDelay = 50_ms; static constexpr Seconds audibleActivityClearDelay = 10_s; #endif @@ -16741,7 +16760,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webPageProxyCounter, ("WebPageProxy")); #if PLATFORM(COCOA) -@@ -761,6 +771,10 @@ WebPageProxy::~WebPageProxy() +@@ -815,6 +825,10 @@ WebPageProxy::~WebPageProxy() if (preferences->mediaSessionCoordinatorEnabled()) GroupActivitiesSessionNotifier::sharedNotifier().removeWebPage(*this); #endif @@ -16752,7 +16771,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } void WebPageProxy::addAllMessageReceivers() -@@ -1247,6 +1261,7 @@ void WebPageProxy::finishAttachingToWebProcess(ProcessLaunchReason reason) +@@ -1313,6 +1327,7 @@ void WebPageProxy::finishAttachingToWebProcess(ProcessLaunchReason reason) pageClient().didRelaunchProcess(); internals().pageLoadState.didSwapWebProcesses(); @@ -16760,7 +16779,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } void WebPageProxy::didAttachToRunningProcess() -@@ -1255,7 +1270,7 @@ void WebPageProxy::didAttachToRunningProcess() +@@ -1321,7 +1336,7 @@ void WebPageProxy::didAttachToRunningProcess() #if ENABLE(FULLSCREEN_API) ASSERT(!m_fullScreenManager); @@ -16769,7 +16788,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 #endif #if ENABLE(VIDEO_PRESENTATION_MODE) ASSERT(!m_playbackSessionManager); -@@ -1656,6 +1671,21 @@ WebProcessProxy& WebPageProxy::ensureRunningProcess() +@@ -1721,6 +1736,21 @@ WebProcessProxy& WebPageProxy::ensureRunningProcess() return m_process; } @@ -16791,7 +16810,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 RefPtr WebPageProxy::loadRequest(ResourceRequest&& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData) { if (m_isClosed) -@@ -2218,6 +2248,42 @@ void WebPageProxy::setControlledByAutomation(bool controlled) +@@ -2294,6 +2324,42 @@ void WebPageProxy::setControlledByAutomation(bool controlled) websiteDataStore().protectedNetworkProcess()->send(Messages::NetworkProcess::SetSessionIsControlledByAutomation(m_websiteDataStore->sessionID(), m_controlledByAutomation), 0); } @@ -16834,7 +16853,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 void WebPageProxy::createInspectorTarget(const String& targetId, Inspector::InspectorTargetType type) { MESSAGE_CHECK(m_process, !targetId.isEmpty()); -@@ -2460,6 +2526,25 @@ void WebPageProxy::updateActivityState(OptionSet flagsToUpdate) +@@ -2534,6 +2600,25 @@ void WebPageProxy::updateActivityState(OptionSet flagsToUpdate) { bool wasVisible = isViewVisible(); internals().activityState.remove(flagsToUpdate); @@ -16860,7 +16879,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 if (flagsToUpdate & ActivityState::IsFocused && pageClient().isViewFocused()) internals().activityState.add(ActivityState::IsFocused); if (flagsToUpdate & ActivityState::WindowIsActive && pageClient().isViewWindowActive()) -@@ -3146,7 +3231,7 @@ void WebPageProxy::performDragOperation(DragData& dragData, const String& dragSt +@@ -3233,7 +3318,7 @@ void WebPageProxy::performDragOperation(DragData& dragData, const String& dragSt grantAccessToCurrentPasteboardData(dragStorageName); #endif @@ -16869,7 +16888,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 performDragControllerAction(DragControllerAction::PerformDragOperation, dragData); #else if (!hasRunningProcess()) -@@ -3163,6 +3248,8 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag +@@ -3250,6 +3335,8 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag if (!hasRunningProcess()) return; @@ -16878,7 +16897,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 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); -@@ -3179,6 +3266,8 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag +@@ -3266,6 +3353,8 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag protectedProcess()->assumeReadAccessToBaseURL(*this, url); ASSERT(dragData.platformData()); @@ -16887,7 +16906,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 sendWithAsyncReply(Messages::WebPage::PerformDragControllerAction(action, dragData.clientPosition(), dragData.globalPosition(), dragData.draggingSourceOperationMask(), *dragData.platformData(), dragData.flags()), WTFMove(completionHandler)); #else sendToProcessContainingFrame(frameID, Messages::WebPage::PerformDragControllerAction(frameID, action, dragData), WTFMove(completionHandler)); -@@ -3194,14 +3283,36 @@ void WebPageProxy::didPerformDragControllerAction(std::optional dragOperationMask, const std::optional& frameID) + { + if (!hasRunningProcess()) +@@ -3309,6 +3420,24 @@ void WebPageProxy::dragEnded(const IntPoint& clientPosition, const IntPoint& glo setDragCaretRect({ }); } @@ -16952,7 +16975,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 void WebPageProxy::didStartDrag() { if (!hasRunningProcess()) -@@ -3229,6 +3358,16 @@ void WebPageProxy::didStartDrag() +@@ -3316,6 +3445,16 @@ void WebPageProxy::didStartDrag() discardQueuedMouseEvents(); send(Messages::WebPage::DidStartDrag()); @@ -16969,7 +16992,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } void WebPageProxy::dragCancelled() -@@ -3373,17 +3512,38 @@ void WebPageProxy::processNextQueuedMouseEvent() +@@ -3461,16 +3600,37 @@ void WebPageProxy::processNextQueuedMouseEvent() process->startResponsivenessTimer(); } @@ -16988,10 +17011,8 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 #endif - LOG_WITH_STREAM(MouseHandling, stream << "UIProcess: sent mouse event " << eventType << " (queue size " << internals().mouseEventQueue.size() << ")"); -- process->recordUserGestureAuthorizationToken(event.authorizationToken()); - sendMouseEvent(m_mainFrame->frameID(), event, WTFMove(sandboxExtensions)); + LOG_WITH_STREAM(MouseHandling, stream << "UIProcess: sent mouse event " << eventType << " (queue size " << internals().mouseEventQueue.size() << ")"); -+ process->recordUserGestureAuthorizationToken(event.authorizationToken()); + sendMouseEvent(m_mainFrame->frameID(), event, WTFMove(sandboxExtensions)); + } else { +#if PLATFORM(WIN) || PLATFORM(COCOA) @@ -17015,7 +17036,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } void WebPageProxy::doAfterProcessingAllPendingMouseEvents(WTF::Function&& action) -@@ -3543,6 +3703,8 @@ void WebPageProxy::wheelEventHandlingCompleted(bool wasHandled) +@@ -3636,6 +3796,8 @@ void WebPageProxy::wheelEventHandlingCompleted(bool wasHandled) if (RefPtr automationSession = process().processPool().automationSession()) automationSession->wheelEventsFlushedForPage(*this); @@ -17024,7 +17045,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } void WebPageProxy::cacheWheelEventScrollingAccelerationCurve(const NativeWebWheelEvent& nativeWheelEvent) -@@ -3690,7 +3852,7 @@ static TrackingType mergeTrackingTypes(TrackingType a, TrackingType b) +@@ -3782,7 +3944,7 @@ static TrackingType mergeTrackingTypes(TrackingType a, TrackingType b) void WebPageProxy::updateTouchEventTracking(const WebTouchEvent& touchStartEvent) { @@ -17033,7 +17054,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 for (auto& touchPoint : touchStartEvent.touchPoints()) { auto location = touchPoint.location(); auto update = [this, location](TrackingType& trackingType, EventTrackingRegions::EventType eventType) { -@@ -4161,6 +4323,8 @@ void WebPageProxy::receivedNavigationPolicyDecision(WebProcessProxy& processNavi +@@ -4253,6 +4415,8 @@ void WebPageProxy::receivedNavigationPolicyDecision(WebProcessProxy& processNavi if (policyAction != PolicyAction::Use || (!preferences().siteIsolationEnabled() && !frame.isMainFrame()) || !navigation) { @@ -17042,7 +17063,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 auto previousPendingNavigationID = internals().pageLoadState.pendingAPIRequest().navigationID; receivedPolicyDecision(policyAction, navigation, navigation->protectedWebsitePolicies().get(), WTFMove(navigationAction), WTFMove(sender), WillContinueLoadInNewProcess::No, std::nullopt); #if HAVE(APP_SSO) -@@ -4268,6 +4432,7 @@ void WebPageProxy::receivedNavigationPolicyDecision(WebProcessProxy& processNavi +@@ -4360,6 +4524,7 @@ void WebPageProxy::receivedNavigationPolicyDecision(WebProcessProxy& processNavi void WebPageProxy::receivedPolicyDecision(PolicyAction action, API::Navigation* navigation, RefPtr&& websitePolicies, std::variant, Ref>&& navigationActionOrResponse, Ref&& sender, WillContinueLoadInNewProcess willContinueLoadInNewProcess, std::optional sandboxExtensionHandle) { @@ -17050,7 +17071,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 if (!hasRunningProcess()) { sender->send(PolicyDecision { isNavigatingToAppBoundDomain() }); return; -@@ -5131,6 +5296,11 @@ void WebPageProxy::pageScaleFactorDidChange(double scaleFactor) +@@ -5230,6 +5395,11 @@ void WebPageProxy::pageScaleFactorDidChange(double scaleFactor) m_pageScaleFactor = scaleFactor; } @@ -17062,7 +17083,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 void WebPageProxy::pluginScaleFactorDidChange(double pluginScaleFactor) { MESSAGE_CHECK(m_process, scaleFactorIsValid(pluginScaleFactor)); -@@ -5644,6 +5814,7 @@ void WebPageProxy::didDestroyNavigationShared(Ref&& process, ui +@@ -5768,6 +5938,7 @@ void WebPageProxy::didDestroyNavigationShared(Ref&& process, ui PageClientProtector protector(pageClient()); m_navigationState->didDestroyNavigation(process->coreProcessIdentifier(), navigationID); @@ -17070,7 +17091,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } void WebPageProxy::didStartProvisionalLoadForFrame(FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, uint64_t navigationID, URL&& url, URL&& unreachableURL, const UserData& userData) -@@ -5898,6 +6069,8 @@ void WebPageProxy::didFailProvisionalLoadForFrameShared(Ref&& p +@@ -6022,6 +6193,8 @@ void WebPageProxy::didFailProvisionalLoadForFrameShared(Ref&& p m_failingProvisionalLoadURL = { }; @@ -17079,7 +17100,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 // 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; -@@ -6517,7 +6690,15 @@ void WebPageProxy::beginSafeBrowsingCheck(const URL&, bool, WebFramePolicyListen +@@ -6635,7 +6808,15 @@ void WebPageProxy::beginSafeBrowsingCheck(const URL&, bool, WebFramePolicyListen void WebPageProxy::decidePolicyForNavigationActionAsync(FrameInfoData&& frameInfo, uint64_t navigationID, NavigationActionData&& navigationActionData, FrameInfoData&& originatingFrameInfo, std::optional originatingPageID, const WebCore::ResourceRequest& originalRequest, WebCore::ResourceRequest&& request, IPC::FormDataReference&& requestBody, CompletionHandler&& completionHandler) { @@ -17096,7 +17117,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } void WebPageProxy::decidePolicyForNavigationActionAsyncShared(Ref&& process, PageIdentifier webPageID, FrameInfoData&& frameInfo, uint64_t navigationID, NavigationActionData&& navigationActionData, FrameInfoData&& originatingFrameInfo, std::optional originatingPageID, const WebCore::ResourceRequest& originalRequest, WebCore::ResourceRequest&& request, IPC::FormDataReference&& requestBody, CompletionHandler&& completionHandler) -@@ -7102,6 +7283,7 @@ void WebPageProxy::createNewPage(FrameInfoData&& originatingFrameInfoData, WebPa +@@ -7220,6 +7401,7 @@ void WebPageProxy::createNewPage(FrameInfoData&& originatingFrameInfoData, WebPa if (RefPtr page = originatingFrameInfo->page()) openerAppInitiatedState = page->lastNavigationWasAppInitiated(); @@ -17104,7 +17125,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 auto completionHandler = [this, protectedThis = Ref { *this }, mainFrameURL, request, reply = WTFMove(reply), privateClickMeasurement = navigationActionData.privateClickMeasurement, openerAppInitiatedState = WTFMove(openerAppInitiatedState), openerFrameID = originatingFrameInfoData.frameID] (RefPtr newPage) mutable { if (!newPage) { reply(std::nullopt, std::nullopt); -@@ -7150,6 +7332,7 @@ void WebPageProxy::createNewPage(FrameInfoData&& originatingFrameInfoData, WebPa +@@ -7268,6 +7450,7 @@ void WebPageProxy::createNewPage(FrameInfoData&& originatingFrameInfoData, WebPa void WebPageProxy::showPage() { m_uiClient->showPage(this); @@ -17112,7 +17133,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } bool WebPageProxy::hasOpenedPage() const -@@ -7229,6 +7412,10 @@ void WebPageProxy::closePage() +@@ -7347,6 +7530,10 @@ void WebPageProxy::closePage() if (isClosed()) return; @@ -17123,7 +17144,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 WEBPAGEPROXY_RELEASE_LOG(Process, "closePage:"); pageClient().clearAllEditCommands(); m_uiClient->close(this); -@@ -7265,6 +7452,8 @@ void WebPageProxy::runJavaScriptAlert(FrameIdentifier frameID, FrameInfoData&& f +@@ -7383,6 +7570,8 @@ void WebPageProxy::runJavaScriptAlert(FrameIdentifier frameID, FrameInfoData&& f } runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), message, [reply = WTFMove(reply)](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler&& completion) mutable { @@ -17132,7 +17153,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 page.m_uiClient->runJavaScriptAlert(page, message, frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)]() mutable { reply(); completion(); -@@ -7286,6 +7475,8 @@ void WebPageProxy::runJavaScriptConfirm(FrameIdentifier frameID, FrameInfoData&& +@@ -7404,6 +7593,8 @@ void WebPageProxy::runJavaScriptConfirm(FrameIdentifier frameID, FrameInfoData&& if (RefPtr automationSession = process().processPool().automationSession()) automationSession->willShowJavaScriptDialog(*this); } @@ -17141,7 +17162,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 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 { -@@ -7309,6 +7500,8 @@ void WebPageProxy::runJavaScriptPrompt(FrameIdentifier frameID, FrameInfoData&& +@@ -7427,6 +7618,8 @@ void WebPageProxy::runJavaScriptPrompt(FrameIdentifier frameID, FrameInfoData&& if (RefPtr automationSession = process().processPool().automationSession()) automationSession->willShowJavaScriptDialog(*this); } @@ -17150,7 +17171,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 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 { -@@ -7425,6 +7618,8 @@ void WebPageProxy::runBeforeUnloadConfirmPanel(FrameIdentifier frameID, FrameInf +@@ -7543,6 +7736,8 @@ void WebPageProxy::runBeforeUnloadConfirmPanel(FrameIdentifier frameID, FrameInf return; } } @@ -17159,7 +17180,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 // Since runBeforeUnloadConfirmPanel() can spin a nested run loop we need to turn off the responsiveness timer and the tryClose timer. protectedProcess()->stopResponsivenessTimer(); -@@ -7883,6 +8078,11 @@ void WebPageProxy::resourceLoadDidCompleteWithError(ResourceLoadInfo&& loadInfo, +@@ -8001,6 +8196,11 @@ void WebPageProxy::resourceLoadDidCompleteWithError(ResourceLoadInfo&& loadInfo, } #if ENABLE(FULLSCREEN_API) @@ -17171,7 +17192,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 WebFullScreenManagerProxy* WebPageProxy::fullScreenManager() { return m_fullScreenManager.get(); -@@ -7959,6 +8159,17 @@ void WebPageProxy::requestDOMPasteAccess(WebCore::DOMPasteAccessCategory pasteAc +@@ -8077,6 +8277,17 @@ void WebPageProxy::requestDOMPasteAccess(WebCore::DOMPasteAccessCategory pasteAc { MESSAGE_CHECK_COMPLETION(m_process, !originIdentifier.isEmpty(), completionHandler(DOMPasteAccessResponse::DeniedForGesture)); @@ -17189,7 +17210,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 m_pageClient->requestDOMPasteAccess(pasteAccessCategory, elementRect, originIdentifier, WTFMove(completionHandler)); } -@@ -8762,6 +8973,8 @@ void WebPageProxy::mouseEventHandlingCompleted(std::optional event +@@ -8880,6 +9091,8 @@ void WebPageProxy::mouseEventHandlingCompleted(std::optional event if (RefPtr automationSession = process().processPool().automationSession()) automationSession->mouseEventsFlushedForPage(*this); didFinishProcessingAllPendingMouseEvents(); @@ -17198,7 +17219,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } } -@@ -8796,6 +9009,7 @@ void WebPageProxy::keyEventHandlingCompleted(std::optional eventTy +@@ -8914,6 +9127,7 @@ void WebPageProxy::keyEventHandlingCompleted(std::optional eventTy if (!canProcessMoreKeyEvents) { if (RefPtr automationSession = process().processPool().automationSession()) automationSession->keyboardEventsFlushedForPage(*this); @@ -17206,7 +17227,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 } } -@@ -9201,7 +9415,10 @@ void WebPageProxy::dispatchProcessDidTerminate(ProcessTerminationReason reason) +@@ -9319,7 +9533,10 @@ void WebPageProxy::dispatchProcessDidTerminate(ProcessTerminationReason reason) { WEBPAGEPROXY_RELEASE_LOG_ERROR(Loading, "dispatchProcessDidTerminate: reason=%" PUBLIC_LOG_STRING, processTerminationReasonToString(reason)); @@ -17218,7 +17239,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 if (m_loaderClient) handledByClient = reason != ProcessTerminationReason::RequestedByClient && m_loaderClient->processDidCrash(*this); else -@@ -9564,6 +9781,7 @@ bool WebPageProxy::useGPUProcessForDOMRenderingEnabled() const +@@ -9684,6 +9901,7 @@ bool WebPageProxy::useGPUProcessForDOMRenderingEnabled() const WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& process, DrawingAreaProxy& drawingArea, RefPtr&& websitePolicies) { @@ -17226,7 +17247,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 WebPageCreationParameters parameters; parameters.processDisplayName = configuration().processDisplayName(); -@@ -9770,6 +9988,8 @@ WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& proc +@@ -9890,6 +10108,8 @@ WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& proc parameters.httpsUpgradeEnabled = preferences().upgradeKnownHostsToHTTPSEnabled() ? m_configuration->httpsUpgradeEnabled() : false; @@ -17235,7 +17256,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 #if PLATFORM(IOS) || PLATFORM(VISION) // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. parameters.allowsDeprecatedSynchronousXMLHttpRequestDuringUnload = allowsDeprecatedSynchronousXMLHttpRequestDuringUnload(); -@@ -9858,8 +10078,42 @@ void WebPageProxy::gamepadActivity(const Vector>& gam +@@ -9983,8 +10203,42 @@ void WebPageProxy::gamepadActivity(const Vector>& gam #endif @@ -17278,7 +17299,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 if (negotiatedLegacyTLS == NegotiatedLegacyTLS::Yes) { m_navigationClient->shouldAllowLegacyTLS(*this, authenticationChallenge.get(), [this, protectedThis = Ref { *this }, authenticationChallenge] (bool shouldAllowLegacyTLS) { if (shouldAllowLegacyTLS) -@@ -9963,6 +10217,12 @@ void WebPageProxy::requestGeolocationPermissionForFrame(GeolocationIdentifier ge +@@ -10088,6 +10342,12 @@ void WebPageProxy::requestGeolocationPermissionForFrame(GeolocationIdentifier ge request->deny(); }; @@ -17291,7 +17312,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 // 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 -@@ -10017,6 +10277,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi +@@ -10142,6 +10402,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi shouldChangeDeniedToPrompt = false; if (sessionID().isEphemeral()) { @@ -17304,7 +17325,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 completionHandler(shouldChangeDeniedToPrompt ? PermissionState::Prompt : PermissionState::Denied); return; } -@@ -10031,6 +10297,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi +@@ -10156,6 +10422,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi return; } @@ -17318,7 +17339,7 @@ index 8455defdbbcf688a5da2ba2b06ce04288c6e9849..553e7b446c1c332d5e9703db0e286f52 completionHandler(shouldChangeDeniedToPrompt ? PermissionState::Prompt : PermissionState::Denied); return; diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h -index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd41bbdc880 100644 +index 30e008435b6ad7c724c0b5d5dfb1e30050b86946..94b1cb92c93883f3713f11525352d91992fee00a 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.h +++ b/Source/WebKit/UIProcess/WebPageProxy.h @@ -26,6 +26,7 @@ @@ -17354,7 +17375,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 namespace API { class Attachment; -@@ -95,6 +114,7 @@ class DestinationColorSpace; +@@ -96,6 +115,7 @@ class DestinationColorSpace; class DragData; class FloatPoint; class FloatQuad; @@ -17362,7 +17383,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 class FloatRect; class FloatSize; class FontAttributeChanges; -@@ -385,6 +405,7 @@ class WebExtensionController; +@@ -395,6 +415,7 @@ class WebExtensionController; class WebFramePolicyListenerProxy; class WebFrameProxy; class WebFullScreenManagerProxy; @@ -17370,7 +17391,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 class WebInspectorUIProxy; class WebKeyboardEvent; class WebMouseEvent; -@@ -595,6 +616,8 @@ public: +@@ -608,6 +629,8 @@ public: void setControlledByAutomation(bool); WebPageInspectorController& inspectorController() { return *m_inspectorController; } @@ -17379,7 +17400,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 #if PLATFORM(IOS_FAMILY) void showInspectorIndication(); -@@ -628,6 +651,7 @@ public: +@@ -641,6 +664,7 @@ public: bool hasSleepDisabler() const; #if ENABLE(FULLSCREEN_API) @@ -17387,7 +17408,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 WebFullScreenManagerProxy* fullScreenManager(); API::FullscreenClient& fullscreenClient() const { return *m_fullscreenClient; } -@@ -716,6 +740,11 @@ public: +@@ -729,6 +753,11 @@ public: void setPageLoadStateObserver(std::unique_ptr&&); @@ -17399,7 +17420,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 void initializeWebPage(); void setDrawingArea(std::unique_ptr&&); -@@ -739,6 +768,7 @@ public: +@@ -752,6 +781,7 @@ public: void addPlatformLoadParameters(WebProcessProxy&, LoadParameters&); RefPtr loadRequest(WebCore::ResourceRequest&&); RefPtr loadRequest(WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, API::Object* userData = nullptr); @@ -17407,7 +17428,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 RefPtr loadFile(const String& fileURL, const String& resourceDirectoryURL, bool isAppInitiated = true, API::Object* userData = nullptr); RefPtr loadData(const IPC::DataReference&, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData = nullptr); RefPtr loadData(const IPC::DataReference&, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, WebCore::ShouldOpenExternalURLsPolicy); -@@ -802,6 +832,7 @@ public: +@@ -815,6 +845,7 @@ public: void restoreSelectionInFocusedEditableElement(); PageClient& pageClient() const; @@ -17415,7 +17436,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 void setViewNeedsDisplay(const WebCore::Region&); void requestScroll(const WebCore::FloatPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin, WebCore::ScrollIsAnimated); -@@ -1309,6 +1340,7 @@ public: +@@ -1326,6 +1357,7 @@ public: #endif void pageScaleFactorDidChange(double); @@ -17423,7 +17444,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 void pluginScaleFactorDidChange(double); void pluginZoomFactorDidChange(double); -@@ -1398,14 +1430,20 @@ public: +@@ -1415,14 +1447,20 @@ public: void didStartDrag(); void dragCancelled(); void setDragCaretRect(const WebCore::IntRect&); @@ -17445,7 +17466,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 #endif void processDidBecomeUnresponsive(); -@@ -1625,6 +1663,7 @@ public: +@@ -1642,6 +1680,7 @@ public: void setViewportSizeForCSSViewportUnits(const WebCore::FloatSize&); WebCore::FloatSize viewportSizeForCSSViewportUnits() const; @@ -17453,7 +17474,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 void didReceiveAuthenticationChallengeProxy(Ref&&, NegotiatedLegacyTLS); void negotiatedLegacyTLS(); void didNegotiateModernTLS(const URL&); -@@ -1659,6 +1698,8 @@ public: +@@ -1676,6 +1715,8 @@ public: #if PLATFORM(COCOA) || PLATFORM(GTK) RefPtr takeViewSnapshot(std::optional&&); @@ -17461,8 +17482,8 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 + RefPtr takeViewSnapshot(std::optional&&) { return nullptr; } #endif - #if ENABLE(WEB_CRYPTO) -@@ -2484,6 +2525,7 @@ private: + void wrapCryptoKey(const Vector&, CompletionHandler&&)>&&); +@@ -2502,6 +2543,7 @@ private: RefPtr launchProcessForReload(); void requestNotificationPermission(const String& originString, CompletionHandler&&); @@ -17470,7 +17491,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 void didChangeContentSize(const WebCore::IntSize&); void didChangeIntrinsicContentSize(const WebCore::IntSize&); -@@ -2953,8 +2995,10 @@ private: +@@ -2994,8 +3036,10 @@ private: String m_overrideContentSecurityPolicy; RefPtr m_inspector; @@ -17481,7 +17502,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 std::unique_ptr m_fullScreenManager; std::unique_ptr m_fullscreenClient; #endif -@@ -3138,6 +3182,22 @@ private: +@@ -3179,6 +3223,22 @@ private: std::optional m_currentDragOperation; bool m_currentDragIsOverFileInput { false }; unsigned m_currentDragNumberOfFilesToBeAccepted { 0 }; @@ -17504,7 +17525,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 #endif bool m_mainFrameHasHorizontalScrollbar { false }; -@@ -3307,6 +3367,10 @@ private: +@@ -3346,6 +3406,10 @@ private: RefPtr messageBody; }; Vector m_pendingInjectedBundleMessages; @@ -17516,7 +17537,7 @@ index 81e3d314c82e29d9c6f157ed1764e6b26b34f1e9..303b0c6a886e56f70d7e155a1758ebd4 #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 e6184097085ccafc1aa7d012314c84eee090300a..7be7efca7dd8cf4092d5be39c9ee59856275b998 100644 +index 7ca0ece202c6da22a9e2464ba51cab87b4a999fe..727963ffceab44db19ae18bf2ed3a07b4f5e9588 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.messages.in +++ b/Source/WebKit/UIProcess/WebPageProxy.messages.in @@ -29,6 +29,7 @@ messages -> WebPageProxy { @@ -17535,7 +17556,7 @@ index e6184097085ccafc1aa7d012314c84eee090300a..7be7efca7dd8cf4092d5be39c9ee5985 PluginScaleFactorDidChange(double zoomFactor) PluginZoomFactorDidChange(double zoomFactor) -@@ -307,10 +309,14 @@ messages -> WebPageProxy { +@@ -305,10 +307,14 @@ messages -> WebPageProxy { StartDrag(struct WebCore::DragItem dragItem, WebKit::ShareableBitmap::Handle dragImage) SetPromisedDataForImage(String pasteboardName, WebKit::SharedMemory::Handle imageHandle, String filename, String extension, String title, String url, String visibleURL, WebKit::SharedMemory::Handle archiveHandle, String originIdentifier) #endif @@ -17567,10 +17588,10 @@ index 1329e19aa1f93077c7d2f5fe98838731b79692a5..8e697f27ef47f0b66f1de6197ab1ce25 } diff --git a/Source/WebKit/UIProcess/WebProcessPool.cpp b/Source/WebKit/UIProcess/WebProcessPool.cpp -index 9fa1ce31e9dfd76e51588fa287989a43bfb1075a..50dcd16e303bebeb0f6f1a31e2c4df544827b2c4 100644 +index 0a88c7b396a570915dd6cb6be8f7651d295efeb7..833c66a9a24c729b92b6ca08718fe69066838c03 100644 --- a/Source/WebKit/UIProcess/WebProcessPool.cpp +++ b/Source/WebKit/UIProcess/WebProcessPool.cpp -@@ -376,10 +376,10 @@ void WebProcessPool::setAutomationClient(std::unique_ptr& +@@ -405,10 +405,10 @@ void WebProcessPool::setAutomationClient(std::unique_ptr& void WebProcessPool::setOverrideLanguages(Vector&& languages) { @@ -17583,7 +17604,7 @@ index 9fa1ce31e9dfd76e51588fa287989a43bfb1075a..50dcd16e303bebeb0f6f1a31e2c4df54 #if ENABLE(GPU_PROCESS) if (RefPtr gpuProcess = GPUProcessProxy::singletonIfCreated()) -@@ -387,9 +387,10 @@ void WebProcessPool::setOverrideLanguages(Vector&& languages) +@@ -416,9 +416,10 @@ void WebProcessPool::setOverrideLanguages(Vector&& languages) #endif #if USE(SOUP) for (Ref networkProcess : NetworkProcessProxy::allNetworkProcesses()) @@ -17595,7 +17616,7 @@ index 9fa1ce31e9dfd76e51588fa287989a43bfb1075a..50dcd16e303bebeb0f6f1a31e2c4df54 void WebProcessPool::fullKeyboardAccessModeChanged(bool fullKeyboardAccessEnabled) { -@@ -829,7 +830,7 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa +@@ -870,7 +871,7 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa #endif parameters.cacheModel = LegacyGlobalSettings::singleton().cacheModel(); @@ -17605,7 +17626,7 @@ index 9fa1ce31e9dfd76e51588fa287989a43bfb1075a..50dcd16e303bebeb0f6f1a31e2c4df54 parameters.urlSchemesRegisteredAsEmptyDocument = copyToVector(m_schemesToRegisterAsEmptyDocument); diff --git a/Source/WebKit/UIProcess/WebProcessProxy.cpp b/Source/WebKit/UIProcess/WebProcessProxy.cpp -index 8e19e14a7dec67b23ed4570523f4e8f53e2cf57a..ca634c57e08ffc346abd22852d9923c4165f0e9f 100644 +index 24825bc329091147eb6baf50440451ae4c2f3901..5e8bc90a6167d6c932932a93f4a46831d0c036d5 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.cpp +++ b/Source/WebKit/UIProcess/WebProcessProxy.cpp @@ -173,6 +173,11 @@ Vector> WebProcessProxy::allProcesses() @@ -17620,7 +17641,7 @@ index 8e19e14a7dec67b23ed4570523f4e8f53e2cf57a..ca634c57e08ffc346abd22852d9923c4 RefPtr WebProcessProxy::processForIdentifier(ProcessIdentifier identifier) { return allProcessMap().get(identifier); -@@ -513,6 +518,26 @@ void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOpt +@@ -510,6 +515,26 @@ void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOpt if (WebKit::isInspectorProcessPool(processPool())) launchOptions.extraInitializationData.add("inspector-process"_s, "1"_s); @@ -17648,10 +17669,10 @@ index 8e19e14a7dec67b23ed4570523f4e8f53e2cf57a..ca634c57e08ffc346abd22852d9923c4 if (isPrewarmed()) diff --git a/Source/WebKit/UIProcess/WebProcessProxy.h b/Source/WebKit/UIProcess/WebProcessProxy.h -index 17944648b6baf0adec20f6756e65f192fa82cd1d..66aee2da889d85cba8462c9a7a6f36ff32346713 100644 +index 1fced6aa6c273d14c30ee69266b2c2266126d9ac..67e7dedac10f09d4ec1b894ee3c1f21935e9622b 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.h +++ b/Source/WebKit/UIProcess/WebProcessProxy.h -@@ -161,6 +161,7 @@ public: +@@ -160,6 +160,7 @@ public: static void forWebPagesWithOrigin(PAL::SessionID, const WebCore::SecurityOriginData&, const Function&); static Vector> allowedFirstPartiesForCookies(); @@ -17660,10 +17681,10 @@ index 17944648b6baf0adec20f6756e65f192fa82cd1d..66aee2da889d85cba8462c9a7a6f36ff WebConnection* webConnection() const { return m_webConnection.get(); } RefPtr protectedWebConnection() const { return m_webConnection; } diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp -index 6bed35d7964b9ccb1d510cab1203fa6b99db45ea..fde4f063f52bcae25b0788f33c64d02e951b407b 100644 +index b1af14650f6b30e84fc03dbd6e7af88050a414d2..90d08ccccff6447c84a6f8cd5745b58aeef122b5 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp -@@ -298,7 +298,8 @@ SOAuthorizationCoordinator& WebsiteDataStore::soAuthorizationCoordinator(const W +@@ -299,7 +299,8 @@ SOAuthorizationCoordinator& WebsiteDataStore::soAuthorizationCoordinator(const W static Ref networkProcessForSession(PAL::SessionID sessionID) { @@ -17673,7 +17694,7 @@ index 6bed35d7964b9ccb1d510cab1203fa6b99db45ea..fde4f063f52bcae25b0788f33c64d02e if (sessionID.isEphemeral()) { // Reuse a previous persistent session network process for ephemeral sessions. for (auto& dataStore : allDataStores().values()) { -@@ -2147,6 +2148,12 @@ void WebsiteDataStore::originDirectoryForTesting(WebCore::ClientOrigin&& origin, +@@ -2189,6 +2190,12 @@ void WebsiteDataStore::originDirectoryForTesting(WebCore::ClientOrigin&& origin, protectedNetworkProcess()->websiteDataOriginDirectoryForTesting(m_sessionID, WTFMove(origin), type, WTFMove(completionHandler)); } @@ -17687,7 +17708,7 @@ index 6bed35d7964b9ccb1d510cab1203fa6b99db45ea..fde4f063f52bcae25b0788f33c64d02e void WebsiteDataStore::hasAppBoundSession(CompletionHandler&& completionHandler) const { diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h -index 5f316ed41917644063eb06e1ee1dcf7985580723..8305c0187811d936390d30d6481bc901e51794fc 100644 +index efcd46c1f5ef6f0f841d9d2f1fda65dc42aaae2f..77bec62446c84fb98d3c17e615ed9bc75b174739 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h @@ -97,6 +97,7 @@ class DeviceIdHashSaltStorage; @@ -17717,10 +17738,10 @@ index 5f316ed41917644063eb06e1ee1dcf7985580723..8305c0187811d936390d30d6481bc901 + virtual ~DownloadInstrumentation() = default; +}; + - class WebsiteDataStore : public API::ObjectImpl, public Identified, public CanMakeWeakPtr, public CanMakeCheckedPtr { + class WebsiteDataStore : public API::ObjectImpl, public Identified, public CanMakeWeakPtr { public: static Ref defaultDataStore(); -@@ -314,11 +324,13 @@ public: +@@ -318,11 +328,13 @@ public: const WebCore::CurlProxySettings& networkProxySettings() const { return m_proxySettings; } #endif @@ -17735,7 +17756,7 @@ index 5f316ed41917644063eb06e1ee1dcf7985580723..8305c0187811d936390d30d6481bc901 void setNetworkProxySettings(WebCore::SoupNetworkProxySettings&&); const WebCore::SoupNetworkProxySettings& networkProxySettings() const { return m_networkProxySettings; } void setCookiePersistentStorage(const String&, SoupCookiePersistentStorageType); -@@ -403,6 +415,12 @@ public: +@@ -407,6 +419,12 @@ public: static const String& defaultBaseDataDirectory(); #endif @@ -17772,36 +17793,18 @@ index 5f316ed41917644063eb06e1ee1dcf7985580723..8305c0187811d936390d30d6481bc901 #if HAVE(APP_SSO) std::unique_ptr m_soAuthorizationCoordinator; #endif -diff --git a/Source/WebKit/UIProcess/cairo/BackingStoreCairo.cpp b/Source/WebKit/UIProcess/cairo/BackingStoreCairo.cpp -index cd172b3766d51a070a3eb87532b496f63b2474e1..e6a268bd1c433a2976c7337253c408217993ae77 100644 ---- a/Source/WebKit/UIProcess/cairo/BackingStoreCairo.cpp -+++ b/Source/WebKit/UIProcess/cairo/BackingStoreCairo.cpp -@@ -27,9 +27,11 @@ - #include "config.h" - #include "BackingStore.h" +diff --git a/Source/WebKit/UIProcess/cairo/BackingStore.h b/Source/WebKit/UIProcess/cairo/BackingStore.h +index 419a9a80b9c8456e05fe7b9b72ef6e3188eb38e7..9980d442dc52ce86b48839a223afc0fe6ca31f6a 100644 +--- a/Source/WebKit/UIProcess/cairo/BackingStore.h ++++ b/Source/WebKit/UIProcess/cairo/BackingStore.h +@@ -51,6 +51,7 @@ public: + float deviceScaleFactor() const { return m_deviceScaleFactor; } -+#include "DrawingAreaProxyCoordinatedGraphics.h" - #include "ShareableBitmap.h" - #include "UpdateInfo.h" - #include "WebPageProxy.h" -+#include "WebPageInspectorController.h" - #include - #include - #include -@@ -61,6 +63,13 @@ std::unique_ptr BackingStore::createBackend() - return makeUnique(m_size, m_deviceScaleFactor); - } + void paint(cairo_t*, const WebCore::IntRect&); ++ RefPtr surface() const { return m_surface; } + void incorporateUpdate(UpdateInfo&&); -+cairo_surface_t* BackingStore::surface() const { -+ if (!m_backend) -+ return nullptr; -+ -+ return m_backend->surface(); -+} -+ - void BackingStore::paint(cairo_t* context, const IntRect& rect) - { - ASSERT(m_backend); + private: diff --git a/Source/WebKit/UIProcess/geoclue/GeoclueGeolocationProvider.cpp b/Source/WebKit/UIProcess/geoclue/GeoclueGeolocationProvider.cpp index 692a45a48fa027f9221338d74f5351bef4baf00f..db8c761c71cc7009be66255c2668de4eff36dee0 100644 --- a/Source/WebKit/UIProcess/geoclue/GeoclueGeolocationProvider.cpp @@ -18130,10 +18133,10 @@ index 71fb4cbd4338bcbda3a61019cbf4a914bf74953e..6f56e4afb345c23b4d8b91ee77f1991c 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 1d19978e9d898171ba74bdb526f7db67840e25d1..c2c4118782744c01c67153d46284fcae70dd0291 100644 +index c5f4a50a780e0394b13362da3d1ce61446f34261..21f3e759077697edc134e729cc096126ce281e05 100644 --- a/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.cpp +++ b/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreDMABuf.cpp -@@ -413,6 +413,25 @@ void AcceleratedBackingStoreDMABuf::RendererCairo::paint(GtkWidget* widget, cair +@@ -417,6 +417,25 @@ void AcceleratedBackingStoreDMABuf::RendererCairo::paint(GtkWidget* widget, cair } #endif @@ -18159,7 +18162,7 @@ index 1d19978e9d898171ba74bdb526f7db67840e25d1..c2c4118782744c01c67153d46284fcae void AcceleratedBackingStoreDMABuf::didCreateBuffer(uint64_t id, const WebCore::IntSize& size, uint32_t format, Vector&& fds, Vector&& offsets, Vector&& strides, uint64_t modifier) { #if USE(GBM) -@@ -576,6 +595,15 @@ bool AcceleratedBackingStoreDMABuf::paint(cairo_t* cr, const WebCore::IntRect& c +@@ -580,6 +599,15 @@ bool AcceleratedBackingStoreDMABuf::paint(cairo_t* cr, const WebCore::IntRect& c } #endif @@ -18214,18 +18217,6 @@ index b73a9be34afe38a37e06eca0c8bfd78dfab88e99..b3bf1185fddaa7f61da0845a05869301 }; GRefPtr m_gdkGLContext; -diff --git a/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreX11.h b/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreX11.h -index 054e80bd900cf16d69801e8102ca989ff0563e1d..8245d7ed58008dbb6152e55e619e4331d30ae674 100644 ---- a/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreX11.h -+++ b/Source/WebKit/UIProcess/gtk/AcceleratedBackingStoreX11.h -@@ -52,6 +52,7 @@ private: - #else - bool paint(cairo_t*, const WebCore::IntRect&) override; - #endif -+ cairo_surface_t* surface() override { return m_surface.get(); } - - RefPtr m_surface; - WebCore::XUniqueDamage m_damage; diff --git a/Source/WebKit/UIProcess/gtk/InspectorTargetProxyGtk.cpp b/Source/WebKit/UIProcess/gtk/InspectorTargetProxyGtk.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5f811ced4eafef530d101c4e397fe2780ac3071 @@ -18493,7 +18484,7 @@ index d18b3e777203ef5d0f33884f909bc598d3526831..aef80b47359d7a2e4805a006dc59cd60 m_primarySelectionOwner = frame; } diff --git a/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm b/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm -index 347b40907e576ab0ba78d65bb91f12de70437a95..58307d07baca9d00c2318074e2a8610d0f09e1c6 100644 +index 3660229a275bc80c03756d40c90418e07ce03ab9..d058147cf255f6e4f6c61338c844db335aa3ea06 100644 --- a/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm +++ b/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm @@ -472,6 +472,8 @@ IntRect PageClientImpl::rootViewToAccessibilityScreen(const IntRect& rect) @@ -18715,7 +18706,7 @@ index 0000000000000000000000000000000000000000..721826c8c98fc85b68a4f45deaee69c1 + +#endif diff --git a/Source/WebKit/UIProcess/mac/PageClientImplMac.h b/Source/WebKit/UIProcess/mac/PageClientImplMac.h -index fc5dd0e2a5f27081287e7e438f3a2104c693ac6b..00748ae080a91519c24804b7857fcdf33961a346 100644 +index b28e9e2b71156126cd21923d5999f7850c3bbdbb..5446d10a32530f9bdf8ed6c92484d262e5b25e46 100644 --- a/Source/WebKit/UIProcess/mac/PageClientImplMac.h +++ b/Source/WebKit/UIProcess/mac/PageClientImplMac.h @@ -54,6 +54,8 @@ class PageClientImpl final : public PageClientImplCocoa @@ -18749,7 +18740,7 @@ index fc5dd0e2a5f27081287e7e438f3a2104c693ac6b..00748ae080a91519c24804b7857fcdf3 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 8ec513d0904d5c3a34971254f3da4e6773cda603..ccac6f3f492ea5ea0d96cffde215264691d2aade 100644 +index 7819beb81efd82c8f244e2bc235308d6f29f0740..6030d7c46409ed22689c694376a8d2d6e92aadde 100644 --- a/Source/WebKit/UIProcess/mac/PageClientImplMac.mm +++ b/Source/WebKit/UIProcess/mac/PageClientImplMac.mm @@ -81,6 +81,7 @@ @@ -18882,10 +18873,10 @@ index 6ab7aacaebfda818e3010bb06db72c8552ac598a..3e19cba50d73084392f62f176ad4c315 bool showAfterPostProcessingContextData(); diff --git a/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm b/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm -index 57b1c6f4ecdbae5d8ffdcccfd60e1c083387c00d..f4961b46b3c348a62f31ca13c40be04aae76e02d 100644 +index 87b6a065e9fef93a55a555358186f8721a62ab34..1543521395c60c5742852c00389973cbb64c57e4 100644 --- a/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm +++ b/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm -@@ -476,6 +476,12 @@ void WebContextMenuProxyMac::getShareMenuItem(CompletionHandler + #include + + #if ENABLE(WPE_PLATFORM) diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj -index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011e4093169 100644 +index 89aabe949607d30c4a3679a66ca6dfbcf80252b0..55c3413a7dbca4de2f18fef8d9b0b5a18899c68a 100644 --- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj +++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj -@@ -1439,6 +1439,7 @@ +@@ -1476,6 +1476,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 */; }; @@ -19904,7 +19907,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 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 */; }; -@@ -2217,6 +2218,18 @@ +@@ -2270,6 +2271,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 */; }; @@ -19923,7 +19926,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 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, ); }; }; -@@ -2293,6 +2306,8 @@ +@@ -2352,6 +2365,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 */; }; @@ -19932,7 +19935,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 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 */; }; -@@ -2311,6 +2326,9 @@ +@@ -2371,6 +2386,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, ); }; }; @@ -19942,7 +19945,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 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 */; }; -@@ -5769,6 +5787,7 @@ +@@ -5907,6 +5925,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 = ""; }; @@ -19950,7 +19953,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 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 = ""; }; -@@ -7397,6 +7416,19 @@ +@@ -7565,6 +7584,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 = ""; }; @@ -19970,7 +19973,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 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 = ""; }; -@@ -7536,6 +7568,8 @@ +@@ -7706,6 +7738,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 = ""; }; @@ -19979,7 +19982,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 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 = ""; }; -@@ -7558,6 +7592,14 @@ +@@ -7729,6 +7763,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 = ""; }; @@ -19994,7 +19997,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 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 = ""; }; -@@ -7806,6 +7848,7 @@ +@@ -7995,6 +8037,7 @@ 3766F9EE189A1241003CF19B /* JavaScriptCore.framework in Frameworks */, 3766F9F1189A1254003CF19B /* libicucore.dylib in Frameworks */, 7B9FC5BB28A5233B007570E7 /* libWebKitPlatform.a in Frameworks */, @@ -20002,7 +20005,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 3766F9EF189A1244003CF19B /* QuartzCore.framework in Frameworks */, 37694525184FC6B600CDE21F /* Security.framework in Frameworks */, 37BEC4DD1948FC6A008B4286 /* WebCore.framework in Frameworks */, -@@ -10442,6 +10485,7 @@ +@@ -10674,6 +10717,7 @@ 99788ACA1F421DCA00C08000 /* _WKAutomationSessionConfiguration.mm */, 990D28A81C6404B000986977 /* _WKAutomationSessionDelegate.h */, 990D28AF1C65203900986977 /* _WKAutomationSessionInternal.h */, @@ -20010,7 +20013,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 5C4609E222430E4C009943C2 /* _WKContentRuleListAction.h */, 5C4609E322430E4D009943C2 /* _WKContentRuleListAction.mm */, 5C4609E422430E4D009943C2 /* _WKContentRuleListActionInternal.h */, -@@ -11663,6 +11707,7 @@ +@@ -11927,6 +11971,7 @@ E34B110C27C46BC6006D2F2E /* libWebCoreTestShim.dylib */, E34B110F27C46D09006D2F2E /* libWebCoreTestSupport.dylib */, DDE992F4278D06D900F60D26 /* libWebKitAdditions.a */, @@ -20018,7 +20021,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 57A9FF15252C6AEF006A2040 /* libWTF.a */, 5750F32A2032D4E500389347 /* LocalAuthentication.framework */, 570DAAB0230273D200E8FC04 /* NearField.framework */, -@@ -12230,6 +12275,12 @@ +@@ -12497,6 +12542,12 @@ children = ( 9197940423DBC4BB00257892 /* InspectorBrowserAgent.cpp */, 9197940323DBC4BB00257892 /* InspectorBrowserAgent.h */, @@ -20031,7 +20034,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 ); path = Agents; sourceTree = ""; -@@ -12238,6 +12289,7 @@ +@@ -12505,6 +12556,7 @@ isa = PBXGroup; children = ( A5D3504D1D78F0D2005124A9 /* RemoteWebInspectorUIProxyMac.mm */, @@ -20039,7 +20042,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 1CA8B935127C774E00576C2B /* WebInspectorUIProxyMac.mm */, 99A7ACE326012919006D57FD /* WKInspectorResourceURLSchemeHandler.h */, 99A7ACE42601291A006D57FD /* WKInspectorResourceURLSchemeHandler.mm */, -@@ -12867,6 +12919,7 @@ +@@ -13154,6 +13206,7 @@ E1513C65166EABB200149FCB /* AuxiliaryProcessProxy.h */, 46A2B6061E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.cpp */, 46A2B6071E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.h */, @@ -20047,7 +20050,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 5C6D69352AC3935D0099BDAF /* BrowsingContextGroup.cpp */, 5C6D69362AC3935D0099BDAF /* BrowsingContextGroup.h */, 07297F9C1C1711EA003F0735 /* DeviceIdHashSaltStorage.cpp */, -@@ -12886,6 +12939,8 @@ +@@ -13173,6 +13226,8 @@ BC06F43912DBCCFB002D78DE /* GeolocationPermissionRequestProxy.cpp */, BC06F43812DBCCFB002D78DE /* GeolocationPermissionRequestProxy.h */, 2DD5A72A1EBF09A7009BA597 /* HiddenPageThrottlingAutoIncreasesCounter.h */, @@ -20056,7 +20059,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 5CEABA2B2333251400797797 /* LegacyGlobalSettings.cpp */, 5CEABA2A2333247700797797 /* LegacyGlobalSettings.h */, 31607F3819627002009B87DA /* LegacySessionStateCoding.h */, -@@ -12920,6 +12975,7 @@ +@@ -13207,6 +13262,7 @@ 1A0C227D2451130A00ED614D /* QuickLookThumbnailingSoftLink.mm */, 1AEE57232409F142002005D6 /* QuickLookThumbnailLoader.h */, 1AEE57242409F142002005D6 /* QuickLookThumbnailLoader.mm */, @@ -20064,7 +20067,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 5CCB54DC2A4FEA6A0005FAA8 /* RemotePageDrawingAreaProxy.cpp */, 5CCB54DB2A4FEA6A0005FAA8 /* RemotePageDrawingAreaProxy.h */, 5C907E9A294D507100B3402D /* RemotePageProxy.cpp */, -@@ -13025,6 +13081,8 @@ +@@ -13313,6 +13369,8 @@ BC7B6204129A0A6700D174A4 /* WebPageGroup.h */, 2D9EA3101A96D9EB002D2807 /* WebPageInjectedBundleClient.cpp */, 2D9EA30E1A96CBFF002D2807 /* WebPageInjectedBundleClient.h */, @@ -20073,7 +20076,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 BC111B0B112F5E4F00337BAB /* WebPageProxy.cpp */, BC032DCB10F4389F0058C15A /* WebPageProxy.h */, BCBD38FA125BAB9A00D2C29F /* WebPageProxy.messages.in */, -@@ -13187,6 +13245,7 @@ +@@ -13475,6 +13533,7 @@ BC646C1911DD399F006455B0 /* WKBackForwardListItemRef.h */, BC646C1611DD399F006455B0 /* WKBackForwardListRef.cpp */, BC646C1711DD399F006455B0 /* WKBackForwardListRef.h */, @@ -20081,7 +20084,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 BCB9E24A1120E15C00A137E0 /* WKContext.cpp */, BCB9E2491120E15C00A137E0 /* WKContext.h */, 1AE52F9319201F6B00A1FA37 /* WKContextConfigurationRef.cpp */, -@@ -13763,6 +13822,9 @@ +@@ -14051,6 +14110,9 @@ 7AFA6F682A9F57C50055322A /* DisplayLinkMac.cpp */, 31ABA79C215AF9E000C90E31 /* HighPerformanceGPUManager.h */, 31ABA79D215AF9E000C90E31 /* HighPerformanceGPUManager.mm */, @@ -20091,7 +20094,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 1AFDE65B1954E8D500C48FFA /* LegacySessionStateCoding.cpp */, 0FCB4E5818BBE3D9000FCFC9 /* PageClientImplMac.h */, 0FCB4E5918BBE3D9000FCFC9 /* PageClientImplMac.mm */, -@@ -13786,6 +13848,8 @@ +@@ -14074,6 +14136,8 @@ E568B92120A3AC6A00E3C856 /* WebDataListSuggestionsDropdownMac.mm */, E55CD20124D09F1F0042DB9C /* WebDateTimePickerMac.h */, E55CD20224D09F1F0042DB9C /* WebDateTimePickerMac.mm */, @@ -20100,7 +20103,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 BC857E8512B71EBB00EDEB2E /* WebPageProxyMac.mm */, BC5750951268F3C6006F0F12 /* WebPopupMenuProxyMac.h */, BC5750961268F3C6006F0F12 /* WebPopupMenuProxyMac.mm */, -@@ -14741,6 +14805,7 @@ +@@ -15047,6 +15111,7 @@ 99788ACB1F421DDA00C08000 /* _WKAutomationSessionConfiguration.h in Headers */, 990D28AC1C6420CF00986977 /* _WKAutomationSessionDelegate.h in Headers */, 990D28B11C65208D00986977 /* _WKAutomationSessionInternal.h in Headers */, @@ -20108,15 +20111,15 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 5C4609E7224317B4009943C2 /* _WKContentRuleListAction.h in Headers */, 5C4609E8224317BB009943C2 /* _WKContentRuleListActionInternal.h in Headers */, 1A5704F81BE01FF400874AF1 /* _WKContextMenuElementInfo.h in Headers */, -@@ -15027,6 +15092,7 @@ +@@ -15344,6 +15409,7 @@ E170876C16D6CA6900F99226 /* BlobRegistryProxy.h in Headers */, 4F601432155C5AA2001FBDE0 /* BlockingResponseMap.h in Headers */, 1A5705111BE410E600874AF1 /* BlockSPI.h in Headers */, + D71A944C237239FB002C4D9E /* BrowserInspectorPipe.h in Headers */, - 463A074B2AFD8C7200CA8DBE /* BufferAndBackendInfo.h in Headers */, + A7E69BCC2B2117A100D43D3F /* BufferAndBackendInfo.h in Headers */, BC3065FA1259344E00E71278 /* CacheModel.h in Headers */, 935BF7FC2936BF1A00B41326 /* CacheStorageCache.h in Headers */, -@@ -15182,7 +15248,11 @@ +@@ -15510,7 +15576,11 @@ BC14DF77120B5B7900826C0C /* InjectedBundleScriptWorld.h in Headers */, CE550E152283752200D28791 /* InsertTextOptions.h in Headers */, 9197940523DBC4BB00257892 /* InspectorBrowserAgent.h in Headers */, @@ -20128,15 +20131,15 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 A5E391FD2183C1F800C8FB31 /* InspectorTargetProxy.h in Headers */, C5BCE5DF1C50766A00CDE3FA /* InteractionInformationAtPosition.h in Headers */, 2D4D2C811DF60BF3002EB10C /* InteractionInformationRequest.h in Headers */, -@@ -15420,6 +15490,7 @@ - CDAC20CA23FC2F750021DEE3 /* RemoteCDMInstanceSession.h in Headers */, +@@ -15754,6 +15824,7 @@ CDAC20C923FC2F750021DEE3 /* RemoteCDMInstanceSessionIdentifier.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 */, -@@ -15472,6 +15543,7 @@ +@@ -15808,6 +15879,7 @@ E1E552C516AE065F004ED653 /* SandboxInitializationParameters.h in Headers */, E36FF00327F36FBD004BE21A /* SandboxStateVariables.h in Headers */, 7BAB111025DD02B3008FC479 /* ScopedActiveMessageReceiveQueue.h in Headers */, @@ -20144,7 +20147,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 E4D54D0421F1D72D007E3C36 /* ScrollingTreeFrameScrollingNodeRemoteIOS.h in Headers */, 0F931C1C18C5711900DBA7C3 /* ScrollingTreeOverflowScrollingNodeIOS.h in Headers */, 0F931C1C18C5711900DBB8D4 /* ScrollingTreeScrollingNodeDelegateIOS.h in Headers */, -@@ -15793,6 +15865,8 @@ +@@ -16139,6 +16211,8 @@ 939EF87029D112EE00F23AEE /* WebPageInlines.h in Headers */, 9197940823DBC4CB00257892 /* WebPageInspectorAgentBase.h in Headers */, A513F5402154A5D700662841 /* WebPageInspectorController.h in Headers */, @@ -20153,7 +20156,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 A543E30C215C8A8D00279CD9 /* WebPageInspectorTarget.h in Headers */, A543E30D215C8A9000279CD9 /* WebPageInspectorTargetController.h in Headers */, A543E307215AD13700279CD9 /* WebPageInspectorTargetFrontendChannel.h in Headers */, -@@ -17975,6 +18049,8 @@ +@@ -18338,6 +18412,8 @@ 522F792928D50EBB0069B45B /* HidService.mm in Sources */, 2749F6442146561B008380BF /* InjectedBundleNodeHandle.cpp in Sources */, 2749F6452146561E008380BF /* InjectedBundleRangeHandle.cpp in Sources */, @@ -20162,7 +20165,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 1CC94E532AC92F190045F269 /* JSWebExtensionAPIAction.mm in Sources */, 1C2B4D4B2A819D0D00C528A1 /* JSWebExtensionAPIAlarms.mm in Sources */, 1C8ECFEA2AFC7DCB007BAA62 /* JSWebExtensionAPICommands.mm in Sources */, -@@ -18378,6 +18454,8 @@ +@@ -18752,6 +18828,8 @@ E3816B3D27E2463A005EAFC0 /* WebMockContentFilterManager.cpp in Sources */, 31BA924D148831260062EDB5 /* WebNotificationManagerMessageReceiver.cpp in Sources */, 2DF6FE52212E110900469030 /* WebPage.cpp in Sources */, @@ -20172,7 +20175,7 @@ index 98b6f9373612c42f72a78bf9e116b784ee75040e..51774e0340fa3453a29fd28d09b02011 BCBD3914125BB1A800D2C29F /* WebPageProxyMessageReceiver.cpp in Sources */, 7CE9CE101FA0767A000177DE /* WebPageUpdatePreferences.cpp in Sources */, diff --git a/Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp b/Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp -index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59ebde3103 100644 +index 8525d9e78cf2ba54d27f469cc028f10d61215ab2..c15a29c5e543e43059a461eebbd1c21309d32fee 100644 --- a/Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp +++ b/Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp @@ -237,6 +237,11 @@ void WebLoaderStrategy::scheduleLoad(ResourceLoader& resourceLoader, CachedResou @@ -20187,7 +20190,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 #if ENABLE(PDFJS) if (tryLoadingUsingPDFJSHandler(resourceLoader, trackingParameters)) return; -@@ -366,7 +371,8 @@ static void addParametersShared(const LocalFrame* frame, NetworkResourceLoadPara +@@ -363,7 +368,8 @@ static void addParametersShared(const LocalFrame* frame, NetworkResourceLoadPara parameters.linkPreconnectEarlyHintsEnabled = mainFrame->settings().linkPreconnectEarlyHintsEnabled(); } @@ -20197,7 +20200,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 { auto identifier = resourceLoader.identifier(); ASSERT(identifier); -@@ -382,7 +388,7 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL +@@ -379,7 +385,7 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL RunLoop::main().dispatch([resourceLoader = Ref { resourceLoader }, error = blockedError(request)] { resourceLoader->didFail(error); }); @@ -20206,7 +20209,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 } } -@@ -392,7 +398,6 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL +@@ -389,7 +395,6 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, url '%s' will be scheduled with the NetworkProcess with priority %d, storedCredentialsPolicy %i", resourceLoader.url().string().latin1().data(), static_cast(resourceLoader.request().priority()), (int)storedCredentialsPolicy); @@ -20214,7 +20217,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 loadParameters.identifier = identifier; loadParameters.webPageProxyID = trackingParameters.webPageProxyID; loadParameters.webPageID = trackingParameters.pageID; -@@ -484,14 +489,11 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL +@@ -479,14 +484,11 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL if (loadParameters.options.mode != FetchOptions::Mode::Navigate) { ASSERT(loadParameters.sourceOrigin); @@ -20232,7 +20235,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 loadParameters.isMainFrameNavigation = isMainFrameNavigation; if (loadParameters.isMainFrameNavigation && document) -@@ -527,12 +529,24 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL +@@ -522,12 +524,24 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL } ASSERT((loadParameters.webPageID && loadParameters.webFrameID) || loadParameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials); @@ -20257,7 +20260,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 if (frame && !frame->settings().siteIsolationEnabled() && !WebProcess::singleton().allowsFirstPartyForCookies(loadParameters.request.firstPartyForCookies())) RELEASE_LOG_FAULT(IPC, "scheduleLoad: Process will terminate due to failed allowsFirstPartyForCookies check"); -@@ -545,7 +559,7 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL +@@ -540,7 +554,7 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL } auto loader = WebResourceLoader::create(resourceLoader, trackingParameters); @@ -20266,7 +20269,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 } void WebLoaderStrategy::scheduleInternallyFailedLoad(WebCore::ResourceLoader& resourceLoader) -@@ -954,7 +968,7 @@ void WebLoaderStrategy::didFinishPreconnection(WebCore::ResourceLoaderIdentifier +@@ -949,7 +963,7 @@ void WebLoaderStrategy::didFinishPreconnection(WebCore::ResourceLoaderIdentifier bool WebLoaderStrategy::isOnLine() const { @@ -20275,7 +20278,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 } void WebLoaderStrategy::addOnlineStateChangeListener(Function&& listener) -@@ -981,6 +995,11 @@ void WebLoaderStrategy::isResourceLoadFinished(CachedResource& resource, Complet +@@ -976,6 +990,11 @@ void WebLoaderStrategy::isResourceLoadFinished(CachedResource& resource, Complet void WebLoaderStrategy::setOnLineState(bool isOnLine) { @@ -20287,7 +20290,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 if (m_isOnLine == isOnLine) return; -@@ -989,6 +1008,12 @@ void WebLoaderStrategy::setOnLineState(bool isOnLine) +@@ -984,6 +1003,12 @@ void WebLoaderStrategy::setOnLineState(bool isOnLine) listener(isOnLine); } @@ -20301,7 +20304,7 @@ index 8d0b53b4b42552d750d3e094f58709b868ac39e4..70be1bb2fade88c7b622ef7057ddac59 { WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::SetCaptureExtraNetworkLoadMetricsEnabled(enabled), 0); diff --git a/Source/WebKit/WebProcess/Network/WebLoaderStrategy.h b/Source/WebKit/WebProcess/Network/WebLoaderStrategy.h -index c934b96510c03ed6301d3695edbb30710331d7f6..ee2e7ba36e50c14aee91e9f09d70b80026aeb8d5 100644 +index 3ef86cc236b8acee2fbe5d0b9c3fd755fcc9f06f..75951fc0fc5e4ef566582c0a494827933ac8bfd1 100644 --- a/Source/WebKit/WebProcess/Network/WebLoaderStrategy.h +++ b/Source/WebKit/WebProcess/Network/WebLoaderStrategy.h @@ -42,6 +42,7 @@ struct FetchOptions; @@ -20331,10 +20334,10 @@ index c934b96510c03ed6301d3695edbb30710331d7f6..ee2e7ba36e50c14aee91e9f09d70b800 } // namespace WebKit diff --git a/Source/WebKit/WebProcess/Network/WebResourceLoader.cpp b/Source/WebKit/WebProcess/Network/WebResourceLoader.cpp -index 3d4a9612fd50f92fa1922f3918926c1f89c8aed7..ba5f0baafe3035386cd6f9bb0b044bb40000e752 100644 +index 59c04024b944527cb4d5aa9ad3359db4d684e102..0ed0cdfcc8f5bab168df8e022e7c3cc80aab086b 100644 --- a/Source/WebKit/WebProcess/Network/WebResourceLoader.cpp +++ b/Source/WebKit/WebProcess/Network/WebResourceLoader.cpp -@@ -198,9 +198,6 @@ void WebResourceLoader::didReceiveResponse(ResourceResponse&& response, PrivateR +@@ -200,9 +200,6 @@ void WebResourceLoader::didReceiveResponse(ResourceResponse&& response, PrivateR } m_coreLoader->didReceiveResponse(inspectorResponse, [this, protectedThis = WTFMove(protectedThis), interceptedRequestIdentifier, policyDecisionCompletionHandler = WTFMove(policyDecisionCompletionHandler), overrideData = WTFMove(overrideData)]() mutable { @@ -20344,7 +20347,7 @@ index 3d4a9612fd50f92fa1922f3918926c1f89c8aed7..ba5f0baafe3035386cd6f9bb0b044bb4 if (!m_coreLoader || !m_coreLoader->identifier()) { m_interceptController.continueResponse(interceptedRequestIdentifier); return; -@@ -218,6 +215,8 @@ void WebResourceLoader::didReceiveResponse(ResourceResponse&& response, PrivateR +@@ -220,6 +217,8 @@ void WebResourceLoader::didReceiveResponse(ResourceResponse&& response, PrivateR } }); }); @@ -20367,10 +20370,10 @@ index ee9c3c4f48c328daaa015e2122235e51349bd999..5b3a4d3e742147195e0ff9e88176759d auto permissionHandlers = m_requestsPerOrigin.take(securityOrigin); diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp -index f7e7166c5905b7de9225d54447ff392c70996c35..37f543662f58af9ff4b5846c0d6b9ed60f0ed83a 100644 +index 6819d51a57c701ccdb625c1b549c5e1e87cce744..24e825dfdddae160de407225f9fa0c5d02cd973b 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp -@@ -455,6 +455,8 @@ void WebChromeClient::addMessageToConsole(MessageSource source, MessageLevel lev +@@ -463,6 +463,8 @@ void WebChromeClient::addMessageToConsole(MessageSource source, MessageLevel lev { // Notify the bundle client. auto page = protectedPage(); @@ -20407,10 +20410,10 @@ index 2eb0886f13ed035a53b8eaa60605de4dfe53fbe3..c46393209cb4f80704bbc9268fad4371 { } diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp -index 11640a4a126ac8f2b04f427687f1d3a4ec521bfc..e03eac20cabd7f0eb2c2e841b40844de24d8c2e1 100644 +index f12b986ff225dba84be246f5ec471c7cf2d27def..954ba6f0f9380c341fa58ae8fdaf2535187d091a 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp -@@ -1533,14 +1533,6 @@ void WebLocalFrameLoaderClient::transitionToCommittedForNewPage() +@@ -1566,14 +1566,6 @@ void WebLocalFrameLoaderClient::transitionToCommittedForNewPage() if (webPage->scrollPinningBehavior() != ScrollPinningBehavior::DoNotPin) view->setScrollPinningBehavior(webPage->scrollPinningBehavior()); @@ -20568,7 +20571,7 @@ index 0000000000000000000000000000000000000000..af21b0d1dd4e9b1f74387a8f8a928244 + +#endif // ENABLE(DRAG_SUPPORT) diff --git a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/DrawingAreaCoordinatedGraphics.cpp b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/DrawingAreaCoordinatedGraphics.cpp -index 488473aad91d0f79265bcb56e3ce9b411d079122..58802769990c6d0a134e831e5454281de46f9c1a 100644 +index 1505262429a0737183ac24b74f6186225a177893..be2122ec1735b10979dd453cb60de3bb02b54bcf 100644 --- a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/DrawingAreaCoordinatedGraphics.cpp +++ b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/DrawingAreaCoordinatedGraphics.cpp @@ -40,6 +40,7 @@ @@ -20579,7 +20582,7 @@ index 488473aad91d0f79265bcb56e3ce9b411d079122..58802769990c6d0a134e831e5454281d #include #include #include -@@ -105,6 +106,16 @@ void DrawingAreaCoordinatedGraphics::scroll(const IntRect& scrollRect, const Int +@@ -103,6 +104,16 @@ void DrawingAreaCoordinatedGraphics::scroll(const IntRect& scrollRect, const Int ASSERT(m_scrollRect.isEmpty()); ASSERT(m_scrollOffset.isEmpty()); ASSERT(m_dirtyRegion.isEmpty()); @@ -20596,15 +20599,7 @@ index 488473aad91d0f79265bcb56e3ce9b411d079122..58802769990c6d0a134e831e5454281d m_layerTreeHost->scrollNonCompositedContents(scrollRect); return; } -@@ -218,6 +229,7 @@ void DrawingAreaCoordinatedGraphics::updatePreferences(const WebPreferencesStore - settings.setAcceleratedCompositingEnabled(false); - } - #endif -+ - settings.setForceCompositingMode(store.getBoolValueForKey(WebPreferencesKey::forceCompositingModeKey())); - // Fixed position elements need to be composited and create stacking contexts - // in order to be scrolled by the ScrollingCoordinator. -@@ -573,6 +585,11 @@ void DrawingAreaCoordinatedGraphics::enterAcceleratedCompositingMode(GraphicsLay +@@ -556,6 +567,11 @@ void DrawingAreaCoordinatedGraphics::enterAcceleratedCompositingMode(GraphicsLay m_scrollOffset = IntSize(); m_displayTimer.stop(); m_isWaitingForDidUpdate = false; @@ -20616,7 +20611,7 @@ index 488473aad91d0f79265bcb56e3ce9b411d079122..58802769990c6d0a134e831e5454281d } void DrawingAreaCoordinatedGraphics::sendEnterAcceleratedCompositingModeIfNeeded() -@@ -630,6 +647,11 @@ void DrawingAreaCoordinatedGraphics::exitAcceleratedCompositingMode() +@@ -613,6 +629,11 @@ void DrawingAreaCoordinatedGraphics::exitAcceleratedCompositingMode() // UI process, we still need to let it know about the new contents, so send an Update message. send(Messages::DrawingAreaProxy::Update(0, WTFMove(updateInfo))); } @@ -20629,10 +20624,10 @@ index 488473aad91d0f79265bcb56e3ce9b411d079122..58802769990c6d0a134e831e5454281d void DrawingAreaCoordinatedGraphics::scheduleDisplay() diff --git a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp -index 0bd870f9c0f2ff5cbb638701dab0dca3595bbc4a..fd09e59c2a94324a2ebb4266196e910d53b54686 100644 +index 88878cde07fc3667371437a12d2bb660b019970a..98cc9d47b99b7d99ce8d257f6d2d423927d247a7 100644 --- a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp +++ b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.cpp -@@ -198,8 +198,16 @@ void LayerTreeHost::setViewOverlayRootLayer(GraphicsLayer* viewOverlayRootLayer) +@@ -197,8 +197,16 @@ void LayerTreeHost::setViewOverlayRootLayer(GraphicsLayer* viewOverlayRootLayer) void LayerTreeHost::scrollNonCompositedContents(const IntRect& rect) { auto* frameView = m_webPage.localMainFrameView(); @@ -20649,7 +20644,7 @@ index 0bd870f9c0f2ff5cbb638701dab0dca3595bbc4a..fd09e59c2a94324a2ebb4266196e910d m_viewportController.didScroll(rect.location()); didChangeViewport(); -@@ -324,6 +332,10 @@ void LayerTreeHost::didChangeViewport() +@@ -317,6 +325,10 @@ void LayerTreeHost::didChangeViewport() if (!view->useFixedLayout()) view->notifyScrollPositionChanged(m_lastScrollPosition); @@ -20661,12 +20656,12 @@ index 0bd870f9c0f2ff5cbb638701dab0dca3595bbc4a..fd09e59c2a94324a2ebb4266196e910d if (m_lastPageScaleFactor != pageScale) { diff --git a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.h b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.h -index d8f40210a6e2dfbaeec672e0871a6cb5aaab24af..9053ecf0d9736c02581c14a5af140f39d01b191e 100644 +index cb3d2cc477cc1244d6d2ccedb3d484cdf4a0d82c..077019e38cd782a62e9328948b6d0a9fb6920380 100644 --- a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.h +++ b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.h -@@ -112,6 +112,13 @@ public: - void adjustTransientZoom(double, WebCore::FloatPoint); - void commitTransientZoom(double, WebCore::FloatPoint); +@@ -114,6 +114,13 @@ public: + #if PLATFORM(WPE) && USE(GBM) && ENABLE(WPE_PLATFORM) + void preferredBufferFormatsDidChange(); #endif + +// Playwright begin @@ -20705,7 +20700,7 @@ index 30c1f55828df6bf4e48543cc3347dde1ee41e10f..e71997ca8292530c5c01ce141443ad42 { if (m_hasRemovedMessageReceiver) diff --git a/Source/WebKit/WebProcess/WebPage/DrawingArea.h b/Source/WebKit/WebProcess/WebPage/DrawingArea.h -index 6ba3b77dc8eaa403ab2ca1a4f25f85eed228af5f..190d9c223c1a99aaa7f5de43b094087875f3fe4e 100644 +index 891e7d4dba97ec6a2b2ac27ae052636ec4de6057..ff1932c7c6d8233b1c92360f1b9fe47d5786e461 100644 --- a/Source/WebKit/WebProcess/WebPage/DrawingArea.h +++ b/Source/WebKit/WebProcess/WebPage/DrawingArea.h @@ -163,6 +163,9 @@ public: @@ -20716,8 +20711,8 @@ index 6ba3b77dc8eaa403ab2ca1a4f25f85eed228af5f..190d9c223c1a99aaa7f5de43b0940878 + void didChangeAcceleratedCompositingMode(bool enabled); +#endif - virtual void adoptLayersFromDrawingArea(DrawingArea&) { } - virtual void adoptDisplayRefreshMonitorsFromDrawingArea(DrawingArea&) { } + #if PLATFORM(WPE) && USE(GBM) && ENABLE(WPE_PLATFORM) + virtual void preferredBufferFormatsDidChange() { } diff --git a/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp b/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp index 35586a9dfccfe82ec7e6d38c4aa2c624b6d05597..58912fc3e33887e5ccfe8ac05c706f473c709566 100644 --- a/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp @@ -20797,10 +20792,10 @@ index 22a2194e393c7bfcc6a6635b6fdb7e95994db3e9..a060b7aff37549c79c63cde23fa66938 uint64_t m_navigationID { 0 }; }; diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.cpp b/Source/WebKit/WebProcess/WebPage/WebPage.cpp -index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c20dffef20 100644 +index 01073ba7befdad88b8f29a063d81b19dcca2e4af..52cb736a9475f4288ef4fe1bf60fd49d5490e8d3 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.cpp +++ b/Source/WebKit/WebProcess/WebPage/WebPage.cpp -@@ -1041,6 +1041,9 @@ WebPage::WebPage(PageIdentifier pageID, WebPageCreationParameters&& parameters) +@@ -1009,6 +1009,9 @@ WebPage::WebPage(PageIdentifier pageID, WebPageCreationParameters&& parameters) #endif #endif // HAVE(SANDBOX_STATE_FLAGS) @@ -20810,7 +20805,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 updateThrottleState(); #if ENABLE(ACCESSIBILITY_ANIMATION_CONTROL) updateImageAnimationEnabled(); -@@ -1915,6 +1918,22 @@ void WebPage::transitionFrameToLocal(LocalFrameCreationParameters&& creationPara +@@ -1918,6 +1921,22 @@ void WebPage::transitionFrameToLocal(LocalFrameCreationParameters&& creationPara frame->transitionToLocal(creationParameters.layerHostingContextIdentifier); } @@ -20833,7 +20828,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 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,17 +2219,14 @@ void WebPage::setSize(const WebCore::IntSize& viewSize) +@@ -2194,17 +2213,14 @@ void WebPage::setSize(const WebCore::IntSize& viewSize) view->resize(viewSize); m_drawingArea->setNeedsDisplay(); @@ -20851,7 +20846,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArguments) { RefPtr localMainFrame = dynamicDowncast(m_page->mainFrame()); -@@ -2235,20 +2251,18 @@ void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArg +@@ -2229,20 +2245,18 @@ void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArg ViewportAttributes attr = computeViewportAttributes(viewportArguments, minimumLayoutFallbackWidth, deviceWidth, deviceHeight, 1, m_viewSize); @@ -20879,7 +20874,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 #if USE(COORDINATED_GRAPHICS) m_drawingArea->didChangeViewportAttributes(WTFMove(attr)); -@@ -2256,7 +2270,6 @@ void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArg +@@ -2250,7 +2264,6 @@ void WebPage::sendViewportAttributesChanged(const ViewportArguments& viewportArg send(Messages::WebPageProxy::DidChangeViewportProperties(attr)); #endif } @@ -20887,7 +20882,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 void WebPage::scrollMainFrameIfNotAtMaxScrollPosition(const IntSize& scrollOffset) { -@@ -2552,6 +2565,7 @@ void WebPage::scaleView(double scale) +@@ -2543,6 +2556,7 @@ void WebPage::scaleView(double scale) } m_page->setViewScaleFactor(scale); @@ -20895,7 +20890,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 scalePage(pageScale, scrollPositionAtNewScale); } -@@ -2731,18 +2745,14 @@ void WebPage::viewportPropertiesDidChange(const ViewportArguments& viewportArgum +@@ -2722,18 +2736,14 @@ void WebPage::viewportPropertiesDidChange(const ViewportArguments& viewportArgum viewportConfigurationChanged(); #endif @@ -20915,7 +20910,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 } #if !PLATFORM(IOS_FAMILY) -@@ -3714,6 +3724,97 @@ void WebPage::touchEvent(const WebTouchEvent& touchEvent, CompletionHandlersendMessageToTargetBackend(targetId, message); } @@ -21025,7 +21020,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 void WebPage::insertNewlineInQuotedContent() { Ref frame = CheckedRef(m_page->focusController())->focusedOrMainFrame(); -@@ -4002,6 +4108,7 @@ void WebPage::didCompletePageTransition() +@@ -3991,6 +4097,7 @@ void WebPage::didCompletePageTransition() void WebPage::show() { send(Messages::WebPageProxy::ShowPage()); @@ -21033,7 +21028,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 } void WebPage::setIsTakingSnapshotsForApplicationSuspension(bool isTakingSnapshotsForApplicationSuspension) -@@ -5031,7 +5138,7 @@ NotificationPermissionRequestManager* WebPage::notificationPermissionRequestMana +@@ -5044,7 +5151,7 @@ NotificationPermissionRequestManager* WebPage::notificationPermissionRequestMana #if ENABLE(DRAG_SUPPORT) @@ -21042,7 +21037,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 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) -@@ -7297,6 +7404,10 @@ void WebPage::didCommitLoad(WebFrame* frame) +@@ -7308,6 +7415,10 @@ void WebPage::didCommitLoad(WebFrame* frame) #endif flushDeferredDidReceiveMouseEvent(); @@ -21053,7 +21048,7 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 } void WebPage::didFinishDocumentLoad(WebFrame& frame) -@@ -7526,6 +7637,9 @@ Ref WebPage::createDocumentLoader(LocalFrame& frame, const Resou +@@ -7537,6 +7648,9 @@ Ref WebPage::createDocumentLoader(LocalFrame& frame, const Resou WebsitePoliciesData::applyToDocumentLoader(WTFMove(*m_pendingWebsitePolicies), documentLoader); m_pendingWebsitePolicies = std::nullopt; } @@ -21064,10 +21059,10 @@ index 5dc0f3459c55b5ac6d13ae939dd7221b4649da65..c4387bf9fe6e0f1959e5757f39bc92c2 return documentLoader; diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h -index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b545250dd8 100644 +index 3bf27b0025d6467a2d52577d627da87f21bdffb6..ab4bde77c7079d78709f9fb66b03aae98446e1d5 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.h +++ b/Source/WebKit/WebProcess/WebPage/WebPage.h -@@ -69,6 +69,7 @@ +@@ -70,6 +70,7 @@ #include #include #include @@ -21075,7 +21070,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 #include #include #include -@@ -109,6 +110,10 @@ +@@ -110,6 +111,10 @@ #include "WebPrintOperationGtk.h" #endif @@ -21086,7 +21081,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 #if PLATFORM(GTK) || PLATFORM(WPE) #include "InputMethodState.h" #endif -@@ -1099,11 +1104,11 @@ public: +@@ -1105,11 +1110,11 @@ public: void clearSelection(); void restoreSelectionInFocusedEditableElement(); @@ -21100,7 +21095,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 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 -@@ -1118,6 +1123,9 @@ public: +@@ -1124,6 +1129,9 @@ public: void didStartDrag(); void dragCancelled(); OptionSet allowedDragSourceActions() const { return m_allowedDragSourceActions; } @@ -21110,7 +21105,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 #endif void beginPrinting(WebCore::FrameIdentifier, const PrintInfo&); -@@ -1345,6 +1353,7 @@ public: +@@ -1351,6 +1359,7 @@ public: void connectInspector(const String& targetId, Inspector::FrontendChannel::ConnectionType); void disconnectInspector(const String& targetId); void sendMessageToTargetBackend(const String& targetId, const String& message); @@ -21118,7 +21113,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 void insertNewlineInQuotedContent(); -@@ -1778,6 +1787,7 @@ private: +@@ -1791,6 +1800,7 @@ private: void tryClose(CompletionHandler&&); void platformDidReceiveLoadParameters(const LoadParameters&); void transitionFrameToLocal(LocalFrameCreationParameters&&, WebCore::FrameIdentifier); @@ -21126,7 +21121,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 void loadRequest(LoadParameters&&); [[noreturn]] void loadRequestWaitingForProcessLaunch(LoadParameters&&, URL&&, WebPageProxyIdentifier, bool); void loadData(LoadParameters&&); -@@ -1816,6 +1826,7 @@ private: +@@ -1829,6 +1839,7 @@ private: void updatePotentialTapSecurityOrigin(const WebTouchEvent&, bool wasHandled); #elif ENABLE(TOUCH_EVENTS) void touchEvent(const WebTouchEvent&, CompletionHandler, bool)>&&); @@ -21134,7 +21129,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 #endif void cancelPointer(WebCore::PointerID, const WebCore::IntPoint&); -@@ -1960,9 +1971,7 @@ private: +@@ -1973,9 +1984,7 @@ private: void addLayerForFindOverlay(CompletionHandler&&); void removeLayerForFindOverlay(CompletionHandler&&); @@ -21144,7 +21139,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 void didChangeSelectedIndexForActivePopupMenu(int32_t newIndex); void setTextForActivePopupMenu(int32_t index); -@@ -2514,6 +2523,7 @@ private: +@@ -2538,6 +2547,7 @@ private: UserActivity m_userActivity; uint64_t m_pendingNavigationID { 0 }; @@ -21153,7 +21148,7 @@ index fa5a03b9faeced1c3f428507ee458f34fc0f5553..760d3f6cdcfc2a72eb1f25b411c359b5 bool m_mainFrameProgressCompleted { false }; diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in -index 0a070bebea8468979e5a8b9d4d57a768930aab70..1c092d5d6860ab0fb9ef37c38d4c01140e87eef6 100644 +index ba8ee4491a50a2eb04143c158f14ebb0338174e4..c3dcdb000cd49b2d7631ad80e6d6eda8759ada15 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in +++ b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in @@ -149,6 +149,7 @@ GenerateSyntheticEditingCommand(enum:uint8_t WebKit::SyntheticEditingCommandType @@ -21205,10 +21200,10 @@ index 0a070bebea8468979e5a8b9d4d57a768930aab70..1c092d5d6860ab0fb9ef37c38d4c0114 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 e2a6c27fabb4304c344f467c877aff40ee3226e3..29e7cfcc6c7e1066dbb9e35ec4477bd49aa045bb 100644 +index 174768320a3d2d5a974b8423f3552cf9130abb72..2303e81ce777a28cf07fc24fb01a386fe39a5abe 100644 --- a/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm +++ b/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm -@@ -813,21 +813,37 @@ String WebPage::platformUserAgent(const URL&) const +@@ -812,21 +812,37 @@ String WebPage::platformUserAgent(const URL&) const bool WebPage::hoverSupportedByPrimaryPointingDevice() const { @@ -21297,7 +21292,7 @@ index f17f5d719d892309ed9c7093384945866b5117b9..1dba47bbf0dbd0362548423a74b38034 } diff --git a/Source/WebKit/WebProcess/WebProcess.cpp b/Source/WebKit/WebProcess/WebProcess.cpp -index e4d40bb685e7784d7952002227cc61b0151b0dfd..ddfb3c382d38a2a58e042d5be233d63dfcf8108f 100644 +index 570975d8cd2d1e71bacb660dc2c50f3cd4832239..376263ec6f5f1430975a00e1b2a47b03493fe860 100644 --- a/Source/WebKit/WebProcess/WebProcess.cpp +++ b/Source/WebKit/WebProcess/WebProcess.cpp @@ -87,6 +87,7 @@ @@ -21308,7 +21303,7 @@ index e4d40bb685e7784d7952002227cc61b0151b0dfd..ddfb3c382d38a2a58e042d5be233d63d #include #include #include -@@ -360,6 +361,8 @@ void WebProcess::initializeProcess(const AuxiliaryProcessInitializationParameter +@@ -361,6 +362,8 @@ void WebProcess::initializeProcess(const AuxiliaryProcessInitializationParameter platformInitializeProcess(parameters); updateCPULimit(); @@ -21317,19 +21312,6 @@ index e4d40bb685e7784d7952002227cc61b0151b0dfd..ddfb3c382d38a2a58e042d5be233d63d } void WebProcess::initializeConnection(IPC::Connection* connection) -diff --git a/Source/WebKit/WebProcess/WebProcess.h b/Source/WebKit/WebProcess/WebProcess.h -index 6c0e1aa6282c404614ef7a76ec8df7556f146906..5e54a06e574845cf2bd5acd3ca8adc8bd94403a8 100644 ---- a/Source/WebKit/WebProcess/WebProcess.h -+++ b/Source/WebKit/WebProcess/WebProcess.h -@@ -775,7 +775,7 @@ private: - - WeakHashMap m_userGestureTokens; - --#if PLATFORM(GTK) -+#if PLATFORM(WAYLAND) || PLATFORM(GTK) - std::unique_ptr m_displayForCompositing; - OptionSet m_dmaBufRendererBufferMode; - #endif 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 @@ -21346,7 +21328,7 @@ index 8987c3964a9308f2454759de7f8972215a3ae416..bcac0afeb94ed8123d1f9fb0b932c849 SetProcessDPIAware(); return true; diff --git a/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm b/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm -index 08ab3227d2bb9ec7375cce834522ecfa59df226b..0dc27f05f5bf07169ca80706dc2b755e6200a610 100644 +index 50eaf5a4da5c6d9a986f821094666698936f66af..9693a99c8558e478c70044b42cbb4c35855550ec 100644 --- a/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm +++ b/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm @@ -4210,7 +4210,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END @@ -21359,10 +21341,10 @@ index 08ab3227d2bb9ec7375cce834522ecfa59df226b..0dc27f05f5bf07169ca80706dc2b755e - (void)touch:(WebEvent *)event { diff --git a/Source/WebKitLegacy/mac/WebView/WebView.mm b/Source/WebKitLegacy/mac/WebView/WebView.mm -index 76c630c56615e95bee5767099ad3af417e56c9d3..10769addcd4eeef5dc497daacbbb0bfad122d0ba 100644 +index f15835107bc07430ff2544feb2caf925315102c9..c3ff4cd3018e3263b2b1490142f48885566ced49 100644 --- a/Source/WebKitLegacy/mac/WebView/WebView.mm +++ b/Source/WebKitLegacy/mac/WebView/WebView.mm -@@ -3976,7 +3976,7 @@ + (void)_doNotStartObservingNetworkReachability +@@ -3942,7 +3942,7 @@ + (void)_doNotStartObservingNetworkReachability } #endif // PLATFORM(IOS_FAMILY) @@ -21371,7 +21353,7 @@ index 76c630c56615e95bee5767099ad3af417e56c9d3..10769addcd4eeef5dc497daacbbb0bfa - (NSArray *)_touchEventRegions { -@@ -4018,7 +4018,7 @@ - (NSArray *)_touchEventRegions +@@ -3984,7 +3984,7 @@ - (NSArray *)_touchEventRegions }).autorelease(); } @@ -21412,7 +21394,7 @@ index 0000000000000000000000000000000000000000..dd6a53e2d57318489b7e49dd7373706d + LIBVPX_LIBRARIES +) diff --git a/Source/cmake/OptionsGTK.cmake b/Source/cmake/OptionsGTK.cmake -index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a14c4f3ae 100644 +index 958481ce651a3788b7f6694c336620194d97f68f..13fd67b1e50f5f341e382b4b85fb89233de9650c 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 @@ -21429,7 +21411,7 @@ index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a find_package(Cairo 1.16.0 REQUIRED) find_package(Fontconfig 2.13.0 REQUIRED) find_package(Freetype 2.9.0 REQUIRED) -@@ -30,6 +35,10 @@ find_package(ZLIB REQUIRED) +@@ -31,6 +36,10 @@ find_package(ZLIB REQUIRED) find_package(WebP REQUIRED COMPONENTS demux) find_package(ATSPI 2.5.3) @@ -21440,7 +21422,7 @@ index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a include(GStreamerDefinitions) SET_AND_EXPOSE_TO_BUILD(USE_CAIRO TRUE) -@@ -98,15 +107,15 @@ endif () +@@ -93,14 +102,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) @@ -21451,7 +21433,6 @@ index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PDFJS PUBLIC OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_SPELLCHECK PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_TOUCH_EVENTS PUBLIC ON) - WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_CRYPTO PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBDRIVER PUBLIC ON) -WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_AVIF PUBLIC ON) @@ -21459,7 +21440,7 @@ index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_JPEGXL PUBLIC ON) # Private options shared with other WebKit ports. Add options here when -@@ -132,7 +141,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_INPUT_TYPE_MONTH PRIVATE ON) +@@ -124,7 +133,7 @@ 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) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_LAYER_BASED_SVG_ENGINE PRIVATE ON) @@ -21468,7 +21449,7 @@ index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_SESSION PRIVATE ${ENABLE_EXPERIMENTAL_FEATURES}) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_SESSION_PLAYLIST PRIVATE OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_STREAM PRIVATE ON) -@@ -145,7 +154,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NETWORK_CACHE_SPECULATIVE_REVALIDATION P +@@ -136,7 +145,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 ${ENABLE_EXPERIMENTAL_FEATURES}) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_OFFSCREEN_CANVAS_IN_WORKERS PRIVATE ${ENABLE_EXPERIMENTAL_FEATURES}) @@ -21476,15 +21457,14 @@ index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_THUNDER PRIVATE OFF) 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_SERVICE_WORKER PRIVATE ON) -@@ -156,6 +165,15 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_API_STATISTICS PRIVATE ON) + WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_SHAREABLE_RESOURCE PRIVATE ON) +@@ -146,6 +155,14 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_API_STATISTICS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_CODECS PRIVATE ${ENABLE_EXPERIMENTAL_FEATURES}) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_RTC PRIVATE ${ENABLE_EXPERIMENTAL_FEATURES}) +# Playwright +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_APPLICATION_MANIFEST PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_CURSOR_VISIBILITY PRIVATE ON) -+WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DOWNLOAD_ATTRIBUTE PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DEVICE_ORIENTATION PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_GAMEPAD PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_SPEECH_SYNTHESIS PRIVATE ON) @@ -21493,7 +21473,7 @@ index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a include(GStreamerDependencies) # Finalize the value for all options. Do not attempt to use an option before -@@ -271,6 +289,7 @@ if (NOT EXISTS "${TOOLS_DIR}/glib/apply-build-revision-to-files.py") +@@ -261,6 +278,7 @@ if (NOT EXISTS "${TOOLS_DIR}/glib/apply-build-revision-to-files.py") endif () SET_AND_EXPOSE_TO_BUILD(USE_ATSPI ${ENABLE_ACCESSIBILITY}) @@ -21502,7 +21482,7 @@ index 26e4e4d9a16897e3685fd9cf5aacb7fce24390b6..ae01866884bc6cbde28425f66847521a SET_AND_EXPOSE_TO_BUILD(HAVE_OS_DARK_MODE_SUPPORT 1) diff --git a/Source/cmake/OptionsWPE.cmake b/Source/cmake/OptionsWPE.cmake -index cf6f8972fa4e26cb4a31331da27ac22c77804cbe..55b822bfb9167d81fe855cd2cc3e9951a2fe98f9 100644 +index a1dd7fe27cdf20f3d526732d2aeb1b4cf2687124..ddd4008cc0dc7517b6e6be313d74c1721bced6b7 100644 --- a/Source/cmake/OptionsWPE.cmake +++ b/Source/cmake/OptionsWPE.cmake @@ -9,8 +9,13 @@ if (${CMAKE_VERSION} VERSION_LESS "3.20" AND NOT ${CMAKE_GENERATOR} STREQUAL "Ni @@ -21519,14 +21499,13 @@ index cf6f8972fa4e26cb4a31331da27ac22c77804cbe..55b822bfb9167d81fe855cd2cc3e9951 find_package(Cairo 1.16.0 REQUIRED) find_package(Fontconfig 2.13.0 REQUIRED) find_package(Freetype 2.9.0 REQUIRED) -@@ -39,12 +44,12 @@ include(GStreamerDefinitions) +@@ -41,11 +46,11 @@ include(GStreamerDefinitions) # changing the value of the option. WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_ACCESSIBILITY PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_ENCRYPTED_MEDIA PUBLIC ${ENABLE_EXPERIMENTAL_FEATURES}) -WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PDFJS PUBLIC ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PDFJS PUBLIC OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBDRIVER PUBLIC ON) - WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_CRYPTO PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_XSLT PUBLIC ON) -WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_AVIF PUBLIC ON) @@ -21543,8 +21522,8 @@ index cf6f8972fa4e26cb4a31331da27ac22c77804cbe..55b822bfb9167d81fe855cd2cc3e9951 WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_SESSION PRIVATE ${ENABLE_EXPERIMENTAL_FEATURES}) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_SESSION_PLAYLIST PRIVATE OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_STREAM PRIVATE ON) -@@ -76,7 +81,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PERIODIC_MEMORY_MONITOR PRIVATE ON) - WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_SERVICE_WORKER PRIVATE ON) +@@ -75,7 +80,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_OFFSCREEN_CANVAS_IN_WORKERS PRIVATE ${EN + 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}) -WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_THUNDER PRIVATE ${ENABLE_DEVELOPER_MODE}) @@ -21552,7 +21531,7 @@ index cf6f8972fa4e26cb4a31331da27ac22c77804cbe..55b822bfb9167d81fe855cd2cc3e9951 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 ${ENABLE_EXPERIMENTAL_FEATURES}) -@@ -87,13 +92,30 @@ if (WPE_VERSION VERSION_GREATER_EQUAL 1.13.90) +@@ -86,6 +91,23 @@ if (WPE_VERSION VERSION_GREATER_EQUAL 1.13.90) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_GAMEPAD PUBLIC ON) endif () @@ -21561,7 +21540,6 @@ index cf6f8972fa4e26cb4a31331da27ac22c77804cbe..55b822bfb9167d81fe855cd2cc3e9951 +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_CURSOR_VISIBILITY PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DARK_MODE_CSS PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DATALIST_ELEMENT PRIVATE ON) -+WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DOWNLOAD_ATTRIBUTE PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DRAG_SUPPORT PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DEVICE_ORIENTATION PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_INPUT_TYPE_COLOR PRIVATE ON) @@ -21570,22 +21548,32 @@ index cf6f8972fa4e26cb4a31331da27ac22c77804cbe..55b822bfb9167d81fe855cd2cc3e9951 +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) ++WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DATE_AND_TIME_INPUT_TYPES PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_SPEECH_SYNTHESIS PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_POINTER_LOCK PRIVATE ON) + # 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. - WEBKIT_OPTION_DEFINE(ENABLE_DOCUMENTATION "Whether to generate documentation." PUBLIC ON) - WEBKIT_OPTION_DEFINE(ENABLE_INTROSPECTION "Whether to enable GObject introspection." PUBLIC ON) - WEBKIT_OPTION_DEFINE(ENABLE_JOURNALD_LOG "Whether to enable journald logging" PUBLIC ON) +@@ -95,7 +117,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) -WEBKIT_OPTION_DEFINE(ENABLE_WPE_QT_API "Whether to enable support for the Qt5/QML plugin" PUBLIC ${ENABLE_DEVELOPER_MODE}) +WEBKIT_OPTION_DEFINE(ENABLE_WPE_QT_API "Whether to enable support for the Qt5/QML plugin" PUBLIC OFF) WEBKIT_OPTION_DEFINE(ENABLE_WPE_1_1_API "Whether to build WPE 1.1 instead of WPE 2.0" PUBLIC OFF) WEBKIT_OPTION_DEFINE(USE_GBM "Whether to enable usage of GBM." PUBLIC ON) WEBKIT_OPTION_DEFINE(USE_LCMS "Whether to enable support for image color management using libcms2." PUBLIC ON) +@@ -214,6 +236,7 @@ endif () + + set(CMAKE_C_VISIBILITY_PRESET hidden) + set(CMAKE_CXX_VISIBILITY_PRESET hidden) ++set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lrt") + set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + set(bmalloc_LIBRARY_TYPE OBJECT) + set(WTF_LIBRARY_TYPE OBJECT) diff --git a/Source/cmake/OptionsWin.cmake b/Source/cmake/OptionsWin.cmake -index afe74b5ddfbae35ccb449cd83e25d55f1c1ed568..3df741316d7603d52c20aa36bc887937820d7016 100644 +index 8cc9e7a924fd2ca419e950b30e75aeafacd4bc44..0101e67b4b917e4742d615394938d75ea00794d6 100644 --- a/Source/cmake/OptionsWin.cmake +++ b/Source/cmake/OptionsWin.cmake @@ -97,16 +97,37 @@ if (OpenJPEG_FOUND) @@ -21630,23 +21618,21 @@ index afe74b5ddfbae35ccb449cd83e25d55f1c1ed568..3df741316d7603d52c20aa36bc887937 WEBKIT_OPTION_BEGIN() # FIXME: Most of these options should not be public. -@@ -184,6 +205,16 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_CRYPTO PRIVATE ${ENABLE_EXPERIMENTAL +@@ -178,6 +199,14 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBDRIVER PRIVATE ${ENABLE_EXPERIMENTAL_ # No support planned WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_FTPDIR PRIVATE OFF) +# Plawright begin -+WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_CSS_CONIC_GRADIENTS PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DARK_MODE_CSS PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DEVICE_ORIENTATION PRIVATE ON) -+WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_DOWNLOAD_ATTRIBUTE PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NOTIFICATIONS PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_POINTER_LOCK PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_TOUCH_EVENTS PRIVATE ON) +# Playwright end + - # FIXME: Implement plugin process on Modern WebKit. https://bugs.webkit.org/show_bug.cgi?id=185313 - WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NETSCAPE_PLUGIN_API PRIVATE OFF) + WEBKIT_OPTION_END() + set(USE_ANGLE_EGL ON) diff --git a/Source/cmake/WebKitCompilerFlags.cmake b/Source/cmake/WebKitCompilerFlags.cmake index df8464ed9d2227db3e801e01e63fafab581e625e..a2fa79209908a6d072faace91e0c5b102843aae7 100644 --- a/Source/cmake/WebKitCompilerFlags.cmake @@ -21976,7 +21962,7 @@ index 451e0333dd4e8f5d6313fa80c5027ef2661a61ac..7398af4772ed9a479b1632e38af2d4ee return exitAfterLoad && webProcessCrashed ? 1 : 0; diff --git a/Tools/MiniBrowser/wpe/main.cpp b/Tools/MiniBrowser/wpe/main.cpp -index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850860f3268 100644 +index 6e618c32288f30d1f24ecbe929a3d37670981154..de6b4b8ede31518309eee78899f27f9b897c7f87 100644 --- a/Tools/MiniBrowser/wpe/main.cpp +++ b/Tools/MiniBrowser/wpe/main.cpp @@ -45,6 +45,9 @@ static gboolean headlessMode; @@ -21989,9 +21975,9 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 static const char* contentFilter; static const char* cookiesFile; static const char* cookiesPolicy; -@@ -72,6 +75,9 @@ static const GOptionEntry commandLineOptions[] = - { "time-zone", 't', 0, G_OPTION_ARG_STRING, &timeZone, "Set time zone", "TIMEZONE" }, - { "features", 'F', 0, G_OPTION_ARG_STRING, &featureList, "Enable or disable WebKit features (hint: pass 'help' for a list)", "FEATURE-LIST" }, +@@ -78,6 +81,9 @@ static const GOptionEntry commandLineOptions[] = + { "use-wpe-platform-api", 0, 0, G_OPTION_ARG_NONE, &useWPEPlatformAPI, "Use the WPE platform API", nullptr }, + #endif { "version", 'v', 0, G_OPTION_ARG_NONE, &printVersion, "Print the WPE version", nullptr }, + { "inspector-pipe", 'v', 0, G_OPTION_ARG_NONE, &inspectorPipe, "Expose remote debugging protocol over pipe", nullptr }, + { "user-data-dir", 0, 0, G_OPTION_ARG_STRING, &userDataDir, "Default profile persistence folder location", "FILE" }, @@ -21999,7 +21985,7 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, nullptr, "[URL]" }, { nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr } }; -@@ -156,15 +162,38 @@ static void filterSavedCallback(WebKitUserContentFilterStore *store, GAsyncResul +@@ -205,15 +211,38 @@ static void filterSavedCallback(WebKitUserContentFilterStore *store, GAsyncResul g_main_loop_quit(data->mainLoop); } @@ -22038,11 +22024,11 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 + +static WebKitWebView* createWebViewImpl(WebKitWebView* webView, WebKitWebContext *webContext, gpointer user_data) { + auto backend = createViewBackend(1280, 720); - struct wpe_view_backend* wpeBackend = backend->backend(); -@@ -176,18 +205,37 @@ static WebKitWebView* createWebView(WebKitWebView* webView, WebKitNavigationActi - delete static_cast(data); - }, backend.release()); +@@ -229,18 +258,37 @@ static WebKitWebView* createWebView(WebKitWebView* webView, WebKitNavigationActi + }, backend.release()); + } - auto* newWebView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, - "backend", viewBackend, @@ -22084,7 +22070,7 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 return newWebView; } -@@ -201,13 +249,89 @@ static WebKitFeature* findFeature(WebKitFeatureList* featureList, const char* id +@@ -254,13 +302,89 @@ static WebKitFeature* findFeature(WebKitFeatureList* featureList, const char* id return nullptr; } @@ -22175,7 +22161,7 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 webkit_network_session_set_itp_enabled(networkSession, enableITP); if (proxy) { -@@ -234,10 +358,18 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -287,10 +411,18 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* webkit_cookie_manager_set_persistent_storage(cookieManager, cookiesFile, storageType); } } @@ -22196,7 +22182,7 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 webkit_website_data_manager_set_itp_enabled(manager, enableITP); if (proxy) { -@@ -268,6 +400,7 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -321,6 +453,7 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* } #endif @@ -22204,9 +22190,9 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 WebKitUserContentManager* userContentManager = nullptr; if (contentFilter) { GFile* contentFilterFile = g_file_new_for_commandline_arg(contentFilter); -@@ -335,6 +468,15 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -388,6 +521,15 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* delete static_cast(data); - }, backend); + }, backend) : nullptr; +// Playwright begin + if (headlessMode) { @@ -22220,8 +22206,8 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 auto* webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, "backend", viewBackend, "web-context", webContext, -@@ -354,8 +496,6 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* - backend->setAccessibleChild(ATK_OBJECT(accessible)); +@@ -414,8 +556,6 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* + g_signal_connect(wpeView, "event", G_CALLBACK(wpeViewEventCallback), webView); #endif - openViews = g_hash_table_new_full(nullptr, nullptr, g_object_unref, nullptr); @@ -22229,7 +22215,7 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 webkit_web_context_set_automation_allowed(webContext, automationMode); g_signal_connect(webContext, "automation-started", G_CALLBACK(automationStartedCallback), webView); g_signal_connect(webView, "permission-request", G_CALLBACK(decidePermissionRequest), nullptr); -@@ -368,16 +508,9 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* +@@ -428,16 +568,9 @@ static void activate(GApplication* application, WPEToolingBackends::ViewBackend* webkit_web_view_set_background_color(webView, &color); if (uriArguments) { @@ -22249,8 +22235,8 @@ index 1d3cb1aeb14068a69cee73a60ce82cfb059322ba..62d47a408869ec476a3c46b5af9fd850 webkit_web_view_load_uri(webView, "about:blank"); else webkit_web_view_load_uri(webView, "https://wpewebkit.org"); -@@ -449,8 +582,14 @@ int main(int argc, char *argv[]) - return 1; +@@ -511,8 +644,14 @@ int main(int argc, char *argv[]) + } } + openViews = g_hash_table_new_full(nullptr, nullptr, g_object_unref, nullptr); @@ -22277,10 +22263,10 @@ index 1067b31bc989748dfcc5502209d36d001b9b239e..7629263fb8bc93dca6dfc01c75eed8d2 + add_subdirectory(Playwright/win) +endif () diff --git a/Tools/Scripts/build-webkit b/Tools/Scripts/build-webkit -index e4cc9fe0cb259211f8fd297885b76539425f7193..8d962dc3c07494a98606aaf07eaacf199cf00fc5 100755 +index 6d42f5c814da1aeac83fc336f20a6e585322cec2..d8423597af92e63422dca0bae1129e7e59e14b77 100755 --- a/Tools/Scripts/build-webkit +++ b/Tools/Scripts/build-webkit -@@ -272,7 +272,7 @@ if (isAppleCocoaWebKit()) { +@@ -273,7 +273,7 @@ if (isAppleCocoaWebKit()) { push @projects, ("Source/WebKit"); if (!isEmbeddedWebKit()) { @@ -22305,22 +22291,22 @@ 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 6b905b186bba5d23a6e968404a4d26d475b087af..a712c4631bc185754862f58dd6941b338144bf01 100644 +index 058d5d3029e2facd15d7cc7a60f499d3139d6e8d..7ac58fe41cee5577e6ce25e437f0db41246da214 100644 --- a/Tools/WebKitTestRunner/TestController.cpp +++ b/Tools/WebKitTestRunner/TestController.cpp -@@ -959,6 +959,7 @@ void TestController::createWebViewWithOptions(const TestOptions& options) +@@ -962,6 +962,7 @@ void TestController::createWebViewWithOptions(const TestOptions& options) 0, // requestStorageAccessConfirm shouldAllowDeviceOrientationAndMotionAccess, runWebAuthenticationPanel, + 0, // handleJavaScriptDialog 0, decidePolicyForMediaKeySystemPermissionRequest, - nullptr, // requestWebAuthenticationNoGesture + queryPermission, diff --git a/Tools/WebKitTestRunner/mac/EventSenderProxy.mm b/Tools/WebKitTestRunner/mac/EventSenderProxy.mm -index 397cacf02ea61a9463050870216251f5a2ef7b06..6d1b612e25ecf4bf4a80878d265844f668aa2ca3 100644 +index c7c925b66ea04d8f7a44fb7410ad45acb74f022f..b4738697e5ecb5c947d2bc1864a0f3907d9a3795 100644 --- a/Tools/WebKitTestRunner/mac/EventSenderProxy.mm +++ b/Tools/WebKitTestRunner/mac/EventSenderProxy.mm -@@ -907,4 +907,51 @@ void EventSenderProxy::scaleGestureEnd(double scale) +@@ -950,4 +950,51 @@ void EventSenderProxy::scaleGestureEnd(double scale) #endif // ENABLE(MAC_GESTURE_EVENTS) @@ -22373,7 +22359,7 @@ index 397cacf02ea61a9463050870216251f5a2ef7b06..6d1b612e25ecf4bf4a80878d265844f6 + } // namespace WTR diff --git a/Tools/glib/dependencies/apt b/Tools/glib/dependencies/apt -index 9f05f879dbd29d93dd189ee05925d1686c6fcaa4..b4c013d0267b77b2bdbcf4d70eb6522168a2969e 100644 +index 7ae5cb94f966df2b730fc54489abf1d8174ed390..c1baaeb0a411ad80d9f6771bd8a3811c4d71d337 100644 --- a/Tools/glib/dependencies/apt +++ b/Tools/glib/dependencies/apt @@ -1,11 +1,11 @@ @@ -22404,11 +22390,11 @@ index 9f05f879dbd29d93dd189ee05925d1686c6fcaa4..b4c013d0267b77b2bdbcf4d70eb65221 # These are dependencies necessary for running tests. diff --git a/Tools/gtk/dependencies/apt b/Tools/gtk/dependencies/apt -index 30d4fb51cbf8d2c1e18132209ffaf6256ed7019d..7df5fe173e3f2d96864245a61ec6d8748b10d976 100644 +index 1e2a9202f88c63afa6525d97d0f945438f5f2032..e6cdee08aff723eb5c6b16491051ca39dfee3f5b 100644 --- a/Tools/gtk/dependencies/apt +++ b/Tools/gtk/dependencies/apt -@@ -37,6 +37,9 @@ PACKAGES+=( - nasm +@@ -33,6 +33,9 @@ PACKAGES+=( + libxtst-dev unifdef xfonts-utils + $(aptIfElse libenchant-dev libenchant-2-dev) @@ -22417,11 +22403,29 @@ index 30d4fb51cbf8d2c1e18132209ffaf6256ed7019d..7df5fe173e3f2d96864245a61ec6d874 # These are dependencies necessary for running tests. cups-daemon +diff --git a/Tools/gtk/jhbuild.modules b/Tools/gtk/jhbuild.modules +index 424963da44676b4bfa394aa054ce08a791099fe5..3e075a6ff3ab18da0999de609192dceb82e664b6 100644 +--- a/Tools/gtk/jhbuild.modules ++++ b/Tools/gtk/jhbuild.modules +@@ -253,10 +253,10 @@ + + + +- ++ hash="sha256:83673c685b910fb7d39f1f28eee5afbefb71c05798fc350ac3bf1b885e1efaa1"> + + + diff --git a/Tools/jhbuild/jhbuild-minimal.modules b/Tools/jhbuild/jhbuild-minimal.modules -index 411cda9ffd07e5c58477afcdfb2637d4c9bb3392..e762744314b5982c20e9626878d728d748b3cd2d 100644 +index 9c4870249367d0c0fb8c08cee660cfd63a6cb8de..496c35c5bd3d3d3c40ec7fc050182c70734c4f84 100644 --- a/Tools/jhbuild/jhbuild-minimal.modules +++ b/Tools/jhbuild/jhbuild-minimal.modules -@@ -24,7 +24,6 @@ +@@ -26,7 +26,6 @@ @@ -22429,6 +22433,20 @@ index 411cda9ffd07e5c58477afcdfb2637d4c9bb3392..e762744314b5982c20e9626878d728d7 +@@ -122,10 +121,10 @@ + + + +- ++ hash="sha256:83673c685b910fb7d39f1f28eee5afbefb71c05798fc350ac3bf1b885e1efaa1"> + + + diff --git a/Tools/wpe/backends/fdo/HeadlessViewBackendFdo.cpp b/Tools/wpe/backends/fdo/HeadlessViewBackendFdo.cpp index 4b3d262528d33800ac46e4e9fc342b11f2744979..39d72bd2c04e79b94a5c7634b6abc9b227d5c148 100644 --- a/Tools/wpe/backends/fdo/HeadlessViewBackendFdo.cpp @@ -22484,6 +22502,24 @@ index 4b3d262528d33800ac46e4e9fc342b11f2744979..39d72bd2c04e79b94a5c7634b6abc9b2 static cairo_user_data_key_t bufferKey; cairo_surface_set_user_data(m_snapshot, &bufferKey, buffer, +diff --git a/Tools/wpe/jhbuild.modules b/Tools/wpe/jhbuild.modules +index e8e82cc7b122bba731e33472742f3d55319dc5a7..85e5f447133048f8db119c5edcfd7c69466fb4b9 100644 +--- a/Tools/wpe/jhbuild.modules ++++ b/Tools/wpe/jhbuild.modules +@@ -102,10 +102,10 @@ + + + +- ++ hash="sha256:83673c685b910fb7d39f1f28eee5afbefb71c05798fc350ac3bf1b885e1efaa1"> + + + diff --git a/WebKit.xcworkspace/contents.xcworkspacedata b/WebKit.xcworkspace/contents.xcworkspacedata index a0f02e61be7e667c6ba164ca578109af36ac28d9..8f5af2e82c167ba6798fb7fde24a9f641f6554a5 100644 --- a/WebKit.xcworkspace/contents.xcworkspacedata diff --git a/playwright/docs/src/api-testing-js.md b/playwright/docs/src/api-testing-js.md index 03e0e6dfcc..680b085a3c 100644 --- a/playwright/docs/src/api-testing-js.md +++ b/playwright/docs/src/api-testing-js.md @@ -331,7 +331,7 @@ test('context request will share cookie storage with its browser context', async [name, value]) )).toEqual(responseCookies); - route.fulfill({ + await route.fulfill({ response, headers: { ...responseHeaders, foo: 'bar' }, }); @@ -375,7 +375,7 @@ test('global context request has isolated cookie storage', async ({ new Map(contextCookies2.map(({ name, value }) => [name, value])) ).toEqual(responseCookies); - route.fulfill({ + await route.fulfill({ response, headers: { ...responseHeaders, foo: 'bar' }, }); diff --git a/playwright/docs/src/api/class-android.md b/playwright/docs/src/api/class-android.md index 6f6d4c7ed1..c889ee5d0b 100644 --- a/playwright/docs/src/api/class-android.md +++ b/playwright/docs/src/api/class-android.md @@ -74,12 +74,6 @@ const { _android: android } = require('playwright'); })(); ``` -Note that since you don't need Playwright to install web browsers when testing Android, you can omit browser download via setting the following environment variable when installing Playwright: - -```bash js -PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright -``` - ## async method: Android.connect * since: v1.28 - returns: <[AndroidDevice]> diff --git a/playwright/docs/src/api/class-browsercontext.md b/playwright/docs/src/api/class-browsercontext.md index 98a1c7d392..87f6369b5c 100644 --- a/playwright/docs/src/api/class-browsercontext.md +++ b/playwright/docs/src/api/class-browsercontext.md @@ -106,7 +106,7 @@ The reason to be reported to the operations interrupted by the context closure. - alias-java: consoleMessage - argument: <[ConsoleMessage]> -Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also emitted if the page throws an error or a warning. +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 [ConsoleMessage] event handler argument. @@ -1120,11 +1120,11 @@ await browser.CloseAsync(); It is possible to examine the request to decide the route action. For example, mocking all requests that contain some post data, and leaving all other requests as is: ```js -await context.route('/api/**', route => { +await context.route('/api/**', async route => { if (route.request().postData().includes('my-string')) - route.fulfill({ body: 'mocked-data' }); + await route.fulfill({ body: 'mocked-data' }); else - route.continue(); + await route.continue(); }); ``` @@ -1138,16 +1138,16 @@ context.route("/api/**", route -> { ``` ```python async -def handle_route(route): +async def handle_route(route: Route): if ("my-string" in route.request.post_data): - route.fulfill(body="mocked-data") + await route.fulfill(body="mocked-data") else: - route.continue_() + await route.continue_() await context.route("/api/**", handle_route) ``` ```python sync -def handle_route(route): +def handle_route(route: Route): if ("my-string" in route.request.post_data): route.fulfill(body="mocked-data") else: diff --git a/playwright/docs/src/api/class-electron.md b/playwright/docs/src/api/class-electron.md index b21d8573b0..d9d32f2c08 100644 --- a/playwright/docs/src/api/class-electron.md +++ b/playwright/docs/src/api/class-electron.md @@ -40,12 +40,6 @@ const { _electron: electron } = require('playwright'); })(); ``` -Note that since you don't need Playwright to install web browsers when testing Electron, you can omit browser download via setting the following environment variable when installing Playwright: - -```bash js -PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright -``` - **Supported Electron versions are:** * v12.2.0+ * v13.4.0+ diff --git a/playwright/docs/src/api/class-electronapplication.md b/playwright/docs/src/api/class-electronapplication.md index 06de31dce4..87e7975b35 100644 --- a/playwright/docs/src/api/class-electronapplication.md +++ b/playwright/docs/src/api/class-electronapplication.md @@ -39,7 +39,27 @@ const { _electron: electron } = require('playwright'); ## event: ElectronApplication.close * since: v1.9 -This event is issued when the application closes. +This event is issued when the application process has been terminated. + +## event: ElectronApplication.console +* since: v1.42 +- argument: <[ConsoleMessage]> + +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 [ConsoleMessage] event handler argument. + +**Usage** + +```js +electronApp.on('console', async msg => { + const values = []; + for (const arg of msg.args()) + values.push(await arg.jsonValue()); + console.log(...values); +}); +await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' })); +``` ## event: ElectronApplication.window * since: v1.9 diff --git a/playwright/docs/src/api/class-elementhandle.md b/playwright/docs/src/api/class-elementhandle.md index 2ee1a77d4e..6d727f675a 100644 --- a/playwright/docs/src/api/class-elementhandle.md +++ b/playwright/docs/src/api/class-elementhandle.md @@ -699,7 +699,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i If [`param: 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"` or `key: "Control+Shift+T"` are supported as well. When specified with the +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. ### param: ElementHandle.press.key diff --git a/playwright/docs/src/api/class-frame.md b/playwright/docs/src/api/class-frame.md index 336ba55bd3..8bf0530dda 100644 --- a/playwright/docs/src/api/class-frame.md +++ b/playwright/docs/src/api/class-frame.md @@ -1400,7 +1400,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i If [`param: 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"` or `key: "Control+Shift+T"` are supported as well. When specified with the +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. ### param: Frame.press.selector = %%-input-selector-%% diff --git a/playwright/docs/src/api/class-keyboard.md b/playwright/docs/src/api/class-keyboard.md index 87ba51ec37..a37869df75 100644 --- a/playwright/docs/src/api/class-keyboard.md +++ b/playwright/docs/src/api/class-keyboard.md @@ -234,7 +234,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i If [`param: 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"` or `key: "Control+Shift+T"` are supported as well. When specified with the +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. **Usage** diff --git a/playwright/docs/src/api/class-locator.md b/playwright/docs/src/api/class-locator.md index f59e0f09f3..bea8507def 100644 --- a/playwright/docs/src/api/class-locator.md +++ b/playwright/docs/src/api/class-locator.md @@ -1720,7 +1720,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i If [`param: 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"` or `key: "Control+Shift+T"` are supported as well. When specified with the +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. ### param: Locator.press.key diff --git a/playwright/docs/src/api/class-page.md b/playwright/docs/src/api/class-page.md index 635f49ebba..922a7df3bf 100644 --- a/playwright/docs/src/api/class-page.md +++ b/playwright/docs/src/api/class-page.md @@ -163,7 +163,7 @@ Emitted when the page closes. - alias-java: consoleMessage - argument: <[ConsoleMessage]> -Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also emitted if the page throws an error or a warning. +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 [ConsoleMessage] event handler argument. @@ -2988,6 +2988,18 @@ Give any CSS `@page` size declared in the page priority over what is declared in [`option: height`] or [`option: format`] options. Defaults to `false`, which will scale the content to fit the paper size. +### option: Page.pdf.tagged +* since: v1.42 +- `tagged` <[boolean]> + +Whether or not to generate tagged (accessible) PDF. Defaults to `false`. + +### option: Page.pdf.outline +* since: v1.42 +- `outline` <[boolean]> + +Whether or not to embed the document outline into the PDF. Defaults to `false`. + ## async method: Page.press * since: v1.8 * discouraged: Use locator-based [`method: Locator.press`] instead. Read more about [locators](../locators.md). @@ -3009,7 +3021,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i If [`param: 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"` or `key: "Control+Shift+T"` are supported as well. When specified with the +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. **Usage** @@ -3130,6 +3142,228 @@ return value resolves to `[]`. ### param: Page.querySelectorAll.selector = %%-query-selector-%% * since: v1.9 + +## async method: Page.addLocatorHandler +* since: v1.42 + +Sometimes, the web page can show an overlay that obstructs elements behind it and prevents certain actions, like click, from completing. When such an overlay is shown predictably, we recommend dismissing it as a part of your test flow. However, sometimes such an overlay may appear non-deterministically, for example certain cookies consent dialogs behave this way. In this case, [`method: Page.addLocatorHandler`] allows handling an overlay during an action that it would block. + +This method registers a handler for an overlay that is executed once the locator is visible on the page. The handler should get rid of the overlay so that actions blocked by it can proceed. This is useful for nondeterministic interstitial pages or dialogs, like a cookie consent dialog. + +Note that execution time of the handler counts towards the timeout of the action/assertion that executed the handler. + +You can register multiple handlers. However, only a single handler will be running at a time. Any actions inside a handler must not require another handler to run. + +:::warning +Running the interceptor will alter your page state mid-test. For example it will change the currently focused element and move the mouse. Make sure that the actions that run after the interceptor are self-contained and do not rely on the focus and mouse state. +
+
+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 methods like [`method: Locator.click`] that are self-contained. +::: + +**Usage** + +An example that closes a cookie dialog when it appears: + +```js +// Setup the handler. +await page.addLocatorHandler(page.getByRole('button', { name: 'Accept all cookies' }), async () => { + await page.getByRole('button', { name: 'Reject all cookies' }).click(); +}); + +// Write the test as usual. +await page.goto('https://example.com'); +await page.getByRole('button', { name: 'Start here' }).click(); +``` + +```java +// Setup the handler. +page.addLocatorHandler(page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Accept all cookies")), () => { + page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Reject all cookies")).click(); +}); + +// Write the test as usual. +page.goto("https://example.com"); +page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); +``` + +```python sync +# Setup the handler. +def handler(): + page.get_by_role("button", name="Reject all cookies").click() +page.add_locator_handler(page.get_by_role("button", name="Accept all cookies"), handler) + +# Write the test as usual. +page.goto("https://example.com") +page.get_by_role("button", name="Start here").click() +``` + +```python async +# Setup the handler. +def handler(): + await page.get_by_role("button", name="Reject all cookies").click() +await page.add_locator_handler(page.get_by_role("button", name="Accept all cookies"), handler) + +# Write the test as usual. +await page.goto("https://example.com") +await page.get_by_role("button", name="Start here").click() +``` + +```csharp +// Setup the handler. +await page.AddLocatorHandlerAsync(page.GetByRole(AriaRole.Button, new() { Name = "Accept all cookies" }), async () => { + await page.GetByRole(AriaRole.Button, new() { Name = "Reject all cookies" }).ClickAsync(); +}); + +// Write the test as usual. +await page.GotoAsync("https://example.com"); +await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync(); +``` + +An example that skips the "Confirm your security details" page when it is shown: + +```js +// Setup the handler. +await page.addLocatorHandler(page.getByText('Confirm your security details'), async () => { + await page.getByRole('button', 'Remind me later').click(); +}); + +// Write the test as usual. +await page.goto('https://example.com'); +await page.getByRole('button', { name: 'Start here' }).click(); +``` + +```java +// Setup the handler. +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.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); +``` + +```python sync +# Setup the handler. +def handler(): + page.get_by_role("button", name="Remind me later").click() +page.add_locator_handler(page.get_by_text("Confirm your security details"), handler) + +# Write the test as usual. +page.goto("https://example.com") +page.get_by_role("button", name="Start here").click() +``` + +```python async +# Setup the handler. +def handler(): + await page.get_by_role("button", name="Remind me later").click() +await page.add_locator_handler(page.get_by_text("Confirm your security details"), handler) + +# Write the test as usual. +await page.goto("https://example.com") +await page.get_by_role("button", name="Start here").click() +``` + +```csharp +// Setup the handler. +await page.AddLocatorHandlerAsync(page.GetByText("Confirm your security details"), async () => { + await page.GetByRole(AriaRole.Button, new() { Name = "Remind me later" }).ClickAsync(); +}); + +// Write the test as usual. +await page.GotoAsync("https://example.com"); +await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync(); +``` + +An example with a custom callback on every actionability check. It uses a `` locator that is always visible, so the handler is called before every actionability check: + +```js +// Setup the handler. +await page.addLocatorHandler(page.locator('body'), async () => { + await page.evaluate(() => window.removeObstructionsForTestIfNeeded()); +}); + +// Write the test as usual. +await page.goto('https://example.com'); +await page.getByRole('button', { name: 'Start here' }).click(); +``` + +```java +// Setup the handler. +page.addLocatorHandler(page.locator("body")), () => { + page.evaluate("window.removeObstructionsForTestIfNeeded()"); +}); + +// Write the test as usual. +page.goto("https://example.com"); +page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); +``` + +```python sync +# Setup the handler. +def handler(): + page.evaluate("window.removeObstructionsForTestIfNeeded()") +page.add_locator_handler(page.locator("body"), handler) + +# Write the test as usual. +page.goto("https://example.com") +page.get_by_role("button", name="Start here").click() +``` + +```python async +# Setup the handler. +def handler(): + await page.evaluate("window.removeObstructionsForTestIfNeeded()") +await page.add_locator_handler(page.locator("body"), handler) + +# Write the test as usual. +await page.goto("https://example.com") +await page.get_by_role("button", name="Start here").click() +``` + +```csharp +// Setup the handler. +await page.AddLocatorHandlerAsync(page.Locator("body"), async () => { + await page.EvaluateAsync("window.removeObstructionsForTestIfNeeded()"); +}); + +// Write the test as usual. +await page.GotoAsync("https://example.com"); +await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync(); +``` + +### param: Page.addLocatorHandler.locator +* since: v1.42 +- `locator` <[Locator]> + +Locator that triggers the handler. + +### param: Page.addLocatorHandler.handler +* langs: js, python +* since: v1.42 +- `handler` <[function]> + +Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. + +### param: Page.addLocatorHandler.handler +* langs: csharp +* since: v1.42 +- `handler` <[function](): [Promise]> + +Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. + +### param: Page.addLocatorHandler.handler +* langs: java +* since: v1.42 +- `handler` <[Runnable]> + +Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click. + ## async method: Page.reload * since: v1.8 - returns: <[null]|[Response]> @@ -3248,11 +3482,11 @@ await page.GotoAsync("https://www.microsoft.com"); It is possible to examine the request to decide the route action. For example, mocking all requests that contain some post data, and leaving all other requests as is: ```js -await page.route('/api/**', route => { +await page.route('/api/**', async route => { if (route.request().postData().includes('my-string')) - route.fulfill({ body: 'mocked-data' }); + await route.fulfill({ body: 'mocked-data' }); else - route.continue(); + await route.continue(); }); ``` @@ -3266,16 +3500,16 @@ page.route("/api/**", route -> { ``` ```python async -def handle_route(route): +async def handle_route(route: Route): if ("my-string" in route.request.post_data): - route.fulfill(body="mocked-data") + await route.fulfill(body="mocked-data") else: - route.continue_() + await route.continue_() await page.route("/api/**", handle_route) ``` ```python sync -def handle_route(route): +def handle_route(route: Route): if ("my-string" in route.request.post_data): route.fulfill(body="mocked-data") else: diff --git a/playwright/docs/src/api/class-route.md b/playwright/docs/src/api/class-route.md index f55843ef5d..dcdca4705a 100644 --- a/playwright/docs/src/api/class-route.md +++ b/playwright/docs/src/api/class-route.md @@ -92,11 +92,11 @@ page.route("**/*", handle) ``` ```csharp -await page.RouteAsync("**/*", route => +await page.RouteAsync("**/*", async route => { var headers = new Dictionary(route.Request.Headers) { { "foo", "bar" } }; headers.Remove("origin"); - route.ContinueAsync(headers); + await route.ContinueAsync(new() { Headers = headers }); }); ``` @@ -264,17 +264,17 @@ page.route("**/*", route -> { ```python async # Handle GET requests. -def handle_get(route): +async def handle_get(route): if route.request.method != "GET": - route.fallback() + await route.fallback() return # Handling GET only. # ... # Handle POST requests. -def handle_post(route): +async def handle_post(route): if route.request.method != "POST": - route.fallback() + await route.fallback() return # Handling POST only. # ... @@ -378,11 +378,11 @@ page.route("**/*", handle) ``` ```csharp -await page.RouteAsync("**/*", route => +await page.RouteAsync("**/*", async route => { var headers = new Dictionary(route.Request.Headers) { { "foo", "foo-value" } }; headers.Remove("bar"); - route.FallbackAsync(headers); + await route.FallbackAsync(new() { Headers = headers }); }); ``` diff --git a/playwright/docs/src/auth.md b/playwright/docs/src/auth.md index 6bfbf146d4..df03d89c60 100644 --- a/playwright/docs/src/auth.md +++ b/playwright/docs/src/auth.md @@ -575,7 +575,7 @@ Reusing authenticated state covers [cookies](https://developer.mozilla.org/en-US ```js // Get session storage and store as env variable const sessionStorage = await page.evaluate(() => JSON.stringify(sessionStorage)); -fs.writeFileSync('playwright/.auth/session.json', JSON.stringify(sessionStorage), 'utf-8'); +fs.writeFileSync('playwright/.auth/session.json', sessionStorage, 'utf-8'); // Set session storage in a new context const sessionStorage = JSON.parse(fs.readFileSync('playwright/.auth/session.json', 'utf-8')); diff --git a/playwright/docs/src/ci-intro.md b/playwright/docs/src/ci-intro.md index 98c8f6e825..f5393cfc9e 100644 --- a/playwright/docs/src/ci-intro.md +++ b/playwright/docs/src/ci-intro.md @@ -8,7 +8,7 @@ title: "CI GitHub Actions" Playwright tests can be run on any CI provider. In this section we will cover running tests on GitHub using GitHub actions. If you would like to see how to configure other CI providers check out our detailed [doc on Continuous Integration](./ci.md). -When [installing Playwright](./intro.md) using the [VS Code extension](./getting-started-vscode.md) or with `npm init playwright@latest` you are given the option to add a [GitHub Actions](https://docs.github.com/en/actions). This creates a `playwright.yml` file inside a `.github/workflows` folder containing everything you need so that your tests run on each push and pull request into the main/master branch. +When [installing Playwright](./intro.md) using the [VS Code extension](./getting-started-vscode.md) or with `npm init playwright@latest` you are given the option to add a [GitHub Actions](https://docs.github.com/en/actions) workflow. This creates a `playwright.yml` file inside a `.github/workflows` folder containing everything you need so that your tests run on each push and pull request into the main/master branch. #### You will learn * langs: js @@ -54,8 +54,8 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - name: Install dependencies @@ -64,7 +64,7 @@ jobs: run: npx playwright install --with-deps - name: Run Playwright tests run: npx playwright test - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: playwright-report @@ -89,7 +89,7 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: @@ -102,7 +102,7 @@ jobs: run: python -m playwright install --with-deps - name: Run your tests run: pytest --tracing=retain-on-failure - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: playwright-traces @@ -121,7 +121,7 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'temurin' @@ -146,7 +146,7 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup dotnet uses: actions/setup-dotnet@v3 with: @@ -181,8 +181,8 @@ jobs: container: image: mcr.microsoft.com/playwright:v%%VERSION%%-jammy steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - name: Install dependencies @@ -207,7 +207,7 @@ jobs: container: image: mcr.microsoft.com/playwright/python:v%%VERSION%%-jammy steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: @@ -237,7 +237,7 @@ jobs: container: image: mcr.microsoft.com/playwright/java:v%%VERSION%%-jammy steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'temurin' @@ -264,7 +264,7 @@ jobs: container: image: mcr.microsoft.com/playwright/dotnet:v%%VERSION%%-jammy steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup dotnet uses: actions/setup-dotnet@v3 with: @@ -291,8 +291,8 @@ jobs: runs-on: ubuntu-latest if: github.event.deployment_status.state == 'success' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - name: Install dependencies @@ -315,7 +315,7 @@ jobs: runs-on: ubuntu-latest if: github.event.deployment_status.state == 'success' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 uses: actions/setup-python@v4 with: python-version: '3.11' @@ -342,7 +342,7 @@ jobs: runs-on: ubuntu-latest if: github.event.deployment_status.state == 'success' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: 'temurin' @@ -368,7 +368,7 @@ jobs: runs-on: ubuntu-latest if: github.event.deployment_status.state == 'success' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup dotnet uses: actions/setup-dotnet@v3 with: diff --git a/playwright/docs/src/ci.md b/playwright/docs/src/ci.md index c5d131b3b7..818e08242a 100644 --- a/playwright/docs/src/ci.md +++ b/playwright/docs/src/ci.md @@ -680,7 +680,7 @@ await playwright.Chromium.LaunchAsync(new() }); ``` -On Linux agents, headed execution requires [Xvfb](https://en.wikipedia.org/wiki/Xvfb) to be installed. Our [Docker image](./docker.md) and GitHub Action have Xvfb pre-installed. To run browsers in headed mode with Xvfb, add `xvfb-run` before the Node.js command. +On Linux agents, headed execution requires [Xvfb](https://en.wikipedia.org/wiki/Xvfb) to be installed. Our [Docker image](./docker.md) and GitHub Action have Xvfb pre-installed. To run browsers in headed mode with Xvfb, add `xvfb-run` before the actual command. ```bash js xvfb-run node index.js diff --git a/playwright/docs/src/docker.md b/playwright/docs/src/docker.md index 98e3f4d860..f0ec7d8621 100644 --- a/playwright/docs/src/docker.md +++ b/playwright/docs/src/docker.md @@ -5,7 +5,7 @@ title: "Docker" ## Introduction -[Dockerfile.jammy] can be used to run Playwright scripts in Docker environment. These image includes all the dependencies needed to run browsers in a Docker container, and also include the browsers themselves. +[Dockerfile.jammy] can be used to run Playwright scripts in Docker environment. These image includes the [Playwright browsers](./browsers.md#install-browsers) and [browser system dependencies](./browsers.md#install-system-dependencies). The Playwright package/dependency is not included in the image and should be installed separately. ## Usage @@ -107,15 +107,12 @@ See our [Continuous Integration guides](./ci.md) for sample configs. See [all available image tags]. -Docker images are published automatically by GitHub Actions. We currently publish images with the -following tags: +We currently publish images with the following tags: - `:next` - tip-of-tree image version based on Ubuntu 22.04 LTS (Jammy Jellyfish). - `:next-jammy` - tip-of-tree image version based on Ubuntu 22.04 LTS (Jammy Jellyfish). - `:v%%VERSION%%` - Playwright v%%VERSION%% release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish). - `:v%%VERSION%%-jammy` - Playwright v%%VERSION%% release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish). - `:v%%VERSION%%-focal` - Playwright v%%VERSION%% release docker image based on Ubuntu 20.04 LTS (Focal Fossa). -- `:sha-XXXXXXX` - docker image for every commit that changed - docker files or browsers, marked with a [short sha](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#Short-SHA-1) (first 7 digits of the SHA commit). :::note It is recommended to always pin your Docker image to a specific version if possible. If the Playwright version in your Docker image does not match the version in your project/tests, Playwright will be unable to locate browser executables. @@ -140,11 +137,9 @@ You can use the [.NET install script](https://learn.microsoft.com/en-us/dotnet/c curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --install-dir /usr/share/dotnet --channel 6.0 ``` -## Development +## Build your own image * langs: js -### Build the image - To run Playwright inside Docker, you need to have Node.js, [Playwright browsers](./browsers.md#install-browsers) and [browser system dependencies](./browsers.md#install-system-dependencies) installed. See the following Dockerfile: ```Dockerfile @@ -153,15 +148,14 @@ FROM node:20-bookworm RUN npx -y playwright@%%VERSION%% install --with-deps ``` -Note: official images published to [Microsoft Artifact Registry] are built using [`//utils/docker/build.sh`](https://github.com/microsoft/playwright/blob/main/utils/docker/build.sh) script. +## Build your own image +* langs: python -```txt -./utils/docker/build.sh jammy playwright:localbuild-jammy -``` +To run Playwright inside Docker, you need to have Python, [Playwright browsers](./browsers.md#install-browsers) and [browser system dependencies](./browsers.md#install-system-dependencies) installed. See the following Dockerfile: -The image will be tagged as `playwright:localbuild-jammy` and could be run as: +```Dockerfile +FROM python:3.12-bookworm -```txt -docker run --rm -it playwright:localbuild /bin/bash +RUN pip install playwright==@%%VERSION%% && \ + playwright install --with-deps ``` - diff --git a/playwright/docs/src/emulation.md b/playwright/docs/src/emulation.md index 65c81612f8..ad709cc308 100644 --- a/playwright/docs/src/emulation.md +++ b/playwright/docs/src/emulation.md @@ -101,7 +101,8 @@ Playwright can emulate various devices by specifying `setDeviceScaleFactor`, `se The viewport is included in the device but you can override it for some tests with [`method: Page.setViewportSize`]. ```js tab=js-test title="playwright.config.ts" -import { defineConfig } from '@playwright/test'; +import { defineConfig, devices } from '@playwright/test'; + export default defineConfig({ projects: [ { @@ -245,7 +246,7 @@ await using var context = await browser.NewContextAsync(new() Whether the meta viewport tag is taken into account and touch events are enabled. ```js title="playwright.config.ts" -import { defineConfig } from '@playwright/test'; +import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ projects: [ @@ -780,16 +781,16 @@ BrowserContext context = browser.newContext(new Browser.NewContextOptions() ```python async context = await browser.new_context( - javaScript_enabled=False + java_script_enabled=False ) ``` ```python sync context = browser.new_context( - javaScript_enabled=False + java_script_enabled=False ) ``` ```csharp -var context = await browser.NewContextAsync(new() { JavaScriptEnabled = true }); +var context = await browser.NewContextAsync(new() { JavaScriptEnabled = false }); ``` diff --git a/playwright/docs/src/intro-csharp.md b/playwright/docs/src/intro-csharp.md index 9281f886c9..43126bb63e 100644 --- a/playwright/docs/src/intro-csharp.md +++ b/playwright/docs/src/intro-csharp.md @@ -91,7 +91,7 @@ Edit the `UnitTest1.cs` file with the code below to create an example end-to-end }> -```csharp +```csharp title="UnitTest1.cs" using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Playwright; @@ -130,7 +130,7 @@ public class Tests : PageTest -```csharp +```csharp title="UnitTest1.cs" using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Playwright; diff --git a/playwright/docs/src/intro-js.md b/playwright/docs/src/intro-js.md index 635ba181d9..6114266907 100644 --- a/playwright/docs/src/intro-js.md +++ b/playwright/docs/src/intro-js.md @@ -115,6 +115,8 @@ To update Playwright to the latest version run the following command: ```bash npm install -D @playwright/test@latest +# Also download new browser binaries and their dependencies: +npx playwright install --with-deps ``` You can always check which version of Playwright you have by running the following command: @@ -124,7 +126,7 @@ npx playwright --version ## System requirements -- Node.js 16+ +- Node.js 18+ - Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL). - MacOS 12 Monterey, MacOS 13 Ventura, or MacOS 14 Sonoma. - Debian 11, Debian 12, Ubuntu 20.04 or Ubuntu 22.04, with x86-64 or arm64 architecture. diff --git a/playwright/docs/src/intro-python.md b/playwright/docs/src/intro-python.md index acedbed197..09a493ae83 100644 --- a/playwright/docs/src/intro-python.md +++ b/playwright/docs/src/intro-python.md @@ -6,14 +6,18 @@ title: "Installation" Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation. +The [Playwright library](./library.md) can be used as a general purpose browser automation tool, providing a powerful set of APIs to automate web applications, for both sync and async Python. + +This introduction describes the Playwright Pytest plugin, which is the recommended way to write end-to-end tests. + **You will learn** -- [How to install Playwright](/intro.md#installing-playwright) +- [How to install Playwright Pytest](/intro.md#installing-playwright-pytest) - [How to run the example test](/intro.md#running-the-example-test) -## Installing Playwright +## Installing Playwright Pytest -Playwright recommends using the official [Playwright Pytest plugin](./test-runners.md) to write end-to-end tests. It provides context isolation, running it on multiple browser configurations out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure with your preferred test-runner. The Pytest plugin utilizes the sync version of Playwright, there is also an async version accessible via the library. +Playwright recommends using the official [Playwright Pytest plugin](./test-runners.md) to write end-to-end tests. It provides context isolation, running it on multiple browser configurations out of the box. Get started by installing Playwright and running the example test to see it in action. diff --git a/playwright/docs/src/library-csharp.md b/playwright/docs/src/library-csharp.md index ff68f86fd9..5dab997f7e 100644 --- a/playwright/docs/src/library-csharp.md +++ b/playwright/docs/src/library-csharp.md @@ -66,6 +66,9 @@ You can do the following to leverage Playwright's web-first assertions when you using Microsoft.Playwright; using static Microsoft.Playwright.Assertions; +// Change the default 5 seconds timeout if you'd like. +SetDefaultExpectTimeout(10_000); + using var playwright = await Playwright.CreateAsync(); await using var browser = await playwright.Chromium.LaunchAsync(); var page = await browser.NewPageAsync(); diff --git a/playwright/docs/src/mock.md b/playwright/docs/src/mock.md index ee00410b79..b3ad4c860f 100644 --- a/playwright/docs/src/mock.md +++ b/playwright/docs/src/mock.md @@ -41,7 +41,7 @@ async def test_mock_the_fruit_api(page: Page): await page.goto("https://demo.playwright.dev/api-mocking") # Assert that the Strawberry fruit is visible - await page.get_by_text("Strawberry").to_be_visible() + await expect(page.get_by_text("Strawberry")).to_be_visible() ``` ```python sync @@ -58,7 +58,7 @@ def test_mock_the_fruit_api(page: Page): page.goto("https://demo.playwright.dev/api-mocking") # Assert that the Strawberry fruit is visible - page.get_by_text("Strawberry").to_be_visible() + expect(page.get_by_text("Strawberry")).to_be_visible() ``` ```csharp @@ -118,7 +118,7 @@ test('gets the json from api and adds a new fruit', async ({ page }) => { await page.route('*/**/api/v1/fruits', async route => { const response = await route.fetch(); const json = await response.json(); - json.push({ name: 'Playwright', id: 100 }); + json.push({ name: 'Loquat', id: 100 }); // Fulfill using the original response, while patching the response body // with the given JSON object. await route.fulfill({ response, json }); @@ -128,7 +128,7 @@ test('gets the json from api and adds a new fruit', async ({ page }) => { await page.goto('https://demo.playwright.dev/api-mocking'); // Assert that the new fruit is visible - await expect(page.getByText('Playwright', { exact: true })).toBeVisible(); + await expect(page.getByText('Loquat', { exact: true })).toBeVisible(); }); ``` @@ -137,18 +137,18 @@ async def test_gets_the_json_from_api_and_adds_a_new_fruit(page: Page): async def handle(route: Route): response = await route.fetch() json = await response.json() - json.append({ "name": "Playwright", "id": 100}) + json.append({ "name": "Loquat", "id": 100}) # Fulfill using the original response, while patching the response body # with the given JSON object. await route.fulfill(response=response, json=json) - await page.route("https://dog.ceo/api/breeds/list/all", handle) + await page.route("https://demo.playwright.dev/api-mocking/api/v1/fruits", handle) # Go to the page await page.goto("https://demo.playwright.dev/api-mocking") # Assert that the new fruit is visible - await page.get_by_text("Playwright", exact=True).to_be_visible() + await expect(page.get_by_text("Loquat", exact=True)).to_be_visible() ``` ```python sync @@ -156,39 +156,39 @@ def test_gets_the_json_from_api_and_adds_a_new_fruit(page: Page): def handle(route: Route): response = route.fetch() json = response.json() - json.append({ "name": "Playwright", "id": 100}) + json.append({ "name": "Loquat", "id": 100}) # Fulfill using the original response, while patching the response body # with the given JSON object. route.fulfill(response=response, json=json) - page.route("https://dog.ceo/api/breeds/list/all", handle) + page.route("https://demo.playwright.dev/api-mocking/api/v1/fruits", handle) # Go to the page page.goto("https://demo.playwright.dev/api-mocking") # Assert that the new fruit is visible - page.get_by_text("Playwright", exact=True).to_be_visible() + expect(page.get_by_text("Loquat", exact=True)).to_be_visible() ``` ```csharp await page.RouteAsync("*/**/api/v1/fruits", async (route) => { var response = await route.FetchAsync(); var fruits = await response.JsonAsync(); - fruits.Add(new Fruit() { Name = "Playwright", Id = 100 }); + fruits.Add(new Fruit() { Name = "Loquat", Id = 100 }); // Fulfill using the original response, while patching the response body // with the given JSON object. await route.FulfillAsync(new () { Response = response, - Json = json + Json = fruits }); } ); // Go to the page await page.GotoAsync("https://demo.playwright.dev/api-mocking"); -// Assert that the Strawberry fruit is visible -await Expect(page.GetByTextAsync("Playwright", new () { Exact = true })).ToBeVisibleAsync(); +// Assert that the Loquat fruit is visible +await Expect(page.GetByTextAsync("Loquat", new () { Exact = true })).ToBeVisibleAsync(); ``` ```java @@ -196,7 +196,7 @@ page.route("*/**/api/v1/fruits", route -> { Response response = route.fetch(); byte[] json = response.body(); parsed = new Gson().fromJson(json, JsonObject.class) - parsed.add(new JsonObject().add("name", "Playwright").add("id", 100)); + 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())); @@ -205,8 +205,8 @@ page.route("*/**/api/v1/fruits", route -> { // Go to the page page.goto("https://demo.playwright.dev/api-mocking"); -// Assert that the Strawberry fruit is visible -assertThat(page.getByText("Playwright", new Page.GetByTextOptions().setExact(true))).isVisible(); +// Assert that the Loquat fruit is visible +assertThat(page.getByText("Loquat", new Page.GetByTextOptions().setExact(true))).isVisible(); ``` In the trace of our test we can see that the API was called and the response was modified. @@ -249,7 +249,7 @@ test('records or updates the HAR file', async ({ page }) => { ``` ```python async -async def records_or_updates_the_har_file(page: Page): +async def test_records_or_updates_the_har_file(page: Page): # Get the response from the HAR file await page.route_from_har("./hars/fruit.har", url="*/**/api/v1/fruits", update=True) @@ -261,7 +261,7 @@ async def records_or_updates_the_har_file(page: Page): ``` ```python sync -def records_or_updates_the_har_file(page: Page): +def test_records_or_updates_the_har_file(page: Page): # Get the response from the HAR file page.route_from_har("./hars/fruit.har", url="*/**/api/v1/fruits", update=True) @@ -347,7 +347,7 @@ async def test_gets_the_json_from_har_and_checks_the_new_fruit_has_been_added(pa await page.goto("https://demo.playwright.dev/api-mocking") # Assert that the Playwright fruit is visible - await page.get_by_text("Playwright", exact=True).to_be_visible() + await expect(page.get_by_text("Playwright", exact=True)).to_be_visible() ``` ```python sync @@ -361,7 +361,7 @@ def test_gets_the_json_from_har_and_checks_the_new_fruit_has_been_added(page: Pa page.goto("https://demo.playwright.dev/api-mocking") # Assert that the Playwright fruit is visible - page.get_by_text("Playwright", exact=True).to_be_visible() + expect(page.get_by_text("Playwright", exact=True)).to_be_visible() ``` ```csharp @@ -425,7 +425,7 @@ mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="op ```bash python # Save API requests from example.com as "example.har" archive. -playwright open --save-har=example.har --save-har-glob="**/api/**" https://example.coms +playwright open --save-har=example.har --save-har-glob="**/api/**" https://example.com ``` ```bash csharp diff --git a/playwright/docs/src/network.md b/playwright/docs/src/network.md index 9f1a5b2cf2..438e929e0c 100644 --- a/playwright/docs/src/network.md +++ b/playwright/docs/src/network.md @@ -495,10 +495,10 @@ await page.GotoAsync("https://example.com"); ```js // Delete header -await page.route('**/*', route => { +await page.route('**/*', async route => { const headers = route.request().headers(); delete headers['X-Secret']; - route.continue({ headers }); + await route.continue({ headers }); }); // Continue requests as POST. @@ -522,7 +522,7 @@ page.route("**/*", route -> route.resume(new Route.ResumeOptions().setMethod("PO async def handle_route(route): headers = route.request.headers del headers["x-secret"] - route.continue_(headers=headers) + await route.continue_(headers=headers) await page.route("**/*", handle_route) # Continue requests as POST. @@ -617,7 +617,7 @@ await page.route('**/title.html', async route => { // Add a prefix to the title. let body = await response.text(); body = body.replace('', '<title>My prefix:'); - route.fulfill({ + await route.fulfill({ // Pass all fields from the response. response, // Override response body. diff --git a/playwright/docs/src/other-locators.md b/playwright/docs/src/other-locators.md index 2950d937b8..c37b97f6e6 100644 --- a/playwright/docs/src/other-locators.md +++ b/playwright/docs/src/other-locators.md @@ -884,11 +884,6 @@ await page.Locator("data-test-id=submit").ClickAsync(); Attribute selectors are not CSS selectors, so anything CSS-specific like `:enabled` is not supported. For more features, use a proper [css] selector, e.g. `css=[data-test="login"]:enabled`. ::: -:::note -Attribute selectors pierce shadow DOM. To opt-out from this behavior, use `:light` suffix after attribute, for example `page.locator('data-test-id:light=submit').click()` -::: - - ## Chaining selectors :::warning diff --git a/playwright/docs/src/puppeteer-js.md b/playwright/docs/src/puppeteer-js.md index 3c3f5d5c99..e620bed9ea 100644 --- a/playwright/docs/src/puppeteer-js.md +++ b/playwright/docs/src/puppeteer-js.md @@ -135,7 +135,7 @@ test.describe('Playwright homepage', () => { 1. Each Playwright Test file has explicit import of the `test` and `expect` functions 1. Test function is marked with `async` 1. Playwright Test is given a `page` as one of its parameters. This is one of the many [useful fixtures](./api/class-fixtures) in Playwright Test. -Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll#1`] and close it in [`method: Test.afterAll#1`]. +Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll`] and close it in [`method: Test.afterAll`]. 1. Locator creation with [`method: Page.locator`] is one of the few methods that is sync. 1. Use [assertions](./test-assertions) to verify the state instead of `page.$eval()`. diff --git a/playwright/docs/src/release-notes-js.md b/playwright/docs/src/release-notes-js.md index 5debb1c84c..3320a0d14e 100644 --- a/playwright/docs/src/release-notes-js.md +++ b/playwright/docs/src/release-notes-js.md @@ -6,6 +6,84 @@ toc_max_heading_level: 2 import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.42 + +### New APIs + +- New method [`method: Page.addLocatorHandler`] registers a callback that will be invoked when specified element becomes visible and may block Playwright actions. The callback can get rid of the overlay. Here is an example that closes a cookie dialog when it appears: +```js +// Setup the handler. +await page.addLocatorHandler( + page.getByRole('heading', { name: 'Hej! You are in control of your cookies.' }), + async () => { + await page.getByRole('button', { name: 'Accept all' }).click(); + }); +// Write the test as usual. +await page.goto('https://www.ikea.com/'); +await page.getByRole('link', { name: 'Collection of blue and white' }).click(); +await expect(page.getByRole('heading', { name: 'Light and easy' })).toBeVisible(); +``` + +- `expect(callback).toPass()` timeout can now be configured by `expect.toPass.timeout` option [globally](./api/class-testconfig#test-config-expect) or in [project config](./api/class-testproject#test-project-expect) + +- [`event: ElectronApplication.console`] event is emitted when Electron main process calls console API methods. +```js +electronApp.on('console', async msg => { + const values = []; + for (const arg of msg.args()) + values.push(await arg.jsonValue()); + console.log(...values); +}); +await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' })); +``` + +- [New syntax](./test-annotations#tag-tests) for adding tags to the tests (@-tokens in the test title are still supported): +```js +test('test customer login', { + tag: ['@fast', '@login'], +}, async ({ page }) => { + // ... +}); +``` + Use `--grep` command line option to run only tests with certain tags. +```sh +npx playwright test --grep @fast +``` + +- `--project` command line [flag](./test-cli#reference) now supports '*' wildcard: +```sh +npx playwright test --project='*mobile*' +``` + +- [New syntax](./test-annotations#annotate-tests) for test annotations: +```js +test('test full report', { + annotation: [ + { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23180' }, + { type: 'docs', description: 'https://playwright.dev/docs/test-annotations#tag-tests' }, + ], +}, async ({ page }) => { + // ... +}); +``` + +- [`method: Page.pdf`] accepts two new options [`tagged`](./api/class-page#page-pdf-option-tagged) and [`outline`](./api/class-page#page-pdf-option-outline). + +### Announcements + +* ⚠️ Ubuntu 18 is not supported anymore. + +### Browser Versions + +* Chromium 123.0.6312.4 +* Mozilla Firefox 123.0 +* WebKit 17.4 + +This version was also tested against the following stable channels: + +* Google Chrome 122 +* Microsoft Edge 123 + ## Version 1.41 ### New APIs @@ -1110,7 +1188,7 @@ Linux support looks like this: ### 🕵️ Anonymous Describe -It is now possible to call [`method: Test.describe#2`] to create suites without a title. This is useful for giving a group of tests a common option with [`method: Test.use`]. +It is now possible to call [`method: Test.describe`] to create suites without a title. This is useful for giving a group of tests a common option with [`method: Test.use`]. ```ts test.describe(() => { @@ -1233,16 +1311,16 @@ test.beforeEach(async ({ page }) => { await page.route('**/*', async route => { const headers = await route.request().allHeaders(); delete headers['if-none-match']; - route.fallback({ headers }); + await route.fallback({ headers }); }); }); test('should work', async ({ page }) => { - await page.route('**/*', route => { + await page.route('**/*', async route => { if (route.request().resourceType() === 'image') - route.abort(); + await route.abort(); else - route.fallback(); + await route.fallback(); }); }); ``` @@ -1520,7 +1598,7 @@ This version was also tested against the following stable channels: Read more in [our documentation](./test-assertions#soft-assertions) -- You can now specify a **custom error message** as a second argument to the `expect` and `expect.soft` functions, for example: +- You can now specify a **custom expect message** as a second argument to the `expect` and `expect.soft` functions, for example: ```js await expect(page.locator('text=Name'), 'should be logged in').toBeVisible(); @@ -1813,7 +1891,7 @@ test('response interception', async ({ page }) => { const response = await page._request.fetch(route.request()); const image = await jimp.read(await response.body()); await image.blur(5); - route.fulfill({ + await route.fulfill({ response, body: await image.getBufferAsync('image/jpeg'), }); diff --git a/playwright/docs/src/service-workers-experimental-network-events-js.md b/playwright/docs/src/service-workers-experimental-network-events-js.md index 96b8ead1e5..0928460b54 100644 --- a/playwright/docs/src/service-workers-experimental-network-events-js.md +++ b/playwright/docs/src/service-workers-experimental-network-events-js.md @@ -137,10 +137,12 @@ self.addEventListener('fetch', event => { (async () => { // 1. Try to first serve directly from caches const response = await caches.match(event.request); - if (response) return response; + if (response) + return response; // 2. Re-write request for /foo to /bar - if (event.request.url.endsWith('foo')) return fetch('./bar'); + if (event.request.url.endsWith('foo')) + return fetch('./bar'); // 3. Prevent tracker.js from being retrieved, and returns a placeholder response if (event.request.url.endsWith('tracker.js')) { diff --git a/playwright/docs/src/test-annotations-js.md b/playwright/docs/src/test-annotations-js.md index f9e8c0678c..68bdd79848 100644 --- a/playwright/docs/src/test-annotations-js.md +++ b/playwright/docs/src/test-annotations-js.md @@ -5,13 +5,17 @@ title: "Annotations" ## Introduction -Playwright Test supports test annotations to deal with failures, flakiness, skip, focus and tag tests: -- [`method: Test.skip#1`] marks the test as irrelevant. Playwright Test does not run such a test. Use this annotation when the test is not applicable in some configuration. -- [`method: Test.fail#1`] marks the test as failing. Playwright Test will run this test and ensure it does indeed fail. If the test does not fail, Playwright Test will complain. -- [`method: Test.fixme#1`] marks the test as failing. Playwright Test will not run this test, as opposed to the `fail` annotation. Use `fixme` when running the test is slow or crashes. -- [`method: Test.slow#1`] marks the test as slow and triples the test timeout. +Playwright supports tags and annotations that are displayed in the test report. -Annotations can be used on a single test or a group of tests. Annotations can be conditional, in which case they apply when the condition is truthy. Annotations may depend on test fixtures. There could be multiple annotations on the same test, possibly in different configurations. +You can add your own tags and annotations at any moment, but Playwright comes with a few built-in ones: +- [`method: Test.skip`] marks the test as irrelevant. Playwright does not run such a test. Use this annotation when the test is not applicable in some configuration. +- [`method: Test.fail`] marks the test as failing. Playwright will run this test and ensure it does indeed fail. If the test does not fail, Playwright will complain. +- [`method: Test.fixme`] marks the test as failing. Playwright will not run this test, as opposed to the `fail` annotation. Use `fixme` when running the test is slow or crashes. +- [`method: Test.slow`] marks the test as slow and triples the test timeout. + +Annotations can be added to a single test or a group of tests. + +Built-in annotations can be conditional, in which case they apply when the condition is truthy, and may depend on test fixtures. There could be multiple annotations on the same test, possibly in different configurations. ## Focus a test @@ -63,21 +67,45 @@ test.describe('two tests', () => { ## Tag tests -Sometimes you want to tag your tests as `@fast` or `@slow` and only run the tests that have the certain tag. We recommend that you use the `--grep` and `--grep-invert` command line flags for that: +Sometimes you want to tag your tests as `@fast` or `@slow`, and then filter by tag in the test report. Or you might want to only run tests that have a certain tag. + +To tag a test, either provide an additional details object when declaring a test, or add `@`-token to the test title. Note that tags must start with `@` symbol. ```js import { test, expect } from '@playwright/test'; -test('Test login page @fast', async ({ page }) => { +test('test login page', { + tag: '@fast', +}, async ({ page }) => { // ... }); -test('Test full report @slow', async ({ page }) => { +test('test full report @slow', async ({ page }) => { // ... }); ``` -You will then be able to run only that test: +You can also tag all tests in a group or provide multiple tags: + +```js +import { test, expect } from '@playwright/test'; + +test.describe('group', { + tag: '@report', +}, () => { + test('test report header', async ({ page }) => { + // ... + }); + + test('test full report', { + tag: ['@slow', '@vrt'], + }, async ({ page }) => { + // ... + }); +}); +``` + +You can now run tests that have a particular tag with [`--grep`](./test-cli.md#reference) command line option. ```bash tab=bash-bash npx playwright test --grep @fast @@ -125,6 +153,52 @@ Or run tests containing both tags (logical `AND` operator) using regex lookahead npx playwright test --grep "(?=.*@fast)(?=.*@slow)" ``` +You can also filter tests in the configuration file via [`property: TestConfig.grep`] and [`property: TestProject.grep`]. + + + +## Annotate tests + +If you would like to annotate your tests with something more substantial than a tag, you can do that when declaring a test. Annotations have a `type` and a `description` for more context, and will be visible in the test report. + +For example, to annotate a test with an issue url: + +```js +import { test, expect } from '@playwright/test'; + +test('test login page', { + annotation: { + type: 'issue', + description: 'https://github.com/microsoft/playwright/issues/23180', + }, +}, async ({ page }) => { + // ... +}); +``` + +You can also annotate all tests in a group or provide multiple annotations: + +```js +import { test, expect } from '@playwright/test'; + +test.describe('report tests', { + annotation: { type: 'category', description: 'report' }, +}, () => { + test('test report header', async ({ page }) => { + // ... + }); + + test('test full report', { + annotation: [ + { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23180' }, + { type: 'performance', description: 'very slow test!' }, + ], + }, async ({ page }) => { + // ... + }); +}); +``` + ## Conditionally skip a group of tests For example, you can run a group of tests just in Chromium by passing a callback. @@ -166,18 +240,19 @@ test('user profile', async ({ page }) => { }); ``` -## Custom annotations +## Runtime annotations -It's also possible to add custom metadata in the form of annotations to your tests. Annotations are key/value pairs accessible via [`test.info().annotations`](./api/class-testinfo#test-info-annotations). Many reporters show annotations, for example `'html'`. +While the test is already running, you can add annotations to [`test.info().annotations`](./api/class-testinfo#test-info-annotations). ```js title="example.spec.ts" -test('user profile', async ({ page }) => { +test('example test', async ({ page, browser }) => { test.info().annotations.push({ - type: 'issue', - description: 'https://github.com/microsoft/playwright/issues/<some-issue>', + type: 'browser version', + description: browser.version(), }); + // ... }); ``` diff --git a/playwright/docs/src/test-api/class-test.md b/playwright/docs/src/test-api/class-test.md index 4fde7bb576..f00e125215 100644 --- a/playwright/docs/src/test-api/class-test.md +++ b/playwright/docs/src/test-api/class-test.md @@ -19,6 +19,9 @@ test('basic test', async ({ page }) => { Declares a test. +* `test(title, body)` +* `test(title, details, body)` + **Usage** ```js @@ -30,33 +33,100 @@ test('basic test', async ({ page }) => { }); ``` +**Tags** + +You can tag tests by providing additional test details. Alternatively, you can include tags in the test title. Note that each tag must start with `@` symbol. + +```js +import { test, expect } from '@playwright/test'; + +test('basic test', { + tag: '@smoke', +}, async ({ page }) => { + await page.goto('https://playwright.dev/'); + // ... +}); + +test('another test @smoke', async ({ page }) => { + await page.goto('https://playwright.dev/'); + // ... +}); +``` + +Test tags are displayed in the test report, and are available to a custom reporter via `TestCase.tags` property. + +You can also filter tests by their tags during test execution: +* in the [command line](../test-cli.md#reference); +* in the config with [`property: TestConfig.grep`] and [`property: TestProject.grep`]; + +Learn more about [tagging](../test-annotations.md#tag-tests). + +**Annotations** + +You can annotate tests by providing additional test details. + +```js +import { test, expect } from '@playwright/test'; + +test('basic test', { + annotation: { + type: 'issue', + description: 'https://github.com/microsoft/playwright/issues/23180', + }, +}, async ({ page }) => { + await page.goto('https://playwright.dev/'); + // ... +}); +``` + +Test annotations are displayed in the test report, and are available to a custom reporter via `TestCase.annotations` property. + +You can also add annotations during runtime by manipulating [`property: TestInfo.annotations`]. + +Learn more about [test annotations](../test-annotations.md). + ### param: Test.(call).title * since: v1.10 - `title` <[string]> Test title. -### param: Test.(call).testFunction +### param: Test.(call).details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> Annotation type, for example `'issue'`. + - `description` ?<[string]> Optional annotation description, for example an issue url. + +Additional test details. + +### param: Test.(call).body * since: v1.10 -- `testFunction` <[function]\([Fixtures], [TestInfo]\)> +- `body` <[function]\([Fixtures], [TestInfo]\)> -Test function that takes one or two arguments: an object with fixtures and optional [TestInfo]. +Test body that takes one or two arguments: an object with fixtures and optional [TestInfo]. -## method: Test.afterAll#1 +## method: Test.afterAll * since: v1.10 Declares an `afterAll` hook that is executed once per worker after all tests. +When called in the scope of a test file, runs after all tests in the file. When called inside a [`method: Test.describe`] group, runs after all tests in the group. + **Details** -When called in the scope of a test file, runs after all tests in the file. When called inside a [`method: Test.describe#1`] group, runs after all tests in the group. If multiple `afterAll` hooks are added, they will run in the order of their registration. +When multiple `afterAll` hooks are added, they will run in the order of their registration. Note that worker process is restarted on test failures, and `afterAll` hook runs again in the new worker. Learn more about [workers and failures](../test-retries.md). Playwright will continue running all applicable hooks even if some of them have failed. +* `test.afterAll(hookFunction)` +* `test.afterAll(title, hookFunction)` + **Usage** ```js @@ -66,23 +136,7 @@ test.afterAll(async () => { }); ``` -### param: Test.afterAll#1.hookFunction -* since: v1.10 -- `hookFunction` <[function]\([Fixtures], [TestInfo]\)> - -Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo]. - - -## method: Test.afterAll#2 -* since: v1.38 - -Declares an `afterAll` hook with a title that is executed once per worker after all tests. - -**Details** - -See [`method: Test.afterAll#1`]. - -**Usage** +Alternatively, you can declare a hook **with a title**. ```js test.afterAll('Teardown', async () => { @@ -91,42 +145,48 @@ test.afterAll('Teardown', async () => { }); ``` -### param: Test.afterAll#2.title +### param: Test.afterAll.title * since: v1.38 -- `title` <[string]> +- `title` ?<[string]> Hook title. -### param: Test.afterAll#2.hookFunction -* since: v1.38 +### param: Test.afterAll.hookFunction +* since: v1.10 - `hookFunction` <[function]\([Fixtures], [TestInfo]\)> Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo]. -## method: Test.afterEach#1 +## method: Test.afterEach * since: v1.10 Declares an `afterEach` hook that is executed after each test. -**Details** +When called in the scope of a test file, runs after each test in the file. When called inside a [`method: Test.describe`] group, runs after each test in the group. -When called in the scope of a test file, runs after each test in the file. When called inside a [`method: Test.describe#1`] group, runs after each test in the group. If multiple `afterEach` hooks are added, they will run in the order of their registration. +You can access all the same [Fixtures] as the test body itself, and also the [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] as the test function itself, and also the [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)` + +**Details** + +When multiple `afterEach` hooks are added, they will run in the order of their registration. Playwright will continue running all applicable hooks even if some of them have failed. **Usage** + ```js title="example.spec.ts" import { test, expect } from '@playwright/test'; -test.afterEach(async ({ page }, testInfo) => { - console.log(`Finished ${testInfo.title} with status ${testInfo.status}`); +test.afterEach(async ({ page }) => { + console.log(`Finished ${test.info().title} with status ${test.info().status}`); - if (testInfo.status !== testInfo.expectedStatus) + if (test.info().status !== test.info().expectedStatus) console.log(`Did not run as expected, ended up at ${page.url()}`); }); @@ -135,70 +195,52 @@ test('my test', async ({ page }) => { }); ``` -### param: Test.afterEach#1.hookFunction -* since: v1.10 -- `hookFunction` <[function]\([Fixtures], [TestInfo]\)> - -Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo]. - - -## method: Test.afterEach#2 -* since: v1.38 - -Declares an `afterEach` hook with a title that is executed after each test. - -**Details** - -See [`method: Test.afterEach#1`]. - -**Usage** +Alternatively, you can delcare a hook **with a title**. ```js title="example.spec.ts" -import { test, expect } from '@playwright/test'; - -test.afterEach('Status check', async ({ page }, testInfo) => { - console.log(`Finished ${testInfo.title} with status ${testInfo.status}`); - - if (testInfo.status !== testInfo.expectedStatus) +test.afterEach('Status check', async ({ page }) => { + if (test.info().status !== test.info().expectedStatus) console.log(`Did not run as expected, ended up at ${page.url()}`); }); - -test('my test', async ({ page }) => { - // ... -}); ``` -### param: Test.afterEach#2.title +### param: Test.afterEach.title * since: v1.38 -- `title` <[string]> +- `title` ?<[string]> Hook title. -### param: Test.afterEach#2.hookFunction -* since: v1.38 +### param: Test.afterEach.hookFunction +* since: v1.10 - `hookFunction` <[function]\([Fixtures], [TestInfo]\)> Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo]. -## method: Test.beforeAll#1 +## method: Test.beforeAll * since: v1.10 Declares a `beforeAll` hook that is executed once per worker process before all tests. +When called in the scope of a test file, runs before all tests in the file. When called inside a [`method: Test.describe`] group, runs before all tests in the group. + +You can use [`method: Test.afterAll`] to teardown any resources set up in `beforeAll`. + +* `test.beforeAll(hookFunction)` +* `test.beforeAll(title, hookFunction)` + **Details** -When called in the scope of a test file, runs before all tests in the file. When called inside a [`method: Test.describe#1`] group, runs before all tests in the group. If multiple `beforeAll` hooks are added, they will run in the order of their registration. +When multiple `beforeAll` hooks are added, they will run in the order of their registration. Note that worker process is restarted on test failures, and `beforeAll` hook runs again in the new worker. Learn more about [workers and failures](../test-retries.md). Playwright will continue running all applicable hooks even if some of them have failed. -You can use [`method: Test.afterAll#1`] to teardown any resources set up in `beforeAll`. - **Usage** + ```js title="example.spec.ts" import { test, expect } from '@playwright/test'; @@ -215,72 +257,55 @@ test('my test', async ({ page }) => { }); ``` -### param: Test.beforeAll#1.hookFunction -* since: v1.10 -- `hookFunction` <[function]\([Fixtures], [TestInfo]\)> - -Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo]. - - -## method: Test.beforeAll#2 -* since: v1.38 - -Declares a `beforeAll` hook with a title that is executed once per worker process before all tests. - -**Details** - -See [`method: Test.beforeAll#1`]. -**Usage** +Alternatively, you can declare a hook **with a title**. ```js title="example.spec.ts" -import { test, expect } from '@playwright/test'; - test.beforeAll('Setup', async () => { console.log('Before tests'); }); - -test('my test', async ({ page }) => { - // ... -}); ``` -### param: Test.beforeAll#2.title +### param: Test.beforeAll.title * since: v1.38 -- `title` <[string]> +- `title` ?<[string]> Hook title. -### param: Test.beforeAll#2.hookFunction -* since: v1.38 +### param: Test.beforeAll.hookFunction +* since: v1.10 - `hookFunction` <[function]\([Fixtures], [TestInfo]\)> Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo]. - -## method: Test.beforeEach#1 +## method: Test.beforeEach * since: v1.10 Declares a `beforeEach` hook that is executed before each test. -**Details** +When called in the scope of a test file, runs before each test in the file. When called inside a [`method: Test.describe`] group, runs before each test in the group. -When called in the scope of a test file, runs before each test in the file. When called inside a [`method: Test.describe#1`] group, runs before each test in the group. If multiple `beforeEach` hooks are added, they will run in the order of their registration. +You can access all the same [Fixtures] as the test body itself, and also the [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] as the test function itself, and also the [TestInfo] object that gives a lot of useful information. For example, you can navigate the page before starting the test. +You can use [`method: Test.afterEach`] to teardown any resources set up in `beforeEach`. -Playwright will continue running all applicable hooks even if some of them have failed. +* `test.beforeEach(hookFunction)` +* `test.beforeEach(title, hookFunction)` + +**Details** -You can use [`method: Test.afterEach#1`] to teardown any resources set up in `beforeEach`. +When multiple `beforeEach` hooks are added, they will run in the order of their registration. + +Playwright will continue running all applicable hooks even if some of them have failed. **Usage** ```js title="example.spec.ts" import { test, expect } from '@playwright/test'; -test.beforeEach(async ({ page }, testInfo) => { - console.log(`Running ${testInfo.title}`); +test.beforeEach(async ({ page }) => { + console.log(`Running ${test.info().title}`); await page.goto('https://my.start.url/'); }); @@ -289,58 +314,42 @@ test('my test', async ({ page }) => { }); ``` -### param: Test.beforeEach#1.hookFunction -* since: v1.10 -- `hookFunction` <[function]\([Fixtures], [TestInfo]\)> - -Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo]. - - -## method: Test.beforeEach#2 -* since: v1.38 - -Declares a `beforeEach` hook with a title that is executed before each test. - -**Details** - -See [`method: Test.beforeEach#1`]. - -**Usage** +Alternatively, you can declare a hook **with a title**. ```js title="example.spec.ts" -import { test, expect } from '@playwright/test'; - -test.beforeEach('Open start URL', async ({ page }, testInfo) => { - console.log(`Running ${testInfo.title}`); +test.beforeEach('Open start URL', async ({ page }) => { + console.log(`Running ${test.info().title}`); await page.goto('https://my.start.url/'); }); - -test('my test', async ({ page }) => { - expect(page.url()).toBe('https://my.start.url/'); -}); ``` -### param: Test.beforeEach#2.title +### param: Test.beforeEach.title * since: v1.38 -- `title` <[string]> +- `title` ?<[string]> Hook title. -### param: Test.beforeEach#2.hookFunction -* since: v1.38 +### param: Test.beforeEach.hookFunction +* since: v1.10 - `hookFunction` <[function]\([Fixtures], [TestInfo]\)> Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo]. -## method: Test.describe#1 +## method: Test.describe * since: v1.10 Declares a group of tests. +* `test.describe(title, callback)` +* `test.describe(callback)` +* `test.describe(title, details, callback)` + **Usage** +You can declare a group of tests with a title. The title will be visible in the test report as a part of each test's title. + ```js test.describe('two tests', () => { test('one', async ({ page }) => { @@ -353,30 +362,59 @@ test.describe('two tests', () => { }); ``` -### param: Test.describe#1.title -* since: v1.10 -- `title` <[string]> +**Anonymous group** -Group title. +You can also declare a test group without a title. This is convenient to give a group of tests a common option with [`method: Test.use`]. -### param: Test.describe#1.callback -* since: v1.10 -- `callback` <[function]> +```js +test.describe(() => { + test.use({ colorScheme: 'dark' }); + + test('one', async ({ page }) => { + // ... + }); -A callback that is run immediately when calling [`method: Test.describe#1`]. Any tests added in this callback will belong to the group. + test('two', async ({ page }) => { + // ... + }); +}); +``` +**Tags** -## method: Test.describe#2 -* since: v1.24 +You can tag all tests in a group by providing additional details. Note that each tag must start with `@` symbol. -Declares an anonymous group of tests. This is convenient to give a group of tests a common option with [`method: Test.use`]. +```js +import { test, expect } from '@playwright/test'; -**Usage** +test.describe('two tagged tests', { + tag: '@smoke', +}, () => { + test('one', async ({ page }) => { + // ... + }); + + test('two', async ({ page }) => { + // ... + }); +}); +``` + +Learn more about [tagging](../test-annotations.md#tag-tests). + +**Annotations** + +You can annotate all tests in a group by providing additional details. ```js -test.describe(() => { - test.use({ colorScheme: 'dark' }); +import { test, expect } from '@playwright/test'; +test.describe('two annotated tests', { + annotation: { + type: 'issue', + description: 'https://github.com/microsoft/playwright/issues/23180', + }, +}, () => { test('one', async ({ page }) => { // ... }); @@ -387,11 +425,29 @@ test.describe(() => { }); ``` -### param: Test.describe#2.callback -* since: v1.24 +Learn more about [test annotations](../test-annotations.md). + +### param: Test.describe.title +* since: v1.10 +- `title` ?<[string]> + +Group title. + +### param: Test.describe.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +Additional details for all tests in the group. + +### param: Test.describe.callback +* since: v1.10 - `callback` <[function]> -A callback that is run immediately when calling [`method: Test.describe#2`]. Any tests added in this callback will belong to the group. +A callback that is run immediately when calling [`method: Test.describe`]. Any tests declared in this callback will belong to the group. @@ -475,24 +531,46 @@ Timeout for each test in milliseconds. Overrides [`property: TestProject.timeout ## method: Test.describe.fixme * since: v1.25 -Declares a test group similarly to [`method: Test.describe#1`]. Tests in this group are marked as "fixme" and will not be executed. +Declares a test group similarly to [`method: Test.describe`]. Tests in this group are marked as "fixme" and will not be executed. + +* `test.describe.fixme(title, callback)` +* `test.describe.fixme(callback)` +* `test.describe.fixme(title, details, callback)` **Usage** ```js -test.describe.fixme('broken tests', () => { +test.describe.fixme('broken tests that should be fixed', () => { test('example', async ({ page }) => { // This test will not run }); }); ``` +You can also omit the title. + +```js +test.describe.fixme(() => { + // ... +}); +``` + ### param: Test.describe.fixme.title * since: v1.25 -- `title` <[string]> +- `title` ?<[string]> Group title. +### param: Test.describe.fixme.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +See [`method: Test.describe`] for details description. + ### param: Test.describe.fixme.callback * since: v1.25 - `callback` <[function]> @@ -506,6 +584,10 @@ A callback that is run immediately when calling [`method: Test.describe.fixme`]. Declares a focused group of tests. If there are some focused tests or suites, all of them will be run but nothing else. +* `test.describe.only(title, callback)` +* `test.describe.only(callback)` +* `test.describe.only(title, details, callback)` + **Usage** ```js @@ -519,12 +601,31 @@ test('not in the focused group', async ({ page }) => { }); ``` +You can also omit the title. + +```js +test.describe.only(() => { + // ... +}); +``` + + ### param: Test.describe.only.title * since: v1.10 -- `title` <[string]> +- `title` ?<[string]> Group title. +### param: Test.describe.only.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +See [`method: Test.describe`] for details description. + ### param: Test.describe.only.callback * since: v1.10 - `callback` <[function]> @@ -539,6 +640,10 @@ A callback that is run immediately when calling [`method: Test.describe.only`]. Declares a group of tests that could be run in parallel. By default, tests in a single test file run one after another, but using [`method: Test.describe.parallel`] allows them to run in parallel. +* `test.describe.parallel(title, callback)` +* `test.describe.parallel(callback)` +* `test.describe.parallel(title, details, callback)` + **Usage** ```js @@ -550,12 +655,30 @@ test.describe.parallel('group', () => { Note that parallel tests are executed in separate processes and cannot share any state or global variables. Each of the parallel tests executes all relevant hooks. +You can also omit the title. + +```js +test.describe.parallel(() => { + // ... +}); +``` + ### param: Test.describe.parallel.title * since: v1.10 -- `title` <[string]> +- `title` ?<[string]> Group title. +### param: Test.describe.parallel.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +See [`method: Test.describe`] for details description. + ### param: Test.describe.parallel.callback * since: v1.10 - `callback` <[function]> @@ -570,6 +693,10 @@ A callback that is run immediately when calling [`method: Test.describe.parallel Declares a focused group of tests that could be run in parallel. This is similar to [`method: Test.describe.parallel`], but focuses the group. If there are some focused tests or suites, all of them will be run but nothing else. +* `test.describe.parallel.only(title, callback)` +* `test.describe.parallel.only(callback)` +* `test.describe.parallel.only(title, details, callback)` + **Usage** ```js @@ -579,12 +706,30 @@ test.describe.parallel.only('group', () => { }); ``` +You can also omit the title. + +```js +test.describe.parallel.only(() => { + // ... +}); +``` + ### param: Test.describe.parallel.only.title * since: v1.10 -- `title` <[string]> +- `title` ?<[string]> Group title. +### param: Test.describe.parallel.only.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +See [`method: Test.describe`] for details description. + ### param: Test.describe.parallel.only.callback * since: v1.10 - `callback` <[function]> @@ -603,6 +748,10 @@ Declares a group of tests that should always be run serially. If one of the test 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)` + **Usage** ```js @@ -612,12 +761,30 @@ test.describe.serial('group', () => { }); ``` +You can also omit the title. + +```js +test.describe.serial(() => { + // ... +}); +``` + ### param: Test.describe.serial.title * since: v1.10 -- `title` <[string]> +- `title` ?<[string]> Group title. +### param: Test.describe.serial.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +See [`method: Test.describe`] for details description. + ### param: Test.describe.serial.callback * since: v1.10 - `callback` <[function]> @@ -636,6 +803,10 @@ Declares a focused group of tests that should always be run serially. If one of 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)` + **Usage** ```js @@ -647,12 +818,30 @@ test.describe.serial.only('group', () => { }); ``` +You can also omit the title. + +```js +test.describe.serial.only(() => { + // ... +}); +``` + ### param: Test.describe.serial.only.title * since: v1.10 - `title` <[string]> Group title. +### param: Test.describe.serial.only.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +See [`method: Test.describe`] for details description. + ### param: Test.describe.serial.only.callback * since: v1.10 - `callback` <[function]> @@ -665,7 +854,11 @@ A callback that is run immediately when calling [`method: Test.describe.serial.o ## method: Test.describe.skip * since: v1.10 -Declares a skipped test group, similarly to [`method: Test.describe#1`]. Tests in the skipped group are never run. +Declares a skipped test group, similarly to [`method: Test.describe`]. Tests in the skipped group are never run. + +* `test.describe.skip(title, callback)` +* `test.describe.skip(title)` +* `test.describe.skip(title, details, callback)` **Usage** @@ -677,12 +870,30 @@ test.describe.skip('skipped group', () => { }); ``` +You can also omit the title. + +```js +test.describe.skip(() => { + // ... +}); +``` + ### param: Test.describe.skip.title * since: v1.10 - `title` <[string]> Group title. +### param: Test.describe.skip.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +See [`method: Test.describe`] for details description. + ### param: Test.describe.skip.callback * since: v1.10 - `callback` <[function]> @@ -822,29 +1033,33 @@ An object containing fixtures and/or options. Learn more about [fixtures format] - -## method: Test.fail#1 +## method: Test.fail * since: v1.10 -Unconditionally marks a test as "should fail". Playwright Test runs this test and ensures that it is actually failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed. +Marks a test as "should fail". Playwright runs this test and ensures that it is actually failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed. + +To declare a "failing" test: +* `test.fail(title, body)` +* `test.fail(title, details, body)` + +To annotate test as "failing" at runtime: +* `test.fail(condition, description)` +* `test.fail(callback, description)` +* `test.fail()` **Usage** +You can declare a test as failing, so that Playwright ensures it actually fails. + ```js import { test, expect } from '@playwright/test'; -test('not yet ready', async ({ page }) => { - test.fail(); +test.fail('not yet ready', async ({ page }) => { // ... }); ``` -## method: Test.fail#2 -* since: v1.10 - -Conditionally mark a test as "should fail" with an optional description. - -**Usage** +If your test fails in some configurations, but not all, you can mark the test as failing inside the test body based on some condition. We recommend passing a `description` argument in this case. ```js import { test, expect } from '@playwright/test'; @@ -855,30 +1070,12 @@ test('fail in WebKit', async ({ page, browserName }) => { }); ``` -### param: Test.fail#2.condition -* since: v1.10 -- `condition` <[boolean]> - -Test is marked as "should fail" when the condition is `true`. - -### param: Test.fail#2.description -* since: v1.10 -- `description` ?<[string]> - -Optional description that will be reflected in a test report. - - -## method: Test.fail#3 -* since: v1.10 - -Conditionally mark all tests in a file or [`method: Test.describe#1`] group as "should fail". - -**Usage** +You can mark all tests in a file or [`method: Test.describe`] group as "should fail" based on some condition with a single `test.fail(callback, description)` call. ```js import { test, expect } from '@playwright/test'; -test.fail(({ browserName }) => browserName === 'webkit'); +test.fail(({ browserName }) => browserName === 'webkit', 'not implemented yet'); test('fail in WebKit 1', async ({ page }) => { // ... @@ -888,147 +1085,164 @@ test('fail in WebKit 2', async ({ page }) => { }); ``` -### param: Test.fail#3.condition -* since: v1.10 -- `callback` <[function]\([Fixtures]\):[boolean]> +You can also call `test.fail()` without arguments inside the test body to always mark the test as failed. We recommend declaring a failing test with `test.fail(title, body)` instead. -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`. +```js +import { test, expect } from '@playwright/test'; -### param: Test.fail#3.description -* since: v1.10 -- `description` ?<[string]> +test('less readable', async ({ page }) => { + test.fail(); + // ... +}); +``` -Optional description that will be reflected in a test report. +### param: Test.fail.title +* since: v1.42 +- `title` ?<[string]> +Test title. -## method: Test.fixme#1 -* since: v1.10 +### param: Test.fail.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> -Declares a test to be fixed, similarly to [`method: Test.(call)`]. This test will not be run. +See [`method: Test.(call)`] for test details description. -**Usage** +### param: Test.fail.body +* since: v1.42 +- `body` ?<[function]\([Fixtures], [TestInfo]\)> +Test body that takes one or two arguments: an object with fixtures and optional [TestInfo]. -```js -import { test, expect } from '@playwright/test'; +### param: Test.fail.condition +* since: v1.10 +- `condition` ?<[boolean]> -test.fixme('test to be fixed', async ({ page }) => { - // ... -}); -``` +Test is marked as "should fail" when the condition is `true`. -### param: Test.fixme#1.title +### param: Test.fail.callback * since: v1.10 -- `title` <[string]> +- `callback` ?<[function]\([Fixtures]\):[boolean]> -Test title. +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`. -### param: Test.fixme#1.testFunction +### param: Test.fail.description * since: v1.10 -- `testFunction` <[function]\([Fixtures], [TestInfo]\)> +- `description` ?<[string]> -Test function that takes one or two arguments: an object with fixtures and optional [TestInfo]. +Optional description that will be reflected in a test report. -## method: Test.fixme#2 +## method: Test.fixme * since: v1.10 -Mark a test as "fixme", with the intention to fix it. Test is immediately aborted when you call [`method: Test.fixme#2`]. +Mark a test as "fixme", with the intention to fix it. Playwright will not run the test past the `test.fixme()` call. + +To declare a "fixme" test: +* `test.fixme(title, body)` +* `test.fixme(title, details, body)` + +To annotate test as "fixme" at runtime: +* `test.fixme(condition, description)` +* `test.fixme(callback, description)` +* `test.fixme()` **Usage** +You can declare a test as to be fixed, and Playwright will not run it. + ```js import { test, expect } from '@playwright/test'; -test('test to be fixed', async ({ page }) => { - test.fixme(); +test.fixme('to be fixed', async ({ page }) => { // ... }); ``` -Mark all tests in a file or [`method: Test.describe#1`] group as "fixme". +If your test should be fixed in some configurations, but not all, you can mark the test as "fixme" inside the test body based on some condition. We recommend passing a `description` argument in this case. Playwright will run the test, but abort it immediately after the `test.fixme` call. ```js import { test, expect } from '@playwright/test'; -test.fixme(); - -test('test to be fixed 1', async ({ page }) => { - // ... -}); -test('test to be fixed 2', async ({ page }) => { +test('to be fixed in Safari', async ({ page, browserName }) => { + test.fixme(browserName === 'webkit', 'This feature breaks in Safari for some reason'); // ... }); ``` - -## method: Test.fixme#3 -* since: v1.10 - -Conditionally mark a test as "fixme" with an optional description. - -**Usage** +You can mark all tests in a file or [`method: Test.describe`] group as "fixme" based on some condition with a single `test.fixme(callback, description)` call. ```js import { test, expect } from '@playwright/test'; -test('broken in WebKit', async ({ page, browserName }) => { - test.fixme(browserName === 'webkit', 'This feature is not implemented on Mac yet'); +test.fixme(({ browserName }) => browserName === 'webkit', 'Should figure out the issue'); + +test('to be fixed in Safari 1', async ({ page }) => { + // ... +}); +test('to be fixed in Safari 2', async ({ page }) => { // ... }); ``` +You can also call `test.fixme()` without arguments inside the test body to always mark the test as failed. We recommend using `test.fixme(title, body)` instead. -### param: Test.fixme#3.condition -* since: v1.10 -- `condition` <[boolean]> +```js +import { test, expect } from '@playwright/test'; -Test is marked as "fixme" when the condition is `true`. +test('less readable', async ({ page }) => { + test.fixme(); + // ... +}); +``` -### param: Test.fixme#3.description +### param: Test.fixme.title * since: v1.10 -- `description` ?<[string]> - -Optional description that will be reflected in a test report. +- `title` ?<[string]> +Test title. +### param: Test.fixme.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> +See [`method: Test.(call)`] for test details description. -## method: Test.fixme#4 +### param: Test.fixme.body * since: v1.10 +- `body` ?<[function]\([Fixtures], [TestInfo]\)> -Conditionally mark all tests in a file or [`method: Test.describe#1`] group as "fixme". +Test body that takes one or two arguments: an object with fixtures and optional [TestInfo]. -**Usage** - -```js -import { test, expect } from '@playwright/test'; - -test.fixme(({ browserName }) => browserName === 'webkit'); - -test('broken in WebKit 1', async ({ page }) => { - // ... -}); -test('broken in WebKit 2', async ({ page }) => { - // ... -}); -``` +### param: Test.fixme.condition +* since: v1.10 +- `condition` ?<[boolean]> +Test is marked as "should fail" when the condition is `true`. -### param: Test.fixme#4.condition +### param: Test.fixme.callback * since: v1.10 -- `callback` <[function]\([Fixtures]\):[boolean]> +- `callback` ?<[function]\([Fixtures]\):[boolean]> -A function that returns whether to mark as "fixme", based on test fixtures. Test or tests are marked as "fixme" when the return value is `true`. +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`. -### param: Test.fixme#4.description +### param: Test.fixme.description * since: v1.10 - `description` ?<[string]> Optional description that will be reflected in a test report. + ## method: Test.info * since: v1.10 - returns: <[TestInfo]> @@ -1053,6 +1267,9 @@ test('example test', async ({ page }) => { Declares a focused test. If there are some focused tests or suites, all of them will be run but nothing else. +* `test.only(title, body)` +* `test.only(title, details, body)` + **Usage** ```js @@ -1067,11 +1284,21 @@ test.only('focus this test', async ({ page }) => { Test title. -### param: Test.only.testFunction +### param: Test.only.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> + +See [`method: Test.(call)`] for test details description. + +### param: Test.only.body * since: v1.10 -- `testFunction` <[function]\([Fixtures], [TestInfo]\)> +- `body` <[function]\([Fixtures], [TestInfo]\)> -Test function that takes one or two arguments: an object with fixtures and optional [TestInfo]. +Test body that takes one or two arguments: an object with fixtures and optional [TestInfo]. ## method: Test.setTimeout @@ -1110,7 +1337,7 @@ Timeout for the currently running test is available through [`property: TestInfo }); ``` -* Changing timeout for all tests in a [`method: Test.describe#1`] group. +* Changing timeout for all tests in a [`method: Test.describe`] group. ```js test.describe('group', () => { @@ -1131,137 +1358,106 @@ Timeout in milliseconds. -## method: Test.skip#1 +## method: Test.skip * since: v1.10 -Declares a skipped test, similarly to [`method: Test.(call)`]. Skipped test is never run. +Skip a test. Playwright will not run the test past the `test.skip()` call. + +Skipped tests are not supposed to be ever run. If you intent to fix the test, use [`method: Test.fixme`] instead. + +To declare a skipped test: +* `test.skip(title, body)` +* `test.skip(title, details, body)` + +To skip a test at runtime: +* `test.skip(condition, description)` +* `test.skip(callback, description)` +* `test.skip()` **Usage** +You can declare a skipped test, and Playwright will not run it. + ```js import { test, expect } from '@playwright/test'; -test.skip('broken test', async ({ page }) => { +test.skip('never run', async ({ page }) => { // ... }); ``` -### param: Test.skip#1.title -* since: v1.10 -- `title` <[string]> - -Test title. - -### param: Test.skip#1.testFunction -* since: v1.10 -- `testFunction` <[function]\([Fixtures], [TestInfo]\)> - -Test function that takes one or two arguments: an object with fixtures and optional [TestInfo]. - - - -## method: Test.skip#2 -* since: v1.10 - -Unconditionally skip a test. Test is immediately aborted when you call [`method: Test.skip#2`]. - -**Usage** +If your test should be skipped in some configurations, but not all, you can skip the test inside the test body based on some condition. We recommend passing a `description` argument in this case. Playwright will run the test, but abort it immediately after the `test.skip` call. ```js import { test, expect } from '@playwright/test'; -test('skipped test', async ({ page }) => { - test.skip(); +test('Safari-only test', async ({ page, browserName }) => { + test.skip(browserName !== 'webkit', 'This feature is Safari-only'); // ... }); ``` -Unconditionally skip all tests in a file or [`method: Test.describe#1`] group: +You can skip all tests in a file or [`method: Test.describe`] group based on some condition with a single `test.skip(callback, description)` call. ```js import { test, expect } from '@playwright/test'; -test.skip(); +test.skip(({ browserName }) => browserName !== 'webkit', 'Safari-only'); -test('skipped test 1', async ({ page }) => { +test('Safari-only test 1', async ({ page }) => { // ... }); -test('skipped test 2', async ({ page }) => { +test('Safari-only test 2', async ({ page }) => { // ... }); ``` - -## method: Test.skip#3 -* since: v1.10 - -Conditionally skip a test with an optional description. - -**Usage** +You can also call `test.skip()` without arguments inside the test body to always mark the test as failed. We recommend using `test.skip(title, body)` instead. ```js import { test, expect } from '@playwright/test'; -test('skip in WebKit', async ({ page, browserName }) => { - test.skip(browserName === 'webkit', 'This feature is not implemented for Mac'); +test('less readable', async ({ page }) => { + test.skip(); // ... }); ``` -Skip from [`method: Test.beforeEach#1`] hook: - -```js -import { test, expect } from '@playwright/test'; - -test.beforeEach(async ({ page }) => { - test.skip(process.env.APP_VERSION === 'v1', 'There are no settings in v1'); - await page.goto('/settings'); -}); -``` - -### param: Test.skip#3.condition -* since: v1.10 -- `condition` <[boolean]> - -A skip condition. Test is skipped when the condition is `true`. - -### param: Test.skip#3.description +### param: Test.skip.title * since: v1.10 -- `description` ?<[void]|[string]> - -Optional description that will be reflected in a test report. +- `title` ?<[string]> +Test title. +### param: Test.skip.details +* since: v1.42 +- `details` ?<[Object]> + - `tag` ?<[string]|[Array]<[string]>> + - `annotation` ?<[Object]|[Array]<[Object]>> + - `type` <[string]> + - `description` ?<[string]> +See [`method: Test.(call)`] for test details description. -## method: Test.skip#4 +### param: Test.skip.body * since: v1.10 +- `body` ?<[function]\([Fixtures], [TestInfo]\)> -Conditionally skips all tests in a file or [`method: Test.describe#1`] group. +Test body that takes one or two arguments: an object with fixtures and optional [TestInfo]. -**Usage** - -```js -import { test, expect } from '@playwright/test'; - -test.skip(({ browserName }) => browserName === 'webkit'); - -test('skip in WebKit 1', async ({ page }) => { - // ... -}); -test('skip in WebKit 2', async ({ page }) => { - // ... -}); -``` +### param: Test.skip.condition +* since: v1.10 +- `condition` ?<[boolean]> +Test is marked as "should fail" when the condition is `true`. -### param: Test.skip#4.condition +### param: Test.skip.callback * since: v1.10 -- `callback` <[function]\([Fixtures]\):[boolean]> +- `callback` ?<[function]\([Fixtures]\):[boolean]> -A function that returns whether to skip, based on test fixtures. Test or tests are skipped when the return value is `true`. +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`. -### param: Test.skip#4.description +### param: Test.skip.description * since: v1.10 - `description` ?<[string]> @@ -1269,17 +1465,23 @@ Optional description that will be reflected in a test report. -## method: Test.slow#1 + + +## method: Test.slow * since: v1.10 -Unconditionally marks a test as "slow". Slow test will be given triple the default timeout. +Marks a test as "slow". Slow test will be given triple the default timeout. -**Details** +Note that [`method: Test.slow`] cannot be used in a `beforeAll` or `afterAll` hook. Use [`method: Test.setTimeout`] instead. -[`method: Test.slow#1`] cannot be used in a `beforeAll` or `afterAll` hook. Use [`method: Test.setTimeout`] instead. +* `test.slow()` +* `test.slow(condition, description)` +* `test.slow(callback, description)` **Usage** +You can mark a test as slow by calling `test.slow()` inside the test body. + ```js import { test, expect } from '@playwright/test'; @@ -1289,68 +1491,52 @@ test('slow test', async ({ page }) => { }); ``` -## method: Test.slow#2 -* since: v1.10 - -Conditionally mark a test as "slow" with an optional description. Slow test will be given triple the default timeout. - -**Usage** +If your test is slow in some configurations, but not all, you can mark it as slow based on a condition. We recommend passing a `description` argument in this case. ```js import { test, expect } from '@playwright/test'; -test('slow in WebKit', async ({ page, browserName }) => { - test.slow(browserName === 'webkit', 'This feature is slow on Mac'); +test('slow in Safari', async ({ page, browserName }) => { + test.slow(browserName === 'webkit', 'This feature is slow in Safari'); // ... }); ``` -### param: Test.slow#2.condition -* since: v1.10 -- `condition` <[boolean]> - -Test is marked as "slow" when the condition is `true`. - -### param: Test.slow#2.description -* since: v1.10 -- `description` ?<[string]> - -Optional description that will be reflected in a test report. - - -## method: Test.slow#3 -* since: v1.10 - -Conditionally mark all tests in a file or [`method: Test.describe#1`] group as "slow". Slow tests will be given triple the default timeout. - -**Usage** +You can mark all tests in a file or [`method: Test.describe`] group as "slow" based on some condition by passing a callback. ```js import { test, expect } from '@playwright/test'; -test.slow(({ browserName }) => browserName === 'webkit'); +test.slow(({ browserName }) => browserName === 'webkit', 'all tests are slow in Safari'); -test('slow in WebKit 1', async ({ page }) => { +test('slow in Safari 1', async ({ page }) => { // ... }); -test('fail in WebKit 2', async ({ page }) => { +test('fail in Safari 2', async ({ page }) => { // ... }); ``` -### param: Test.slow#3.condition +### param: Test.slow.condition +* since: v1.10 +- `condition` ?<[boolean]> + +Test is marked as "slow" when the condition is `true`. + +### param: Test.slow.callback * since: v1.10 -- `callback` <[function]\([Fixtures]\):[boolean]> +- `callback` ?<[function]\([Fixtures]\):[boolean]> A function that returns whether to mark as "slow", based on test fixtures. Test or tests are marked as "slow" when the return value is `true`. -### param: Test.slow#3.description +### param: Test.slow.description * since: v1.10 - `description` ?<[string]> Optional description that will be reflected in a test report. + ## async method: Test.step * since: v1.10 - returns: <[any]> @@ -1527,7 +1713,7 @@ Whether to box the step in the report. Defaults to `false`. When the step is box ## method: Test.use * since: v1.10 -Specifies options or fixtures to use in a single test file or a [`method: Test.describe#1`] group. Most useful to set an option, for example set `locale` to configure `context` fixture. +Specifies options or fixtures to use in a single test file or a [`method: Test.describe`] group. Most useful to set an option, for example set `locale` to configure `context` fixture. **Usage** diff --git a/playwright/docs/src/test-api/class-testconfig.md b/playwright/docs/src/test-api/class-testconfig.md index 09d0558d33..a5854ef4e3 100644 --- a/playwright/docs/src/test-api/class-testconfig.md +++ b/playwright/docs/src/test-api/class-testconfig.md @@ -52,6 +52,8 @@ 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. + - `toPass` ?<[Object]> Configuration for the [expect(value).toPass()](../test-assertions.md#expecttopass) method. + - `timeout` ?<[int]> timeout for toPass method in milliseconds. Configuration for the `expect` assertion library. Learn more about [various timeouts](../test-timeouts.md). @@ -472,6 +474,7 @@ export default defineConfig({ }); ``` + ## property: TestConfig.testDir * since: v1.10 - type: ?<[string]> diff --git a/playwright/docs/src/test-api/class-testinfo.md b/playwright/docs/src/test-api/class-testinfo.md index b8b492efbc..d766920b06 100644 --- a/playwright/docs/src/test-api/class-testinfo.md +++ b/playwright/docs/src/test-api/class-testinfo.md @@ -2,7 +2,7 @@ * since: v1.10 * langs: js -`TestInfo` contains information about currently running test. It is available to test functions, [`method: Test.beforeEach#1`], [`method: Test.afterEach#1`], [`method: Test.beforeAll#1`] and [`method: Test.afterAll#1`] hooks, and test-scoped fixtures. `TestInfo` provides utilities to control test execution: attach files, update test timeout, determine which test is currently running and whether it was retried, etc. +`TestInfo` contains information about currently running test. It is available to test functions, [`method: Test.beforeEach`], [`method: Test.afterEach`], [`method: Test.beforeAll`] and [`method: Test.afterAll`] hooks, and test-scoped fixtures. `TestInfo` provides utilities to control test execution: attach files, update test timeout, determine which test is currently running and whether it was retried, etc. ```js import { test, expect } from '@playwright/test'; @@ -20,7 +20,7 @@ test('basic test', async ({ page }, testInfo) => { - `type` <[string]> Annotation type, for example `'skip'` or `'fail'`. - `description` ?<[string]> Optional description. -The list of annotations applicable to the current test. Includes annotations from the test, annotations from all [`method: Test.describe#1`] groups the test belongs to and file-level annotations for the test file. +The list of annotations applicable to the current test. Includes annotations from the test, annotations from all [`method: Test.describe`] groups the test belongs to and file-level annotations for the test file. Learn more about [test annotations](../test-annotations.md). @@ -115,7 +115,7 @@ Processed configuration from the [configuration file](../test-configuration.md). * since: v1.10 - type: <[int]> -The number of milliseconds the test took to finish. Always zero before the test finishes, either successfully or not. Can be used in [`method: Test.afterEach#1`] hook. +The number of milliseconds the test took to finish. Always zero before the test finishes, either successfully or not. Can be used in [`method: Test.afterEach`] hook. ## property: TestInfo.error @@ -137,8 +137,8 @@ Errors thrown during test execution, if any. - type: <[TestStatus]<"passed"|"failed"|"timedOut"|"skipped"|"interrupted">> Expected status for the currently running test. This is usually `'passed'`, except for a few cases: -* `'skipped'` for skipped tests, e.g. with [`method: Test.skip#2`]; -* `'failed'` for tests marked as failed with [`method: Test.fail#1`]. +* `'skipped'` for skipped tests, e.g. with [`method: Test.skip`]; +* `'failed'` for tests marked as failed with [`method: Test.fail`]. Expected status is usually compared with the actual [`property: TestInfo.status`]: @@ -154,12 +154,12 @@ test.afterEach(async ({}, testInfo) => { ## method: TestInfo.fail#1 * since: v1.10 -Marks the currently running test as "should fail". Playwright Test runs this test and ensures that it is actually failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed. This is similar to [`method: Test.fail#1`]. +Marks the currently running test as "should fail". Playwright Test runs this test and ensures that it is actually failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed. This is similar to [`method: Test.fail`]. ## method: TestInfo.fail#2 * since: v1.10 -Conditionally mark the currently running test as "should fail" with an optional description. This is similar to [`method: Test.fail#2`]. +Conditionally mark the currently running test as "should fail" with an optional description. This is similar to [`method: Test.fail`]. ### param: TestInfo.fail#2.condition * since: v1.10 @@ -184,12 +184,12 @@ Absolute path to a file where the currently running test is declared. ## method: TestInfo.fixme#1 * since: v1.10 -Mark a test as "fixme", with the intention to fix it. Test is immediately aborted. This is similar to [`method: Test.fixme#2`]. +Mark a test as "fixme", with the intention to fix it. Test is immediately aborted. This is similar to [`method: Test.fixme`]. ## method: TestInfo.fixme#2 * since: v1.10 -Conditionally mark the currently running test as "fixme" with an optional description. This is similar to [`method: Test.fixme#3`]. +Conditionally mark the currently running test as "fixme" with an optional description. This is similar to [`method: Test.fixme`]. ### param: TestInfo.fixme#2.condition * since: v1.10 @@ -330,12 +330,12 @@ Timeout in milliseconds. ## method: TestInfo.skip#1 * since: v1.10 -Unconditionally skip the currently running test. Test is immediately aborted. This is similar to [`method: Test.skip#2`]. +Unconditionally skip the currently running test. Test is immediately aborted. This is similar to [`method: Test.skip`]. ## method: TestInfo.skip#2 * since: v1.10 -Conditionally skips the currently running test with an optional description. This is similar to [`method: Test.skip#3`]. +Conditionally skips the currently running test with an optional description. This is similar to [`method: Test.skip`]. ### param: TestInfo.skip#2.condition * since: v1.10 @@ -353,12 +353,12 @@ Optional description that will be reflected in a test report. ## method: TestInfo.slow#1 * since: v1.10 -Marks the currently running test as "slow", giving it triple the default timeout. This is similar to [`method: Test.slow#1`]. +Marks the currently running test as "slow", giving it triple the default timeout. This is similar to [`method: Test.slow`]. ## method: TestInfo.slow#2 * since: v1.10 -Conditionally mark the currently running test as "slow" with an optional description, giving it triple the default timeout. This is similar to [`method: Test.slow#2`]. +Conditionally mark the currently running test as "slow" with an optional description, giving it triple the default timeout. This is similar to [`method: Test.slow`]. ### param: TestInfo.slow#2.condition * since: v1.10 @@ -403,7 +403,7 @@ Suffix used to differentiate snapshots between multiple test configurations. For * since: v1.10 - type: ?<[TestStatus]<"passed"|"failed"|"timedOut"|"skipped"|"interrupted">> -Actual status for the currently running test. Available after the test has finished in [`method: Test.afterEach#1`] hook and fixtures. +Actual status for the currently running test. Available after the test has finished in [`method: Test.afterEach`] hook and fixtures. Status is usually compared with the [`property: TestInfo.expectedStatus`]: @@ -416,18 +416,6 @@ test.afterEach(async ({}, testInfo) => { }); ``` -## property: TestInfo.stderr -* since: v1.10 -- type: <[Array]<[string]|[Buffer]>> - -Output written to `process.stderr` or `console.error` during the test execution. - -## property: TestInfo.stdout -* since: v1.10 -- type: <[Array]<[string]|[Buffer]>> - -Output written to `process.stdout` or `console.log` during the test execution. - ## property: TestInfo.timeout * since: v1.10 - type: <[int]> diff --git a/playwright/docs/src/test-api/class-testproject.md b/playwright/docs/src/test-api/class-testproject.md index 338910db71..87e5b58aa7 100644 --- a/playwright/docs/src/test-api/class-testproject.md +++ b/playwright/docs/src/test-api/class-testproject.md @@ -102,6 +102,8 @@ 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. + - `toPass` ?<[Object]> Configuration for the [expect(value).toPass()](../test-assertions.md) method. + - `timeout` ?<[int]> timeout for toPass method in milliseconds. Configuration for the `expect` assertion library. @@ -197,6 +199,7 @@ Use [`method: Test.describe.configure`] to change the number of retries for a sp Use [`property: TestConfig.retries`] to change this option for all projects. + ## property: TestProject.teardown * since: v1.34 - type: ?<[string]> diff --git a/playwright/docs/src/test-assertions-csharp-java-python.md b/playwright/docs/src/test-assertions-csharp-java-python.md index 0fa60874b0..bc695ee46c 100644 --- a/playwright/docs/src/test-assertions-csharp-java-python.md +++ b/playwright/docs/src/test-assertions-csharp-java-python.md @@ -34,13 +34,13 @@ title: "Assertions" ## Custom Expect Message * langs: python -You can specify a custom error message as a second argument to the `expect` function, for example: +You can specify a custom expect message as a second argument to the `expect` function, for example: ```python expect(page.get_by_text("Name"), "should be logged in").to_be_visible() ``` -The error would look like this: +When expect fails, the error would look like this: ```bash def test_foobar(page: Page) -> None: @@ -56,11 +56,12 @@ tests/test_foobar.py:22: AssertionError ``` ## Setting a custom timeout -* langs: python +* langs: python, csharp You can specify a custom timeout for assertions either globally or per assertion. The default timeout is 5 seconds. ### Global timeout +* langs: python ```python title="conftest.py" from playwright.sync_api import expect @@ -68,6 +69,64 @@ from playwright.sync_api import expect expect.set_options(timeout=10_000) ``` +### Global timeout +* langs: csharp + +<Tabs + groupId="test-runners" + defaultValue="nunit" + values={[ + {label: 'NUnit', value: 'nunit'}, + {label: 'MSTest', value: 'mstest'} + ] +}> +<TabItem value="nunit"> + +```csharp title="UnitTest1.cs" +using Microsoft.Playwright; +using Microsoft.Playwright.NUnit; +using NUnit.Framework; + +namespace PlaywrightTests; + +[Parallelizable(ParallelScope.Self)] +[TestFixture] +public class Tests : PageTest +{ + [OneTimeSetUp] + public void GlobalSetup() + { + SetDefaultExpectTimeout(10_000); + } + // ... +} +``` + +</TabItem> +<TabItem value="mstest"> + +```csharp title="UnitTest1.cs" +using Microsoft.Playwright; +using Microsoft.Playwright.MSTest; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace PlaywrightTests; + +[TestClass] +public class UnitTest1 : PageTest +{ + [ClassInitialize] + public static void GlobalSetup(TestContext context) + { + SetDefaultExpectTimeout(10_000); + } + // ... +} +``` + +</TabItem> +</Tabs> + ### Per assertion timeout ```python title="test_foobar.py" @@ -76,3 +135,7 @@ from playwright.sync_api import expect def test_foobar(page: Page) -> None: expect(page.get_by_text("Name")).to_be_visible(timeout=10_000) ``` + +```csharp title="UnitTest1.cs" +await Expect(Page.GetByText("Name")).ToBeVisibleAsync(new() { Timeout = 10_000 }); +``` diff --git a/playwright/docs/src/test-assertions-js.md b/playwright/docs/src/test-assertions-js.md index 7db1f9c141..2d7a0b0332 100644 --- a/playwright/docs/src/test-assertions-js.md +++ b/playwright/docs/src/test-assertions-js.md @@ -136,13 +136,21 @@ Note that soft assertions only work with Playwright test runner. ## Custom expect message -You can specify a custom error message as a second argument to the `expect` function, for example: +You can specify a custom expect message as a second argument to the `expect` function, for example: ```js await expect(page.getByText('Name'), 'should be logged in').toBeVisible(); ``` -The error would look like this: +This message will be shown in reporters, both for passing and failing expects, providing more context about the assertion. + +When expect passes, you might see a successful step like this: + +```txt +✅ should be logged in @example.spec.ts:18 +``` + +When expect fails, the error would look like this: ```bash Error: should be logged in @@ -160,7 +168,7 @@ The error would look like this: 6 | ``` -The same works with soft assertions: +Soft assertions also support custom message: ```js expect.soft(value, 'my soft assertion').toBe(56); @@ -191,8 +199,8 @@ await expect.poll(async () => { const response = await page.request.get('https://api.example.com'); return response.status(); }, { - // Custom error message, optional. - message: 'make sure API eventually succeeds', // custom error message + // Custom expect message for reporting, optional. + message: 'make sure API eventually succeeds', // Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout. timeout: 10000, }).toBe(200); diff --git a/playwright/docs/src/test-cli-js.md b/playwright/docs/src/test-cli-js.md index 29ff2e6b9f..9a178d2308 100644 --- a/playwright/docs/src/test-cli-js.md +++ b/playwright/docs/src/test-cli-js.md @@ -84,19 +84,21 @@ Complete set of Playwright Test options is available in the [configuration file] | `--debug`| Run tests with Playwright Inspector. Shortcut for `PWDEBUG=1` environment variable and `--timeout=0 --max-failures=1 --headed --workers=1` options.| | `-c <file>` or `--config <file>`| Configuration file. If not passed, defaults to `playwright.config.ts` or `playwright.config.js` in the current directory. | | `--forbid-only` | Whether to disallow `test.only`. Useful on CI.| -| `-g <grep>` or `--grep <grep>` | Only run tests matching this regular expression. For example, this will run `'should add to cart'` when passed `-g "add to cart"`. The regular expression will be tested against the string that consists of the test file name, `test.describe` name (if any) and the test name divided by spaces, e.g. `my-test.spec.ts my-suite my-test`. The filter does not apply to the tests from dependcy projects, i.e. Playwright will still run all tests from [project dependencies](./test-projects.md#dependencies). | +| `-g <grep>` or `--grep <grep>` | Only run tests matching this regular expression. For example, this will run `'should add to cart'` when passed `-g "add to cart"`. The regular expression will be tested against the string that consists of the test file name, `test.describe` titles if any, test title and all test tags, separated by spaces, e.g. `my-test.spec.ts my-suite my-test @smoke`. The filter does not apply to the tests from dependcy projects, i.e. Playwright will still run all tests from [project dependencies](./test-projects.md#dependencies). | | `--grep-invert <grep>` | Only run tests **not** matching this regular expression. The opposite of `--grep`. The filter does not apply to the tests from dependcy projects, i.e. Playwright will still run all tests from [project dependencies](./test-projects.md#dependencies).| | `--global-timeout <number>` | Total timeout for the whole test run in milliseconds. By default, there is no global timeout. Learn more about [various timeouts](./test-timeouts.md).| | `--list` | list all the tests, but do not run them.| | `--max-failures <N>` or `-x`| Stop after the first `N` test failures. Passing `-x` stops after the first failure.| +| `--no-deps` | Ignore the dependencies between projects and behave as if they were not specified. | | `--output <dir>` | Directory for artifacts produced by tests, defaults to `test-results`. | | `--pass-with-no-tests` | Allows the test suite to pass when no files are found. | -| `--project <name>` | Only run tests from one of the specified [projects](./test-projects.md). Defaults to running all projects defined in the configuration file.| +| `--project <name>` | Only run tests from the specified [projects](./test-projects.md), supports '*' wildcard. Defaults to running all projects defined in the configuration file.| | `--quiet` | Whether to suppress stdout and stderr from the tests. | | `--repeat-each <N>` | Run each test `N` times, defaults to one. | -| `--reporter <reporter>` | Choose a reporter: minimalist `dot`, concise `line` or detailed `list`. See [reporters](./test-reporters.md) for more information. | +| `--reporter <reporter>` | Choose a reporter: minimalist `dot`, concise `line` or detailed `list`. See [reporters](./test-reporters.md) for more information. You can also pass a path to a [custom reporter](./test-reporters.md#custom-reporters) file. | | `--retries <number>` | The maximum number of [retries](./test-retries.md#retries) for flaky tests, defaults to zero (no retries). | | `--shard <shard>` | [Shard](./test-parallel.md#shard-tests-between-multiple-machines) tests and execute only selected shard, specified in the form `current/all`, 1-based, for example `3/5`.| +| `--tag <tag>` | Only run tests with a tag matching this tag expression. Learn more about [tagging](./test-annotations.md#tag-tests). | | `--timeout <number>` | Maximum timeout in milliseconds for each test, defaults to 30 seconds. Learn more about [various timeouts](./test-timeouts.md).| | `--trace <mode>` | Force tracing mode, can be `on`, `off`, `on-first-retry`, `on-all-retries`, `retain-on-failure` | | `--ignore-snapshots` | Whether to ignore [snapshots](./test-snapshots.md). Use this when snapshot expectations are known to be different, e.g. running tests on Linux against Windows screenshots. | diff --git a/playwright/docs/src/test-fixtures-js.md b/playwright/docs/src/test-fixtures-js.md index 33febcb5aa..48c9d074ba 100644 --- a/playwright/docs/src/test-fixtures-js.md +++ b/playwright/docs/src/test-fixtures-js.md @@ -5,7 +5,7 @@ title: "Fixtures" ## Introduction -Playwright Test is based on the concept of test fixtures. Test fixtures are used to establish environment for each test, giving the test everything it needs and nothing else. Test fixtures are isolated between tests. With fixtures, you can group tests based on their meaning, instead of their common setup. +Playwright Test is based on the concept of test fixtures. Test fixtures are used to establish the environment for each test, giving the test everything it needs and nothing else. Test fixtures are isolated between tests. With fixtures, you can group tests based on their meaning, instead of their common setup. ### Built-in fixtures @@ -1080,4 +1080,4 @@ import { test } from './fixtures'; test('passes', async ({ database, page, a11y }) => { // use database and a11y fixtures. }); -``` \ No newline at end of file +``` diff --git a/playwright/docs/src/test-global-setup-teardown-js.md b/playwright/docs/src/test-global-setup-teardown-js.md index 973c53bb55..045bb7d428 100644 --- a/playwright/docs/src/test-global-setup-teardown-js.md +++ b/playwright/docs/src/test-global-setup-teardown-js.md @@ -13,7 +13,7 @@ There are two ways to configure global setup and teardown: using a global setup ### Setup -First we add a new project with the name 'setup'. We then give it a [`property: TestProject.testMatch`] property in order to match the file called `global.setup.ts`: +First we add a new project with the name 'setup db'. We then give it a [`property: TestProject.testMatch`] property in order to match the file called `global.setup.ts`: ```js title="playwright.config.ts" import { defineConfig } from '@playwright/test'; @@ -23,8 +23,8 @@ export default defineConfig({ // ... projects: [ { - name: 'setup', - testMatch: /global.setup\.ts/, + name: 'setup db', + testMatch: /global\.setup\.ts/, }, // { // other project @@ -32,7 +32,7 @@ export default defineConfig({ ] }); ``` -Then we add the [`property: TestProject.dependencies`] property to our projects that depend on the setup project and pass into the array the name of of our dependency project, which we defined in the previous step: +Then we add the [`property: TestProject.dependencies`] property to our projects that depend on the setup project and pass into the array the name of our dependency project, which we defined in the previous step: ```js title="playwright.config.ts" import { defineConfig, devices } from '@playwright/test'; @@ -42,119 +42,42 @@ export default defineConfig({ // ... projects: [ { - name: 'setup', + name: 'setup db', testMatch: /global\.setup\.ts/, }, { - name: 'chromium', + name: 'chromium with db', use: { ...devices['Desktop Chrome'] }, - dependencies: ['setup'], + dependencies: ['setup db'], }, ] }); ``` -### Setup Example - -This example will show you how to use project dependencies to create a global setup that logins into an application and saves the state in storage state. This is useful if you want to run multiple tests that require a sign sign-in state and you want to avoid login for each test. - -The setup project will write the storage state into an 'playwright/.auth/user.json' file next to your playwright.config. By exporting a const of `STORAGE_STATE` we can then easily share the location of the storage file between projects with the [`StorageState`](./test-use-options#basic-options) method. This applies the storage state on the browser context with its cookies and a local storage snapshot. - -In this example the 'logged in chromium' project depends on the setup project whereas the 'logged out chromium' project does not depend on the setup project, and does not use the `storageState` option. - -```js title="playwright.config.ts" -import { defineConfig, devices } from '@playwright/test'; - -export const STORAGE_STATE = path.join(__dirname, 'playwright/.auth/user.json'); - -export default defineConfig({ - testDir: './tests', - // ... - use: { - baseURL: 'http://localhost:3000/', - }, - projects: [ - { - name: 'setup', - testMatch: /global\.setup\.ts/, - }, - { - name: 'logged in chromium', - testMatch: '**/*.loggedin.spec.ts', - dependencies: ['setup'], - use: { - ...devices['Desktop Chrome'], - storageState: STORAGE_STATE, - }, - }, - { - name: 'logged out chromium', - use: { ...devices['Desktop Chrome'] }, - testIgnore: ['**/*loggedin.spec.ts'] - }, - ], -}); -``` -We then create a setup test, stored at root level of your project, that logs in to an application and populates the context with the storage state after the login actions have been performed. By doing this you only have to log in once and the credentials will be stored in the `STORAGE_STATE` file, meaning you don't need to log in again for every test. Start by importing the `STORAGE_STATE` from the Playwright config file and then use this as the path to save your storage state to the page's context. +In this example the 'chromium with db' project depends on the 'setup db' project. We then create a setup test, stored at root level of your project (note that setup and teardown code must be defined as regular tests by calling [test()](./api/class-test#test-call) function): ```js title="tests/global.setup.ts" -import { test as setup, expect } from '@playwright/test'; -import { STORAGE_STATE } from '../playwright.config'; +import { test as setup } from '@playwright/test'; -setup('do login', async ({ page }) => { - await page.goto('/'); - await page.getByLabel('User Name').fill('user'); - await page.getByLabel('Password').fill('password'); - await page.getByText('Sign in').click(); - - // Wait until the page actually signs in. - await expect(page.getByText('Hello, user!')).toBeVisible(); - - await page.context().storageState({ path: STORAGE_STATE }); +setup('create new database', async ({ }) => { + console.log('creating new database...'); + // Initialize the database }); ``` -```js title="tests/menu.loggedin.spec.ts" +```js title="tests/menu.spec.ts" import { test, expect } from '@playwright/test'; -test.beforeEach(async ({ page }) => { - await page.goto('/'); -}); - test('menu', async ({ page }) => { - // You are signed in! + // Your test that depends on the database }); ``` -For a more detailed example check out our blog post: [A better global setup in Playwright reusing login with project dependencies](https://dev.to/playwright/a-better-global-setup-in-playwright-reusing-login-with-project-dependencies-14) or check the [v1.31 release video](https://youtu.be/PI50YAPTAs4) to see the demo. - ### Teardown You can teardown your setup by adding a [`property: TestProject.teardown`] property to your setup project. This will run after all dependent projects have run. -First we add a new project into the projects array and give it a name such as 'cleanup db'. We then give it a [`property: TestProject.testMatch`] property in order to match the file called `global.teardown.ts`: - -```js title="playwright.config.ts" -import { defineConfig } from '@playwright/test'; - -export default defineConfig({ - testDir: './tests', - // ... - projects: [ - // { - // setup project - // }, - { - name: 'cleanup db', - testMatch: /global\.teardown\.ts/, - }, - // { - // other project - // } - ] -}); -``` -Then we add the [`property: TestProject.teardown`] property to our setup project with the name 'cleanup db' which is the name we gave to our teardown project in the previous step: +First we add the [`property: TestProject.teardown`] property to our setup project with the name 'cleanup db' which is the name we gave to our teardown project in the previous step: ```js title="playwright.config.ts" import { defineConfig } from '@playwright/test'; @@ -172,58 +95,33 @@ export default defineConfig({ name: 'cleanup db', testMatch: /global\.teardown\.ts/, }, - // { - // other project - // } + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + dependencies: ['setup db'], + }, ] }); ``` -### Teardown Example - -Start by creating a `global.setup.ts` file in the tests directory of your project. This will be used to seed the database with some data before all tests have run. - -```js title="tests/global.setup.ts" -// seed the database with some data -``` -Then create a `global.teardown.ts` file in the tests directory of your project. This will be used to delete the data from the database after all tests have run. +Then we create a `global.teardown.ts` file in the tests directory of your project. This will be used to delete the data from the database after all tests have run. ```js title="tests/global.teardown.ts" -// delete the data from the database -``` -In the Playwright config file: - - Add a project into the projects array and give it a name such as 'setup db'. Give it a [`property: TestProject.testMatch`] property in order to match the file called `global.setup.ts`. - - - Create another project and give it a name such as 'cleanup db'. Give it a [`property: TestProject.testMatch`] property in order to match the file called `global.teardown.ts`. - - - Add the [`property: TestProject.teardown`] property to our setup project with the name 'cleanup db' which is the name given to our teardown project in the previous step. Finally add the 'chromium' project with the [`property: TestProject.dependencies`] on the 'setup db' project. +import { test as teardown } from '@playwright/test'; - -```js title="playwright.config.ts" -import { defineConfig } from '@playwright/test'; - -export default defineConfig({ - testDir: './tests', - // ... - projects: [ - { - name: 'setup db', - testMatch: /global\.setup\.ts/, - teardown: 'cleanup db', - }, - { - name: 'cleanup db', - testMatch: /global\.teardown\.ts/, - }, - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - dependencies: ['setup db'], - }, - ] +teardown('delete database', async ({ }) => { + console.log('deleting test database...'); + // Delete the database }); ``` +### More examples + +For more detailed examples check out: +- our [authentication](./auth.md) guide +- our blog post [A better global setup in Playwright reusing login with project dependencies](https://dev.to/playwright/a-better-global-setup-in-playwright-reusing-login-with-project-dependencies-14) +- [v1.31 release video](https://youtu.be/PI50YAPTAs4) to see the demo + ## Option 2: Configure globalSetup and globalTeardown You can use the `globalSetup` option in the [configuration file](./test-configuration.md#advanced-configuration) to set something up once before running all tests. The global setup file must export a single function that takes a config object. This function will be run once before all the tests. diff --git a/playwright/docs/src/test-parameterize-js.md b/playwright/docs/src/test-parameterize-js.md index 19111d4eea..a2e7075f52 100644 --- a/playwright/docs/src/test-parameterize-js.md +++ b/playwright/docs/src/test-parameterize-js.md @@ -156,7 +156,7 @@ For example, consider the following test file that needs a username and a passwo ```js title="example.spec.ts" test(`example test`, async ({ page }) => { // ... - await page.getByLabel('User Name').fill(process.env.USERNAME); + await page.getByLabel('User Name').fill(process.env.USER_NAME); await page.getByLabel('Password').fill(process.env.PASSWORD); }); ``` @@ -164,17 +164,17 @@ test(`example test`, async ({ page }) => { You can run this test with your secret username and password set in the command line. ```bash tab=bash-bash -USERNAME=me PASSWORD=secret npx playwright test +USER_NAME=me PASSWORD=secret npx playwright test ``` ```batch tab=bash-batch -set USERNAME=me +set USER_NAME=me set PASSWORD=secret npx playwright test ``` ```powershell tab=bash-powershell -$env:USERNAME=me +$env:USER_NAME=me $env:PASSWORD=secret npx playwright test ``` @@ -234,7 +234,7 @@ Now, you can just edit `.env` file to set any variables you'd like. ```bash # .env file STAGING=0 -USERNAME=me +USER_NAME=me PASSWORD=secret ``` diff --git a/playwright/docs/src/test-projects-js.md b/playwright/docs/src/test-projects-js.md index d401ae9c20..ee73846183 100644 --- a/playwright/docs/src/test-projects-js.md +++ b/playwright/docs/src/test-projects-js.md @@ -216,7 +216,7 @@ You can also teardown your setup by adding a [`property: TestProject.teardown`] ### Test filtering -If `--grep/--grep-invert` [option](./test-cli.md#reference) is used, test file name filter is specified in [command line](./test-cli.md) or [test.only()](./api/class-test.md#test-only) is used, the will only apply to the tests from the deepest projects in the project dependency chain. In other words, if a matching test belongs to a project that has project dependencies, Playwright will run all the tests from the project depdencies ignoring the filters. +If `--grep/--grep-invert` or `--shard` [option](./test-cli.md#reference) is used, test file name filter is specified in [command line](./test-cli.md) or [test.only()](./api/class-test.md#test-only) is used, it will only apply to the tests from the deepest projects in the project dependency chain. In other words, if a matching test belongs to a project that has project dependencies, Playwright will run all the tests from the project depdencies ignoring the filters. ## Custom project parameters diff --git a/playwright/docs/src/test-reporter-api/class-suite.md b/playwright/docs/src/test-reporter-api/class-suite.md index 6c94c9e823..0c0c75691f 100644 --- a/playwright/docs/src/test-reporter-api/class-suite.md +++ b/playwright/docs/src/test-reporter-api/class-suite.md @@ -9,7 +9,7 @@ * File suite #1 * [TestCase] #1 * [TestCase] #2 - * Suite corresponding to a [`method: Test.describe#1`] group + * Suite corresponding to a [`method: Test.describe`] group * [TestCase] #1 in a group * [TestCase] #2 in a group * < more test cases ... > @@ -54,7 +54,7 @@ Child suites. See [Suite] for the hierarchy of suites. * since: v1.10 - type: <[Array]<[TestCase]>> -Test cases in the suite. Note that only test cases defined directly in this suite are in the list. Any test cases defined in nested [`method: Test.describe#1`] groups are listed +Test cases in the suite. Note that only test cases defined directly in this suite are in the list. Any test cases defined in nested [`method: Test.describe`] groups are listed in the child [`property: Suite.suites`]. ## property: Suite.title @@ -65,7 +65,7 @@ Suite title. * Empty for root suite. * Project name for project suite. * File path for file suite. -* Title passed to [`method: Test.describe#1`] for a group suite. +* Title passed to [`method: Test.describe`] for a group suite. ## method: Suite.titlePath * since: v1.10 diff --git a/playwright/docs/src/test-reporter-api/class-testcase.md b/playwright/docs/src/test-reporter-api/class-testcase.md index de795d2028..d1e7b63927 100644 --- a/playwright/docs/src/test-reporter-api/class-testcase.md +++ b/playwright/docs/src/test-reporter-api/class-testcase.md @@ -10,7 +10,10 @@ - `type` <[string]> Annotation type, for example `'skip'` or `'fail'`. - `description` ?<[string]> Optional description. -The list of annotations applicable to the current test. Includes annotations from the test, annotations from all [`method: Test.describe#1`] groups the test belongs to and file-level annotations for the test file. +The list of annotations applicable to the current test. Includes: +* annotations defined on the test or suite via [`method: Test.(call)`] and [`method: Test.describe`]; +* annotations implicitly added by methods [`method: Test.skip`], [`method: Test.fixme`] and [`method: Test.fail`]; +* annotations appended to [`property: TestInfo.annotations`] during the test execution. Annotations are available during test execution through [`property: TestInfo.annotations`]. @@ -21,8 +24,8 @@ Learn more about [test annotations](../test-annotations.md). - type: <[TestStatus]<"passed"|"failed"|"timedOut"|"skipped"|"interrupted">> Expected test status. -* Tests marked as [`method: Test.skip#1`] or [`method: Test.fixme#1`] are expected to be `'skipped'`. -* Tests marked as [`method: Test.fail#1`] are expected to be `'failed'`. +* Tests marked as [`method: Test.skip`] or [`method: Test.fixme`] are expected to be `'skipped'`. +* Tests marked as [`method: Test.fail`] are expected to be `'failed'`. * Other tests are expected to be `'passed'`. See also [`property: TestResult.status`] for the actual status. @@ -79,11 +82,19 @@ The maximum number of retries given to this test in the configuration. Learn more about [test retries](../test-retries.md#retries). +## property: TestCase.tags +* since: v1.42 +- type: <[Array]<[string]>> + +The list of tags defined on the test or suite via [`method: Test.(call)`] or [`method: Test.describe`], as well as `@`-tokens extracted from test and suite titles. + +Learn more about [test tags](../test-annotations.md#tag-tests). + ## property: TestCase.timeout * since: v1.10 - type: <[float]> -The timeout given to the test. Affected by [`property: TestConfig.timeout`], [`property: TestProject.timeout`], [`method: Test.setTimeout`], [`method: Test.slow#1`] and [`method: TestInfo.setTimeout`]. +The timeout given to the test. Affected by [`property: TestConfig.timeout`], [`property: TestProject.timeout`], [`method: Test.setTimeout`], [`method: Test.slow`] and [`method: TestInfo.setTimeout`]. ## property: TestCase.title * since: v1.10 diff --git a/playwright/docs/src/test-reporters-js.md b/playwright/docs/src/test-reporters-js.md index ed4424f3e1..2673739c9c 100644 --- a/playwright/docs/src/test-reporters-js.md +++ b/playwright/docs/src/test-reporters-js.md @@ -289,7 +289,9 @@ export default defineConfig({ You can use the built in `github` reporter to get automatic failure annotations when running in GitHub actions. -Note that all other reporters work on GitHub Actions as well, but do not provide annotations. +Note that all other reporters work on GitHub Actions as well, but do not provide annotations. Also, it is not recommended to +use this annotation type if running your tests with a matrix strategy as the stack trace failures will multiply and obscure the +GitHub file view. ```js title="playwright.config.ts" import { defineConfig } from '@playwright/test'; @@ -340,6 +342,13 @@ export default defineConfig({ reporter: './my-awesome-reporter.ts', }); ``` + +Or just pass the reporter file path as `--reporter` command line option: + +```bash +npx playwright test --reporter="./myreporter/my-awesome-reporter.ts" +``` + ## Third party reporter showcase * [Allure](https://www.npmjs.com/package/allure-playwright) diff --git a/playwright/docs/src/test-retries-js.md b/playwright/docs/src/test-retries-js.md index 0deca7544c..b53b426880 100644 --- a/playwright/docs/src/test-retries-js.md +++ b/playwright/docs/src/test-retries-js.md @@ -169,7 +169,7 @@ It is usually better to make your tests isolated, so they can be efficiently run ## Reuse single page between tests -Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll#1`] and close it in [`method: Test.afterAll#1`]. +Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll`] and close it in [`method: Test.afterAll`]. ```js tab=js-js title="example.spec.js" // @ts-check diff --git a/playwright/docs/src/test-sharding-js.md b/playwright/docs/src/test-sharding-js.md index 3be5b3bcc8..6738dbeada 100644 --- a/playwright/docs/src/test-sharding-js.md +++ b/playwright/docs/src/test-sharding-js.md @@ -76,8 +76,8 @@ jobs: shardIndex: [1, 2, 3, 4] shardTotal: [4] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - name: Install dependencies @@ -109,8 +109,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18 - name: Install dependencies diff --git a/playwright/docs/src/test-timeouts-js.md b/playwright/docs/src/test-timeouts-js.md index 61d148f8fb..02f00f0b26 100644 --- a/playwright/docs/src/test-timeouts-js.md +++ b/playwright/docs/src/test-timeouts-js.md @@ -54,7 +54,7 @@ test('very slow test', async ({ page }) => { }); ``` -API reference: [`method: Test.setTimeout`] and [`method: Test.slow#1`]. +API reference: [`method: Test.setTimeout`] and [`method: Test.slow`]. ### Change timeout from a `beforeEach` hook diff --git a/playwright/docs/src/test-typescript-js.md b/playwright/docs/src/test-typescript-js.md index 465f2c2d93..12a1173ea4 100644 --- a/playwright/docs/src/test-typescript-js.md +++ b/playwright/docs/src/test-typescript-js.md @@ -5,7 +5,26 @@ title: "TypeScript" ## Introduction -Playwright supports TypeScript out of the box. You just write tests in TypeScript, and Playwright will read them, transform to JavaScript and run. +Playwright supports TypeScript out of the box. You just write tests in TypeScript, and Playwright will read them, transform to JavaScript and run. Note that Playwright does not check the types and will run tests even if there are non-critical TypeScript compilation errors. + +We recommend you run TypeScript compiler alongside Playwright. For example on GitHub actions: + +```yaml +jobs: + test: + runs-on: ubuntu-latest + steps: + ... + - name: Run type checks + run: npx tsc -p tsconfig.json --noEmit + - name: Run Playwright tests + run: npx playwright test +``` + +For local development, you can run `tsc` in [watch](https://www.typescriptlang.org/docs/handbook/configuring-watch.html) mode like this: +```sh +npx tsc -p tsconfig.json --noEmit -w +``` ## tsconfig.json diff --git a/playwright/docs/src/test-webserver-js.md b/playwright/docs/src/test-webserver-js.md index 4e88b7a288..aba072d56a 100644 --- a/playwright/docs/src/test-webserver-js.md +++ b/playwright/docs/src/test-webserver-js.md @@ -32,8 +32,11 @@ export default defineConfig({ | `command`| Shell command to start the local dev server of your app. | | `url`| URL of your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. | | `reuseExistingServer`| If `true`, it will re-use an existing server on the url when available. If no server is running on that url, it will run the command to start a new server. If `false`, it will throw if an existing process is listening on the url. To see the stdout, you can set the `DEBUG=pw:webserver` environment variable. | +| `ignoreHTTPSErrors` | Whether to ignore HTTPS errors when fetching the `url`. Defaults to `false`. | +| `cwd` | Current working directory of the spawned process, defaults to the directory of the configuration file. | | `stdout` | If `"pipe"`, it will pipe the stdout of the command to the process stdout. If `"ignore"`, it will ignore the stdout of the command. Default to `"ignore"`. | | `stderr` | Whether to pipe the stderr of the command to the process stderr or ignore it. Defaults to `"pipe"`. | +| `timeout` | `How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. | ## Adding a server timeout diff --git a/playwright/docs/src/writing-tests-csharp.md b/playwright/docs/src/writing-tests-csharp.md index 1aea228c68..938b24429f 100644 --- a/playwright/docs/src/writing-tests-csharp.md +++ b/playwright/docs/src/writing-tests-csharp.md @@ -19,7 +19,7 @@ Take a look at the example test below to see how to write a test using using [lo }> <TabItem value="nunit"> -```csharp +```csharp title="UnitTest1.cs" using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Playwright; @@ -60,7 +60,7 @@ public class Tests : PageTest </TabItem> <TabItem value="mstest"> -```csharp +```csharp title="UnitTest1.cs" using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Playwright; @@ -132,7 +132,7 @@ The Playwright NUnit and MSTest test framework base classes will isolate each te }> <TabItem value="nunit"> -```csharp +```csharp title="UnitTest1.cs" using System.Threading.Tasks; using Microsoft.Playwright.NUnit; using NUnit.Framework; @@ -154,7 +154,7 @@ public class Tests : PageTest </TabItem> <TabItem value="mstest"> -```csharp +```csharp title="UnitTest1.cs" using System.Threading.Tasks; using Microsoft.Playwright.MSTest; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -189,7 +189,7 @@ You can use `SetUp`/`TearDown` in NUnit or `TestInitialize`/`TestCleanup` in MST }> <TabItem value="nunit"> -```csharp +```csharp title="UnitTest1.cs" using System.Threading.Tasks; using Microsoft.Playwright.NUnit; using NUnit.Framework; @@ -218,7 +218,7 @@ public class Tests : PageTest </TabItem> <TabItem value="mstest"> -```csharp +```csharp title="UnitTest1.cs" using System.Threading.Tasks; using Microsoft.Playwright.MSTest; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/playwright/examples/todomvc/playwright.config.ts b/playwright/examples/todomvc/playwright.config.ts index f127e1a7f1..3d76370d4d 100644 --- a/playwright/examples/todomvc/playwright.config.ts +++ b/playwright/examples/todomvc/playwright.config.ts @@ -31,7 +31,7 @@ export default defineConfig({ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: [['html'], ['list']], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { diff --git a/playwright/package-lock.json b/playwright/package-lock.json index 9c5e3f9c0f..e5227f8549 100644 --- a/playwright/package-lock.json +++ b/playwright/package-lock.json @@ -1,32 +1,32 @@ { "name": "playwright-internal", - "version": "1.41.1", - "lockfileVersion": 2, + "version": "1.42.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "playwright-internal", - "version": "1.41.1", + "version": "1.42.0", "license": "Apache-2.0", "workspaces": [ "packages/*" ], "devDependencies": { "@actions/core": "^1.10.0", - "@babel/cli": "^7.23.0", - "@babel/code-frame": "^7.22.13", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-typescript": "^7.22.15", - "@babel/preset-react": "^7.22.15", + "@babel/cli": "^7.23.4", + "@babel/code-frame": "^7.23.5", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-typescript": "^7.23.6", + "@babel/preset-react": "^7.23.3", "@types/babel__core": "^7.20.2", "@types/codemirror": "^5.60.7", "@types/formidable": "^2.0.4", - "@types/node": "^16.18.34", + "@types/node": "^18.15.3", "@types/react": "^18.0.12", "@types/react-dom": "^18.0.5", "@types/resize-observer-browser": "^0.1.7", @@ -35,8 +35,8 @@ "@typescript-eslint/eslint-plugin": "^6.13.2", "@typescript-eslint/parser": "^6.13.2", "@typescript-eslint/utils": "^6.13.2", - "@vitejs/plugin-basic-ssl": "^1.0.1", - "@vitejs/plugin-react": "^3.1.0", + "@vitejs/plugin-basic-ssl": "^1.1.0", + "@vitejs/plugin-react": "^4.2.1", "@zip.js/zip.js": "^2.7.29", "chokidar": "^3.5.3", "colors": "^1.4.0", @@ -55,15 +55,13 @@ "formidable": "^2.1.1", "license-checker": "^25.0.1", "mime": "^3.0.0", - "ncp": "^2.0.0", "node-stream-zip": "^1.15.0", - "proxy": "^2.1.1", "react": "^18.1.0", "react-dom": "^18.1.0", "socksv5": "0.0.6", "ssim.js": "^3.5.0", "typescript": "^5.3.2", - "vite": "^4.4.12", + "vite": "^5.0.12", "ws": "^8.5.0", "xml2js": "^0.5.0", "yaml": "^2.2.2" @@ -82,9 +80,9 @@ } }, "node_modules/@actions/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", - "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", + "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", "dev": true, "dependencies": { "@actions/http-client": "^2.0.1", @@ -92,12 +90,13 @@ } }, "node_modules/@actions/http-client": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", - "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.0.tgz", + "integrity": "sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==", "dev": true, "dependencies": { - "tunnel": "^0.0.6" + "tunnel": "^0.0.6", + "undici": "^5.25.4" } }, "node_modules/@ampproject/remapping": { @@ -113,9 +112,9 @@ } }, "node_modules/@babel/cli": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.23.0.tgz", - "integrity": "sha512-17E1oSkGk2IwNILM4jtfAvgjt+ohmpfBky8aLerUfYZhiPNg7ca+CRCxZn8QDxwNhV/upsc2VHBCqGFIR+iBfA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.23.4.tgz", + "integrity": "sha512-j3luA9xGKCXVyCa5R7lJvOMM+Kc2JEnAEIgz2ggtjQ/j5YUVgfsg/WsG95bbsgq7YLHuiCOzMnoSasuY16qiCw==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", @@ -141,18 +140,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/cli/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -160,33 +153,33 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz", - "integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.8.tgz", - "integrity": "sha512-75+KxFB4CZqYRXjx4NlR4J7yGvKumBuZTmV4NV6v09dVXXkuYVYLT68N6HCzLvfJ+fWCxQsntNzKwwIXL4bHnw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -197,11 +190,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -222,47 +215,31 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz", - "integrity": "sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA==", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-validator-option": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1" + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz", + "integrity": "sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -317,19 +294,20 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "license": "MIT", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -344,17 +322,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-optimise-call-expression": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", @@ -424,9 +391,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "engines": { "node": ">=6.9.0" } @@ -440,30 +407,30 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -474,9 +441,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -484,30 +451,24 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -516,71 +477,63 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", - "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", - "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "dev": true, - "license": "MIT", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "dev": true, "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -590,45 +543,62 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "license": "MIT", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -637,15 +607,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", - "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -655,9 +625,9 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", - "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", + "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -670,16 +640,16 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz", - "integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.15" + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" }, "engines": { "node": ">=6.9.0" @@ -704,11 +674,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz", - "integrity": "sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -718,11 +688,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz", - "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -731,22 +701,10 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx/node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", - "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz", + "integrity": "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -760,14 +718,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", - "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", + "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-create-class-features-plugin": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" + "@babel/plugin-syntax-typescript": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -777,17 +735,17 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.15.tgz", - "integrity": "sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz", + "integrity": "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-react-display-name": "^7.22.5", + "@babel/plugin-transform-react-display-name": "^7.23.3", "@babel/plugin-transform-react-jsx": "^7.22.15", "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.22.5" + "@babel/plugin-transform-react-pure-annotations": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -797,12 +755,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.18.6", - "license": "MIT", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -811,6 +772,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -825,19 +798,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -845,11 +818,11 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -859,8 +832,9 @@ }, "node_modules/@electron/get": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", @@ -878,173 +852,189 @@ "global-tunnel-ng": "^2.7.1" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", + "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.9.tgz", - "integrity": "sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz", - "integrity": "sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.9.tgz", - "integrity": "sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz", - "integrity": "sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz", - "integrity": "sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz", - "integrity": "sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz", - "integrity": "sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz", - "integrity": "sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz", - "integrity": "sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz", - "integrity": "sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.11.tgz", - "integrity": "sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -1054,177 +1044,177 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz", - "integrity": "sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz", - "integrity": "sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz", - "integrity": "sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz", - "integrity": "sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz", - "integrity": "sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz", - "integrity": "sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz", - "integrity": "sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz", - "integrity": "sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz", - "integrity": "sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz", - "integrity": "sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz", - "integrity": "sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -1245,9 +1235,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -1276,10 +1266,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1291,29 +1291,84 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "*" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1328,14 +1383,15 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "license": "MIT", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1346,16 +1402,17 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.1", - "license": "MIT", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "engines": { "node": ">=6.0.0" } @@ -1366,9 +1423,9 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", + "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1376,22 +1433,16 @@ }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", "dev": true, - "license": "MIT", "optional": true }, - "node_modules/@nicolo-ribaudo/semver-v6": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", - "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1402,16 +1453,18 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1465,186 +1518,212 @@ "link": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.0.tgz", - "integrity": "sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz", + "integrity": "sha512-idWaG8xeSRCfRq9KpRysDHJ/rEHBEXcHuJ82XY0yYFIWnLMjZv9vF/7DOq8djQ2n3Lk6+3qfSH8AqlmHlmi1MA==", "cpu": [ "arm" ], "optional": true, "os": [ "android" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.0.tgz", - "integrity": "sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.5.tgz", + "integrity": "sha512-f14d7uhAMtsCGjAYwZGv6TwuS3IFaM4ZnGMUn3aCBgkcHAYErhV1Ad97WzBvS2o0aaDv4mVz+syiN0ElMyfBPg==", "cpu": [ "arm64" ], "optional": true, "os": [ "android" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.0.tgz", - "integrity": "sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.5.tgz", + "integrity": "sha512-ndoXeLx455FffL68OIUrVr89Xu1WLzAG4n65R8roDlCoYiQcGGg6MALvs2Ap9zs7AHg8mpHtMpwC8jBBjZrT/w==", "cpu": [ "arm64" ], "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.0.tgz", - "integrity": "sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.5.tgz", + "integrity": "sha512-UmElV1OY2m/1KEEqTlIjieKfVwRg0Zwg4PLgNf0s3glAHXBN99KLpw5A5lrSYCa1Kp63czTpVll2MAqbZYIHoA==", "cpu": [ "x64" ], "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.0.tgz", - "integrity": "sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.5.tgz", + "integrity": "sha512-Q0LcU61v92tQB6ae+udZvOyZ0wfpGojtAKrrpAaIqmJ7+psq4cMIhT/9lfV6UQIpeItnq/2QDROhNLo00lOD1g==", "cpu": [ "arm" ], "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.0.tgz", - "integrity": "sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.5.tgz", + "integrity": "sha512-dkRscpM+RrR2Ee3eOQmRWFjmV/payHEOrjyq1VZegRUa5OrZJ2MAxBNs05bZuY0YCtpqETDy1Ix4i/hRqX98cA==", "cpu": [ "arm64" ], "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.0.tgz", - "integrity": "sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.5.tgz", + "integrity": "sha512-QaKFVOzzST2xzY4MAmiDmURagWLFh+zZtttuEnuNn19AiZ0T3fhPyjPPGwLNdiDT82ZE91hnfJsUiDwF9DClIQ==", "cpu": [ "arm64" ], "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.0.tgz", - "integrity": "sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.5.tgz", + "integrity": "sha512-HeGqmRJuyVg6/X6MpE2ur7GbymBPS8Np0S/vQFHDmocfORT+Zt76qu+69NUoxXzGqVP1pzaY6QIi0FJWLC3OPA==", "cpu": [ "riscv64" ], "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.0.tgz", - "integrity": "sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz", + "integrity": "sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==", "cpu": [ "x64" ], "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.0.tgz", - "integrity": "sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.5.tgz", + "integrity": "sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==", "cpu": [ "x64" ], "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.0.tgz", - "integrity": "sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.5.tgz", + "integrity": "sha512-aHSsMnUw+0UETB0Hlv7B/ZHOGY5bQdwMKJSzGfDfvyhnpmVxLMGnQPGNE9wgqkLUs3+gbG1Qx02S2LLfJ5GaRQ==", "cpu": [ "arm64" ], "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.0.tgz", - "integrity": "sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.5.tgz", + "integrity": "sha512-AiqiLkb9KSf7Lj/o1U3SEP9Zn+5NuVKgFdRIZkvd4N0+bYrTOovVd0+LmYCPQGbocT4kvFyK+LXCDiXPBF3fyA==", "cpu": [ "ia32" ], "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.0.tgz", - "integrity": "sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.5.tgz", + "integrity": "sha512-1q+mykKE3Vot1kaFJIDoUFv5TuW+QQVaf2FmTT9krg86pQrGStOSJJ0Zil7CFagyxDuouTepzt5Y5TVzyajOdQ==", "cpu": [ "x64" ], "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@sindresorhus/is": { "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.1.tgz", + "integrity": "sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0-next.0 || ^2.0.0", + "debug": "^4.3.4", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "svelte-hmr": "^0.15.3", + "vitefu": "^0.2.5" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.0.0.tgz", + "integrity": "sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, "node_modules/@szmarczak/http-timer": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", "dev": true, - "license": "MIT", "dependencies": { "defer-to-connect": "^1.0.1" }, @@ -1653,9 +1732,9 @@ } }, "node_modules/@types/babel__core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", - "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -1665,34 +1744,34 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/codemirror": { - "version": "5.60.7", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.7.tgz", - "integrity": "sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==", + "version": "5.60.15", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz", + "integrity": "sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==", "dev": true, "dependencies": { "@types/tern": "*" @@ -1704,9 +1783,10 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/formidable": { - "version": "2.0.4", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.6.tgz", + "integrity": "sha512-L4HcrA05IgQyNYJj6kItuIkXrInJvsXTPC5B1i64FggWKKqSL+4hgt7asiSNva75AoLQjq29oPxFfU4GAQ6Z2w==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -1718,20 +1798,25 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.18.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.34.tgz", - "integrity": "sha512-VmVm7gXwhkUimRfBwVI1CHhwp86jDWR04B5FGebMMyxV90SlCmFujwUHrxTD4oO+SOYU86SoxvhgeRQJY7iXFg==", - "devOptional": true + "version": "18.19.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.8.tgz", + "integrity": "sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg==", + "devOptional": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/prop-types": { - "version": "15.7.3", - "dev": true, - "license": "MIT" + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "dev": true }, "node_modules/@types/react": { - "version": "18.0.12", + "version": "18.2.48", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", + "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", "dev": true, - "license": "MIT", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -1739,22 +1824,25 @@ } }, "node_modules/@types/react-dom": { - "version": "18.0.5", + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", "dev": true, - "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/resize-observer-browser": { - "version": "0.1.7", - "dev": true, - "license": "MIT" + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.11.tgz", + "integrity": "sha512-cNw5iH8JkMkb3QkCoe7DaZiawbDQEUX8t7iuQaRTyLOyQCR2h+ibBD4GJt7p5yhUHrlOeL7ZtbxNHeipqNsBzQ==", + "dev": true }, "node_modules/@types/scheduler": { - "version": "0.16.1", - "dev": true, - "license": "MIT" + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "dev": true }, "node_modules/@types/semver": { "version": "7.5.6", @@ -1763,42 +1851,43 @@ "dev": true }, "node_modules/@types/tern": { - "version": "0.23.4", - "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.4.tgz", - "integrity": "sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==", + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", "dev": true, "dependencies": { "@types/estree": "*" } }, "node_modules/@types/ws": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", - "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/xml2js": { - "version": "0.4.9", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", - "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz", + "integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/type-utils": "6.13.2", - "@typescript-eslint/utils": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1823,6 +1912,18 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -1838,16 +1939,22 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/parser": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz", - "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.0.tgz", + "integrity": "sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4" }, "engines": { @@ -1867,13 +1974,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", - "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2" + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1884,13 +1991,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz", - "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", + "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/utils": "6.13.2", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1911,9 +2018,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", - "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1924,16 +2031,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", - "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -1950,6 +2058,18 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -1965,18 +2085,24 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz", - "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", "semver": "^7.5.4" }, "engines": { @@ -1990,6 +2116,18 @@ "eslint": "^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -2005,13 +2143,19 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", - "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2029,163 +2173,174 @@ "dev": true }, "node_modules/@vitejs/plugin-basic-ssl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.0.1.tgz", - "integrity": "sha512-pcub+YbFtFhaGRTo1832FQHQSHvMrlb43974e2eS8EKleR3p1cDdkJFPci1UhwkEf1J9Bz+wKBSzqpKp7nNj2A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", + "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", "dev": true, "engines": { "node": ">=14.6.0" }, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, "node_modules/@vitejs/plugin-react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz", - "integrity": "sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==", - "dev": true, + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", + "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", "dependencies": { - "@babel/core": "^7.20.12", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.19.6", - "magic-string": "^0.27.0", + "@babel/core": "^7.23.5", + "@babel/plugin-transform-react-jsx-self": "^7.23.3", + "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.1.0-beta.0" + "vite": "^4.2.0 || ^5.0.0" } }, - "node_modules/@vitejs/plugin-vue2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue2/-/plugin-vue2-2.2.0.tgz", - "integrity": "sha512-1km7zEuZ/9QRPvzXSjikbTYGQPG86Mq1baktpC4sXqsXlb02HQKfi+fl8qVS703JM7cgm24Ga9j+RwKmvFn90A==", + "node_modules/@vitejs/plugin-vue": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz", + "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==", "engines": { - "node": "^14.18.0 || >= 16.0.0" + "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0", - "vue": "^2.7.0-0" + "vite": "^4.0.0 || ^5.0.0", + "vue": "^3.2.25" } }, "node_modules/@vue/compiler-core": { - "version": "3.2.36", - "license": "MIT", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz", + "integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==", "peer": true, "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.36", + "@babel/parser": "^7.23.6", + "@vue/shared": "3.4.15", + "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map": "^0.6.1" + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "peer": true + }, "node_modules/@vue/compiler-dom": { - "version": "3.2.36", - "license": "MIT", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz", + "integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==", "peer": true, "dependencies": { - "@vue/compiler-core": "3.2.36", - "@vue/shared": "3.2.36" + "@vue/compiler-core": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.2.36", - "license": "MIT", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz", + "integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==", "peer": true, "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.36", - "@vue/compiler-dom": "3.2.36", - "@vue/compiler-ssr": "3.2.36", - "@vue/reactivity-transform": "3.2.36", - "@vue/shared": "3.2.36", + "@babel/parser": "^7.23.6", + "@vue/compiler-core": "3.4.15", + "@vue/compiler-dom": "3.4.15", + "@vue/compiler-ssr": "3.4.15", + "@vue/shared": "3.4.15", "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" + "magic-string": "^0.30.5", + "postcss": "^8.4.33", + "source-map-js": "^1.0.2" } }, - "node_modules/@vue/compiler-sfc/node_modules/magic-string": { - "version": "0.25.9", - "license": "MIT", - "peer": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - } + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "peer": true }, "node_modules/@vue/compiler-ssr": { - "version": "3.2.36", - "license": "MIT", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz", + "integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.2.36", - "@vue/shared": "3.2.36" + "@vue/compiler-dom": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/reactivity": { - "version": "3.2.36", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/shared": "3.2.36" - } - }, - "node_modules/@vue/reactivity-transform": { - "version": "3.2.36", - "license": "MIT", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz", + "integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==", "peer": true, "dependencies": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.36", - "@vue/shared": "3.2.36", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" + "@vue/shared": "3.4.15" } }, - "node_modules/@vue/reactivity-transform/node_modules/magic-string": { - "version": "0.25.9", - "license": "MIT", + "node_modules/@vue/runtime-core": { + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz", + "integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==", "peer": true, "dependencies": { - "sourcemap-codec": "^1.4.8" + "@vue/reactivity": "3.4.15", + "@vue/shared": "3.4.15" } }, - "node_modules/@vue/runtime-core": { - "version": "3.2.36", - "license": "MIT", + "node_modules/@vue/runtime-dom": { + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz", + "integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==", "peer": true, "dependencies": { - "@vue/reactivity": "3.2.36", - "@vue/shared": "3.2.36" + "@vue/runtime-core": "3.4.15", + "@vue/shared": "3.4.15", + "csstype": "^3.1.3" } }, - "node_modules/@vue/runtime-dom": { - "version": "3.2.36", - "license": "MIT", + "node_modules/@vue/server-renderer": { + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz", + "integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==", "peer": true, "dependencies": { - "@vue/runtime-core": "3.2.36", - "@vue/shared": "3.2.36", - "csstype": "^2.6.8" + "@vue/compiler-ssr": "3.4.15", + "@vue/shared": "3.4.15" + }, + "peerDependencies": { + "vue": "3.4.15" } }, - "node_modules/@vue/runtime-dom/node_modules/csstype": { - "version": "2.6.20", - "license": "MIT", - "peer": true - }, "node_modules/@vue/shared": { - "version": "3.2.36", - "license": "MIT", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz", + "integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==", "peer": true }, "node_modules/@zip.js/zip.js": { - "version": "2.7.29", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.29.tgz", - "integrity": "sha512-KtOa3HY7Vi77ctZAVhx2nsKweDTCP4DVBghZkvg5qyIX6T/Z54QlU6f0q2hFhxu5j+LgUDeMMhiv2xj4ZF6snA==", + "version": "2.7.32", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.32.tgz", + "integrity": "sha512-9Ox1meDIvIKE23LLA7Fxd/ewJpKjj2KryH92doHRqx2406LmIzorsiMawL0qIK7dvwN9K+mfk47lauoIE0o1zQ==", "dev": true, "engines": { "bun": ">=0.7.0", @@ -2195,13 +2350,14 @@ }, "node_modules/abbrev": { "version": "1.1.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true }, "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -2235,24 +2391,27 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "3.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dependencies": { "color-convert": "^1.9.0" }, @@ -2274,27 +2433,33 @@ "node": ">=8.0.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/ansi-to-html/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, - "node_modules/args": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", - "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { - "camelcase": "5.0.0", - "chalk": "2.4.2", - "leven": "2.1.0", - "mri": "1.1.4" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 8" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -2318,8 +2483,9 @@ }, "node_modules/array-find-index": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2424,11 +2590,14 @@ }, "node_modules/asap": { "version": "2.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true }, "node_modules/async": { "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==", "dev": true }, "node_modules/asynciterator.prototype": { @@ -2453,17 +2622,17 @@ } }, "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", + "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", "dependencies": { "dequal": "^2.0.3" } }, "node_modules/babel-plugin-jsx-dom-expressions": { - "version": "0.36.10", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.36.10.tgz", - "integrity": "sha512-QA2k/14WGw+RgcGGnEuLWwnu4em6CGhjeXtjvgOYyFHYS2a+CzPeaVQHDOlfuiBcjq/3hWMspHMIMnPEOIzdBg==", + "version": "0.37.13", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.37.13.tgz", + "integrity": "sha512-oAEMMIgU0h1DmHn4ZDaBBFc08nsVJciLq9pF7g0ZdpeIDKfY4zXjXr8+/oBjKhXG8nyomhnTodPjeG+/ZXcWXQ==", "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", @@ -2475,12 +2644,23 @@ "@babel/core": "^7.20.12" } }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/babel-preset-solid": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.7.3.tgz", - "integrity": "sha512-HOdyrij99zo+CBrmtDxSexBAl54vCBCfBoyueLBvcfVniaEXNd4ftKqSN6XQcLvFfCY28UFO+DHaigXzWKOfzg==", + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.8.9.tgz", + "integrity": "sha512-1awR1QCoryXtAdnjsrx/eVBTYz+tpHUDOdBXqG9oVV7S0ojf2MV/woR0+8BG+LMXVzIr60oKYzCZ9UZGafxmpg==", "dependencies": { - "babel-plugin-jsx-dom-expressions": "^0.36.9" + "babel-plugin-jsx-dom-expressions": "^0.37.13" }, "peerDependencies": { "@babel/core": "^7.0.0" @@ -2488,34 +2668,40 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/basic-auth-parser": { - "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz", - "integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/boolean": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/brace-expansion": { - "version": "1.1.11", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.0.1" }, @@ -2524,9 +2710,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "funding": [ { "type": "opencollective", @@ -2542,10 +2728,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -2556,21 +2742,24 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } }, "node_modules/buffer-from": { "version": "1.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/cacheable-request": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", "dev": true, - "license": "MIT", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -2584,22 +2773,54 @@ "node": ">=8" } }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", + "dev": true + }, + "node_modules/cacheable-request/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.0" + } + }, "node_modules/cacheable-request/node_modules/lowercase-keys": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2614,19 +2835,10 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", - "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { - "version": "1.0.30001514", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001514.tgz", - "integrity": "sha512-ENcIpYBmwAAOm/V2cXgM7rZUrKKaqisZl4ZAI520FIkqGXUxJjmaIssbRW5HVVR5tyV6ygTLIm15aU8LUmQSaQ==", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "funding": [ { "type": "opencollective", @@ -2644,7 +2856,8 @@ }, "node_modules/chalk": { "version": "2.4.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -2681,39 +2894,10 @@ "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/anymatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/chokidar/node_modules/binary-extensions": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar/node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cli": { "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "integrity": "sha512-dbn5HyeJWSOU58RwOEiF1VWrl7HRvDsKLpu0uiI/vExH6iNoyUzjB5Mr3IJY5DVUfnbpe9793xw4DFJVzC9nWQ==", "dev": true, "dependencies": { "glob": ">= 3.1.4" @@ -2724,6 +2908,8 @@ }, "node_modules/cliff": { "version": "0.1.10", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "integrity": "sha512-roZWcC2Cxo/kKjRXw7YUpVNtxJccbvcl7VzTjUYgLQk6Ot0R8bm2netbhSZYWWNrKlOO/7HD6GXHl8dtzE6SiQ==", "dev": true, "dependencies": { "colors": "~1.0.3", @@ -2736,16 +2922,18 @@ }, "node_modules/cliff/node_modules/colors": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.1.90" } }, "node_modules/cliui": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -2753,11 +2941,15 @@ } }, "node_modules/clone-response": { - "version": "1.0.2", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, - "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/code-red": { @@ -2772,14 +2964,6 @@ "periscopic": "^3.1.0" } }, - "node_modules/code-red/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/codemirror-shadow-1": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/codemirror-shadow-1/-/codemirror-shadow-1-0.0.1.tgz", @@ -2787,35 +2971,40 @@ }, "node_modules/color-convert": { "version": "1.9.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { "color-name": "1.1.3" } }, "node_modules/color-name": { "version": "1.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/colors": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.1.90" } }, "node_modules/commander": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/commonmark": { "version": "0.30.0", + "resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.30.0.tgz", + "integrity": "sha512-j1yoUo4gxPND1JWV9xj5ELih0yMv1iCWDG6eEQIPLSWLxzCXiFoyS7kvB+WwU+tZMf4snwJMMtaubV0laFpiBA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "entities": "~2.0", "mdurl": "~1.0.1", @@ -2829,23 +3018,20 @@ "node": "*" } }, - "node_modules/commonmark/node_modules/entities": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/concat-map": { "version": "0.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/concat-stream": { "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "engines": [ "node >= 0.8" ], - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -2877,8 +3063,9 @@ }, "node_modules/concurrently/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -2891,8 +3078,9 @@ }, "node_modules/concurrently/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2906,8 +3094,9 @@ }, "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2917,8 +3106,9 @@ }, "node_modules/concurrently/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -2928,21 +3118,24 @@ }, "node_modules/concurrently/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/concurrently/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/concurrently/node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2954,7 +3147,9 @@ } }, "node_modules/config-chain": { - "version": "1.1.12", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, "optional": true, "dependencies": { @@ -2963,21 +3158,21 @@ } }, "node_modules/convert-source-map": { - "version": "1.7.0", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/core-util-is": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/cross-env": { "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", "dev": true, - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.1" }, @@ -2993,8 +3188,9 @@ }, "node_modules/cross-spawn": { "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3017,21 +3213,27 @@ } }, "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/cycle": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", "dev": true, "engines": { "node": ">=0.4.0" } }, "node_modules/date-fns": { - "version": "2.23.0", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "dev": true, - "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, "engines": { "node": ">=0.11" }, @@ -3042,7 +3244,8 @@ }, "node_modules/debug": { "version": "4.3.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -3057,16 +3260,19 @@ }, "node_modules/debuglog": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, - "license": "MIT", "engines": { "node": "*" } }, "node_modules/decompress-response": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", "dev": true, - "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" }, @@ -3075,9 +3281,10 @@ } }, "node_modules/deep-is": { - "version": "0.1.3", - "dev": true, - "license": "MIT" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/deepmerge": { "version": "4.3.1", @@ -3089,13 +3296,14 @@ }, "node_modules/defer-to-connect": { "version": "1.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true }, "node_modules/define-data-property": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", - "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, "dependencies": { "get-intrinsic": "^1.2.1", @@ -3133,8 +3341,9 @@ }, "node_modules/detect-node": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/dezalgo": { @@ -3161,8 +3370,9 @@ }, "node_modules/doctrine": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -3171,17 +3381,22 @@ } }, "node_modules/dotenv": { - "version": "16.0.0", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, "node_modules/duplexer3": { - "version": "0.1.4", - "dev": true, - "license": "BSD-3-Clause" + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "dev": true }, "node_modules/electron": { "version": "19.0.11", @@ -3202,46 +3417,27 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.454", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.454.tgz", - "integrity": "sha512-pmf1rbAStw8UEQ0sr2cdJtWl48ZMuPD9Sto8HVQOq9vx9j2WgDEN6lYoaqFvqEHYOmGA9oRGn7LqWI9ta0YugQ==" - }, - "node_modules/electron/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } + "version": "1.4.639", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.639.tgz", + "integrity": "sha512-CkKf3ZUVZchr+zDpAlNLEEy2NJJ9T64ULWaDgy3THXXlPVPkLu3VOs9Bac44nebVtdwl2geSj6AxTtGDOxoXhg==" }, - "node_modules/electron/node_modules/extract-zip": { - "version": "1.7.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - } - }, - "node_modules/electron/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" + "node_modules/electron/node_modules/@types/node": { + "version": "16.18.71", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.71.tgz", + "integrity": "sha512-ARO+458bNJQeNEFuPyT6W+q9ULotmsQzhV3XABsFSxEvRMUYENcBsNAHWYPlahU+UHa5gCVwyKT1Z3f1Wwr26Q==", + "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/encodeurl": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">= 0.8" @@ -3249,59 +3445,62 @@ }, "node_modules/end-of-stream": { "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/enquirer": { - "version": "2.3.6", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8.6" } }, "node_modules/entities": { - "version": "2.2.0", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "dev": true }, "node_modules/env-paths": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -3311,7 +3510,7 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", @@ -3325,7 +3524,7 @@ "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -3357,26 +3556,26 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -3398,14 +3597,16 @@ }, "node_modules/es6-error": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/esbuild": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.11.tgz", - "integrity": "sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -3414,369 +3615,56 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.11", - "@esbuild/android-arm64": "0.18.11", - "@esbuild/android-x64": "0.18.11", - "@esbuild/darwin-arm64": "0.18.11", - "@esbuild/darwin-x64": "0.18.11", - "@esbuild/freebsd-arm64": "0.18.11", - "@esbuild/freebsd-x64": "0.18.11", - "@esbuild/linux-arm": "0.18.11", - "@esbuild/linux-arm64": "0.18.11", - "@esbuild/linux-ia32": "0.18.11", - "@esbuild/linux-loong64": "0.18.11", - "@esbuild/linux-mips64el": "0.18.11", - "@esbuild/linux-ppc64": "0.18.11", - "@esbuild/linux-riscv64": "0.18.11", - "@esbuild/linux-s390x": "0.18.11", - "@esbuild/linux-x64": "0.18.11", - "@esbuild/netbsd-x64": "0.18.11", - "@esbuild/openbsd-x64": "0.18.11", - "@esbuild/sunos-x64": "0.18.11", - "@esbuild/win32-arm64": "0.18.11", - "@esbuild/win32-ia32": "0.18.11", - "@esbuild/win32-x64": "0.18.11" - } - }, - "node_modules/esbuild/node_modules/@esbuild/android-arm": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.11.tgz", - "integrity": "sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/android-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.11.tgz", - "integrity": "sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, - "node_modules/esbuild/node_modules/@esbuild/android-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.11.tgz", - "integrity": "sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "engines": { - "node": ">=12" + "node": ">=6" } }, - "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.11.tgz", - "integrity": "sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz", - "integrity": "sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.11.tgz", - "integrity": "sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.11.tgz", - "integrity": "sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/linux-arm": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.11.tgz", - "integrity": "sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.11.tgz", - "integrity": "sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.11.tgz", - "integrity": "sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.11.tgz", - "integrity": "sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.11.tgz", - "integrity": "sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.11.tgz", - "integrity": "sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.11.tgz", - "integrity": "sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/linux-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.11.tgz", - "integrity": "sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.11.tgz", - "integrity": "sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.11.tgz", - "integrity": "sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.11.tgz", - "integrity": "sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.11.tgz", - "integrity": "sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.11.tgz", - "integrity": "sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild/node_modules/@esbuild/win32-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.11.tgz", - "integrity": "sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "license": "MIT", - "engines": { - "node": ">=0.8.0" + "node": ">=0.8.0" } }, "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3828,8 +3716,9 @@ }, "node_modules/eslint-plugin-notice": { "version": "0.9.10", + "resolved": "https://registry.npmjs.org/eslint-plugin-notice/-/eslint-plugin-notice-0.9.10.tgz", + "integrity": "sha512-rF79EuqdJKu9hhTmwUkNeSvLmmq03m/NXq/NHwUENHbdJ0wtoyOjxZBhW4QCug8v5xYE6cGe3AWkGqSIe9KUbQ==", "dev": true, - "license": "MIT", "dependencies": { "find-root": "^1.1.0", "lodash": "^4.17.15", @@ -3870,9 +3759,10 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.3.0", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -3880,6 +3770,16 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -3892,21 +3792,16 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "*" } }, "node_modules/eslint-scope": { @@ -3939,8 +3834,9 @@ }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3951,10 +3847,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3968,8 +3875,9 @@ }, "node_modules/eslint/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3979,13 +3887,15 @@ }, "node_modules/eslint/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -3995,8 +3905,9 @@ }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -4005,9 +3916,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -4021,16 +3932,30 @@ }, "node_modules/eslint/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4038,12 +3963,24 @@ "node": ">=8" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" @@ -4089,20 +4026,56 @@ } }, "node_modules/estree-walker": { - "version": "2.0.2", - "license": "MIT", - "peer": true + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dev": true, + "dependencies": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/extract-zip/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/eyes": { "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", "dev": true, "engines": { "node": "> 0.1.90" @@ -4138,29 +4111,33 @@ }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fastq": { - "version": "1.11.1", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fd-slicer": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, - "license": "MIT", "dependencies": { "pend": "~1.2.0" } }, "node_modules/file-entry-cache": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -4170,8 +4147,9 @@ }, "node_modules/fill-range": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4181,8 +4159,9 @@ }, "node_modules/find-root": { "version": "1.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true }, "node_modules/find-up": { "version": "5.0.0", @@ -4201,36 +4180,24 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, - "license": "MIT", "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/flatted": { - "version": "3.1.1", - "dev": true, - "license": "ISC" + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true }, "node_modules/for-each": { "version": "0.3.3", @@ -4242,9 +4209,9 @@ } }, "node_modules/formidable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", - "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", "dev": true, "dependencies": { "dezalgo": "^1.0.4", @@ -4258,8 +4225,9 @@ }, "node_modules/fs-extra": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -4271,18 +4239,20 @@ }, "node_modules/fs-readdir-recursive": { "version": "1.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -4293,9 +4263,13 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "license": "MIT" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.6", @@ -4326,46 +4300,46 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "engines": { "node": ">=6.9.0" } }, "node_modules/get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-stream": { - "version": "5.2.0", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, - "license": "MIT", "dependencies": { "pump": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/get-symbol-description": { @@ -4385,14 +4359,15 @@ } }, "node_modules/glob": { - "version": "7.2.0", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -4405,8 +4380,9 @@ }, "node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -4414,10 +4390,33 @@ "node": ">= 6" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/global-agent": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, - "license": "BSD-3-Clause", "optional": true, "dependencies": { "boolean": "^3.0.1", @@ -4431,6 +4430,19 @@ "node": ">=10.0" } }, + "node_modules/global-agent/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/global-agent/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -4447,10 +4459,18 @@ "node": ">=10" } }, + "node_modules/global-agent/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "optional": true + }, "node_modules/global-tunnel-ng": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", "dev": true, - "license": "BSD-3-Clause", "optional": true, "dependencies": { "encodeurl": "^1.0.2", @@ -4464,15 +4484,17 @@ }, "node_modules/globals": { "version": "11.12.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "engines": { "node": ">=4" } }, "node_modules/globalthis": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.1.3" }, @@ -4526,8 +4548,9 @@ }, "node_modules/got": { "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", "dev": true, - "license": "MIT", "dependencies": { "@sindresorhus/is": "^0.14.0", "@szmarczak/http-timer": "^1.1.2", @@ -4545,21 +4568,11 @@ "node": ">=8.6" } }, - "node_modules/got/node_modules/get-stream": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/graceful-fs": { - "version": "4.2.9", - "dev": true, - "license": "ISC" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", @@ -4567,17 +4580,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -4589,18 +4591,19 @@ }, "node_modules/has-flag": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { "node": ">=4" } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4645,18 +4648,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hexoid": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/hosted-git-info": { "version": "2.8.9", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, "node_modules/html-entities": { "version": "2.3.3", @@ -4674,9 +4691,9 @@ "dev": true }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, "engines": { "node": ">= 4" @@ -4700,16 +4717,18 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -4717,23 +4736,25 @@ }, "node_modules/inherits": { "version": "2.0.4", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true, - "license": "ISC", "optional": true }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -4781,6 +4802,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -4810,11 +4843,12 @@ } }, "node_modules/is-core-module": { - "version": "2.9.0", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, - "license": "MIT", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4837,8 +4871,9 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -4857,8 +4892,9 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -4880,8 +4916,9 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -4912,8 +4949,9 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -5067,9 +5105,9 @@ } }, "node_modules/is-what": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz", - "integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==", + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", "engines": { "node": ">=12.13" }, @@ -5079,18 +5117,21 @@ }, "node_modules/isarray": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isstream": { "version": "0.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true }, "node_modules/iterator.prototype": { "version": "1.1.2", @@ -5107,7 +5148,8 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -5123,7 +5165,8 @@ }, "node_modules/jsesc": { "version": "2.5.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "bin": { "jsesc": "bin/jsesc" }, @@ -5132,14 +5175,16 @@ } }, "node_modules/json-buffer": { - "version": "3.0.0", - "dev": true, - "license": "MIT" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -5149,13 +5194,15 @@ }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "node_modules/json-stringify-safe": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, - "license": "ISC", "optional": true }, "node_modules/json5": { @@ -5171,8 +5218,9 @@ }, "node_modules/jsonfile": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -5193,33 +5241,27 @@ } }, "node_modules/keyv": { - "version": "3.1.0", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "MIT", "dependencies": { - "json-buffer": "3.0.0" + "json-buffer": "3.0.1" } }, "node_modules/kleur": { "version": "4.1.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "engines": { "node": ">=6" } }, - "node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -5230,8 +5272,9 @@ }, "node_modules/license-checker": { "version": "25.0.1", + "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", + "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "chalk": "^2.4.1", "debug": "^3.1.0", @@ -5250,8 +5293,9 @@ }, "node_modules/license-checker/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -5287,18 +5331,21 @@ }, "node_modules/lodash": { "version": "4.17.21", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, "node_modules/loose-envify": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, - "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5308,30 +5355,27 @@ }, "node_modules/lowercase-keys": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "yallist": "^3.0.2" } }, "node_modules/magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", - "dev": true, + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" @@ -5339,8 +5383,9 @@ }, "node_modules/make-dir": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, - "license": "MIT", "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -5360,8 +5405,9 @@ }, "node_modules/matcher": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "escape-string-regexp": "^4.0.0" @@ -5372,8 +5418,9 @@ }, "node_modules/matcher/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=10" @@ -5389,13 +5436,14 @@ }, "node_modules/mdurl": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true }, "node_modules/merge-anything": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.4.tgz", - "integrity": "sha512-7PWKwGOs5WWcpw+/OvbiFiAvEP6bv/QHiicigpqMGKIqPPAtGhBLR8LFJW+Zu6m9TXiR/a8+AiPlGG0ko1ruoQ==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz", + "integrity": "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==", "dependencies": { "is-what": "^4.1.8" }, @@ -5417,8 +5465,9 @@ }, "node_modules/metric-lcs": { "version": "0.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/metric-lcs/-/metric-lcs-0.1.2.tgz", + "integrity": "sha512-+TZ5dUDPKPJaU/rscTzxyN8ZkX7eAVLAiQU/e+YINleXPv03SCmJShaMT1If1liTH8OcmWXZs0CmzCBRBLcMpA==", + "dev": true }, "node_modules/micromatch": { "version": "4.0.5", @@ -5435,8 +5484,9 @@ }, "node_modules/mime": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -5446,52 +5496,53 @@ }, "node_modules/mimic-response": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { - "version": "1.2.6", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "license": "MIT" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mkdirp": { - "version": "0.5.5", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "license": "MIT", "dependencies": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, - "node_modules/mri": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", - "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/ms": { "version": "2.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { "version": "3.3.7", @@ -5512,26 +5563,20 @@ }, "node_modules/natural-compare": { "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/ncp": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "bin": { - "ncp": "bin/ncp" - } + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/node-stream-zip": { "version": "1.15.0", + "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" }, @@ -5542,8 +5587,9 @@ }, "node_modules/nopt": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, - "license": "ISC", "dependencies": { "abbrev": "1", "osenv": "^0.1.4" @@ -5554,8 +5600,9 @@ }, "node_modules/normalize-package-data": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -5563,6 +5610,23 @@ "validate-npm-package-license": "^3.0.1" } }, + "node_modules/normalize-package-data/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/normalize-package-data/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -5574,24 +5638,27 @@ }, "node_modules/normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/normalize-url": { "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/npm-conf": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "config-chain": "^1.1.11", @@ -5603,8 +5670,9 @@ }, "node_modules/npm-conf/node_modules/pify": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=4" @@ -5612,8 +5680,9 @@ }, "node_modules/npm-normalize-package-bin": { "version": "1.0.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true }, "node_modules/object-assign": { "version": "4.1.1", @@ -5625,9 +5694,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5635,20 +5704,21 @@ }, "node_modules/object-keys": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -5722,8 +5792,9 @@ }, "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "ISC", "dependencies": { "wrappy": "1" } @@ -5747,24 +5818,27 @@ }, "node_modules/os-homedir": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/os-tmpdir": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/osenv": { "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, - "license": "ISC", "dependencies": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -5772,8 +5846,9 @@ }, "node_modules/p-cancelable": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -5831,24 +5906,27 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-type": { "version": "4.0.0", @@ -5861,8 +5939,9 @@ }, "node_modules/pend": { "version": "1.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true }, "node_modules/periscopic": { "version": "3.1.0", @@ -5874,17 +5953,10 @@ "is-reference": "^3.0.0" } }, - "node_modules/periscopic/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/picocolors": { "version": "1.0.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -5900,16 +5972,18 @@ }, "node_modules/pify": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/pkginfo": { - "version": "0.4.0-next", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -5935,9 +6009,9 @@ "link": true }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "funding": [ { "type": "opencollective", @@ -5963,29 +6037,48 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/prepend-http": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "node_modules/progress": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -6003,28 +6096,16 @@ }, "node_modules/proto-list": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true, - "license": "ISC", "optional": true }, - "node_modules/proxy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz", - "integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==", - "dev": true, - "dependencies": { - "args": "^5.0.3", - "basic-auth-parser": "0.0.2-1", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/pump": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -6040,9 +6121,9 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -6056,6 +6137,8 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -6070,13 +6153,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/react": { - "version": "18.1.0", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "dev": true, - "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -6085,15 +6168,16 @@ } }, "node_modules/react-dom": { - "version": "18.1.0", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dev": true, - "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.22.0" + "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.1.0" + "react": "^18.2.0" } }, "node_modules/react-is": { @@ -6112,8 +6196,9 @@ }, "node_modules/read-installed": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", "dev": true, - "license": "ISC", "dependencies": { "debuglog": "^1.0.1", "read-package-json": "^2.0.0", @@ -6137,8 +6222,9 @@ }, "node_modules/read-package-json": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", "dev": true, - "license": "ISC", "dependencies": { "glob": "^7.1.1", "json-parse-even-better-errors": "^2.3.0", @@ -6147,9 +6233,10 @@ } }, "node_modules/readable-stream": { - "version": "2.3.7", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6162,8 +6249,10 @@ }, "node_modules/readdir-scoped-modules": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, - "license": "ISC", "dependencies": { "debuglog": "^1.0.1", "dezalgo": "^1.0.0", @@ -6173,8 +6262,9 @@ }, "node_modules/readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -6206,6 +6296,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -6225,18 +6321,20 @@ }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.22.1", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, - "license": "MIT", "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -6258,25 +6356,43 @@ }, "node_modules/responselike": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", "dev": true, - "license": "MIT", "dependencies": { "lowercase-keys": "^1.0.0" } }, "node_modules/reusify": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/roarr": { "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, - "license": "BSD-3-Clause", "optional": true, "dependencies": { "boolean": "^3.0.1", @@ -6291,22 +6407,40 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.5.tgz", + "integrity": "sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==", + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.5", + "@rollup/rollup-android-arm64": "4.9.5", + "@rollup/rollup-darwin-arm64": "4.9.5", + "@rollup/rollup-darwin-x64": "4.9.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.5", + "@rollup/rollup-linux-arm64-gnu": "4.9.5", + "@rollup/rollup-linux-arm64-musl": "4.9.5", + "@rollup/rollup-linux-riscv64-gnu": "4.9.5", + "@rollup/rollup-linux-x64-gnu": "4.9.5", + "@rollup/rollup-linux-x64-musl": "4.9.5", + "@rollup/rollup-win32-arm64-msvc": "4.9.5", + "@rollup/rollup-win32-ia32-msvc": "4.9.5", + "@rollup/rollup-win32-x64-msvc": "4.9.5", "fsevents": "~2.3.2" } }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -6322,15 +6456,15 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^1.9.0" }, @@ -6339,13 +6473,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -6364,31 +6498,38 @@ }, "node_modules/safe-buffer": { "version": "5.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", + "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/sax": { - "version": "1.2.4", - "dev": true, - "license": "ISC" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "dev": true }, "node_modules/scheduler": { - "version": "0.22.0", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dev": true, - "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -6403,14 +6544,16 @@ }, "node_modules/semver-compare": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/serialize-error": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "type-fest": "^0.13.1" @@ -6422,24 +6565,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "optional": true, + "node_modules/seroval": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.4.tgz", + "integrity": "sha512-qQs/N+KfJu83rmszFQaTxcoJoPn6KNUruX4KmnmyD0oZkUoiNvJ1rpdYKDf4YHM05k+HOgCxa3yvf15QbVijGg==", "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/seroval": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-0.5.1.tgz", - "integrity": "sha512-ZfhQVB59hmIauJG5Ydynupy8KHyr5imGNtdDhbZG68Ufh1Ynkv9KOYOAABf71oVbQxJ8VkWnMHAjEHE7fWkH5g==", + "node_modules/seroval-plugins": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.4.tgz", + "integrity": "sha512-DQ2IK6oQVvy8k+c2V5x5YCtUa/GGGsUwUBNN9UqohrZ0rWdUapBFpNMYP1bCyRHoxOJjdKGl+dieacFIpU/i1A==", "engines": { "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/set-function-name": { @@ -6458,8 +6616,9 @@ }, "node_modules/shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -6469,8 +6628,9 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -6491,22 +6651,26 @@ }, "node_modules/slash": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/slide": { "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", "dev": true, - "license": "ISC", "engines": { "node": "*" } }, "node_modules/socksv5": { "version": "0.0.6", + "resolved": "https://registry.npmjs.org/socksv5/-/socksv5-0.0.6.tgz", + "integrity": "sha512-tQpQ0MdNQAsQBDhCXy3OvGGJikh9QOl3PkbwT4POJiQCm/fK4z9AxKQQRG8WLeF6talphnPrSWiZRpTl42rApg==", "bundleDependencies": [ "ipv6" ], @@ -6545,22 +6709,23 @@ } }, "node_modules/solid-js": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.7.3.tgz", - "integrity": "sha512-4hwaF/zV/xbNeBBIYDyu3dcReOZBECbO//mrra6GqOrKy4Soyo+fnKjpZSa0nODm6j1aL0iQRh/7ofYowH+jzw==", + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.11.tgz", + "integrity": "sha512-WdwmER+TwBJiN4rVQTVBxocg+9pKlOs41KzPYntrC86xO5sek8TzBYozPEZPL1IRWDouf2lMrvSbIs3CanlPvQ==", "dependencies": { "csstype": "^3.1.0", - "seroval": "^0.5.0" + "seroval": "^1.0.3", + "seroval-plugins": "^1.0.3" } }, "node_modules/solid-refresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.5.2.tgz", - "integrity": "sha512-I69HmFj0LsGRJ3n8CEMVjyQFgVtuM2bSjznu2hCnsY+i5oOxh8ioWj00nnHBv0UYD3WpE/Sq4Q3TNw2IKmKN7A==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.6.3.tgz", + "integrity": "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==", "dependencies": { - "@babel/generator": "^7.21.1", - "@babel/helper-module-imports": "^7.18.6", - "@babel/types": "^7.21.2" + "@babel/generator": "^7.23.6", + "@babel/helper-module-imports": "^7.22.15", + "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" @@ -6576,25 +6741,23 @@ }, "node_modules/source-map-js": { "version": "1.0.2", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "engines": { "node": ">=0.10.0" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "license": "MIT", - "peer": true - }, "node_modules/spawn-command": { "version": "0.0.2-1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true }, "node_modules/spdx-compare": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", + "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", "dev": true, - "license": "MIT", "dependencies": { "array-find-index": "^1.0.2", "spdx-expression-parse": "^3.0.0", @@ -6602,9 +6765,10 @@ } }, "node_modules/spdx-correct": { - "version": "3.1.1", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -6612,32 +6776,37 @@ }, "node_modules/spdx-exceptions": { "version": "2.3.0", - "dev": true, - "license": "CC-BY-3.0" + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, - "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { - "version": "3.0.10", - "dev": true, - "license": "CC0-1.0" + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true }, "node_modules/spdx-ranges": { "version": "2.1.1", - "dev": true, - "license": "(MIT AND CC-BY-3.0)" + "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", + "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", + "dev": true }, "node_modules/spdx-satisfies": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz", + "integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==", "dev": true, - "license": "MIT", "dependencies": { "spdx-compare": "^1.0.0", "spdx-expression-parse": "^3.0.0", @@ -6645,9 +6814,10 @@ } }, "node_modules/sprintf-js": { - "version": "1.1.2", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true, - "license": "BSD-3-Clause", "optional": true }, "node_modules/ssim.js": { @@ -6658,28 +6828,31 @@ }, "node_modules/stack-trace": { "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "dev": true, - "license": "MIT", "engines": { "node": "*" } }, "node_modules/string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/string-width": { - "version": "4.2.2", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" @@ -6707,6 +6880,8 @@ }, "node_modules/string.prototype.repeat": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz", + "integrity": "sha512-1BH+X+1hSthZFW+X+JaUkjkkUPwIlLEMJBLANN3hOob3RhEk5snLWNECDnYbgn/m5c5JV7Ersu1Yubaf+05cIA==", "dev": true }, "node_modules/string.prototype.trim": { @@ -6756,8 +6931,9 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -6779,8 +6955,9 @@ }, "node_modules/sumchecker": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "debug": "^4.1.0" }, @@ -6790,7 +6967,8 @@ }, "node_modules/supports-color": { "version": "5.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { "has-flag": "^3.0.0" }, @@ -6800,8 +6978,9 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6810,12 +6989,27 @@ } }, "node_modules/svelte": { - "version": "3.55.1", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz", - "integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==", - "peer": true, + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.9.tgz", + "integrity": "sha512-hsoB/WZGEPFXeRRLPhPrbRz67PhP6sqYgvwcAs+gWdSQSvNDw+/lTeUJSWe5h2xC97Fz/8QxAOqItwBzNJPU8w==", + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, "engines": { - "node": ">= 8" + "node": ">=16" } }, "node_modules/svelte-hmr": { @@ -6831,28 +7025,32 @@ }, "node_modules/text-table": { "version": "0.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/to-fast-properties": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "engines": { "node": ">=4" } }, "node_modules/to-readable-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -6866,24 +7064,26 @@ }, "node_modules/tree-kill": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, - "license": "MIT", "bin": { "tree-kill": "cli.js" } }, "node_modules/treeify": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.6" } }, "node_modules/ts-api-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", - "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", "dev": true, "engines": { "node": ">=16.13.0" @@ -6894,21 +7094,24 @@ }, "node_modules/tslib": { "version": "1.14.1", - "dev": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "node_modules/tunnel": { "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } }, "node_modules/type-check": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -6917,10 +7120,11 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, + "optional": true, "engines": { "node": ">=10" }, @@ -6995,14 +7199,15 @@ }, "node_modules/typedarray": { "version": "0.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", - "dev": true, + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7026,25 +7231,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "5.28.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", + "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "dev": true, + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "optional": true, - "peer": true + "devOptional": true }, "node_modules/universalify": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4.0.0" } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "funding": [ { "type": "opencollective", @@ -7081,8 +7298,9 @@ }, "node_modules/url-parse-lax": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", "dev": true, - "license": "MIT", "dependencies": { "prepend-http": "^2.0.0" }, @@ -7092,13 +7310,15 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true }, "node_modules/util-extend": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==", + "dev": true }, "node_modules/uuid": { "version": "8.3.2", @@ -7116,36 +7336,37 @@ }, "node_modules/validate-npm-package-license": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "node_modules/vite": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.12.tgz", - "integrity": "sha512-KtPlUbWfxzGVul8Nut8Gw2Qe8sBzWY+8QVc5SL8iRFnpnrcoCaNlzO40c1R6hPmcdTwIPEDkq0Y9+27a5tVbdQ==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", @@ -7178,805 +7399,826 @@ } }, "node_modules/vite-plugin-solid": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.7.0.tgz", - "integrity": "sha512-avp/Jl5zOp/Itfo67xtDB2O61U7idviaIp4mLsjhCa13PjKNasz+IID0jYTyqUp9SFx6/PmBr6v4KgDppqompg==", - "dependencies": { - "@babel/core": "^7.20.5", - "@babel/preset-typescript": "^7.18.6", - "@types/babel__core": "^7.1.20", - "babel-preset-solid": "^1.7.2", - "merge-anything": "^5.1.4", - "solid-refresh": "^0.5.0", - "vitefu": "^0.2.3" + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.8.2.tgz", + "integrity": "sha512-HcvMs6DTxBaO4kE3psnirPQBCUUdYeQkCNKuB2TpEkJsxb6BGP6/7qkbbCSMxn25PyNdjvzVi1WXi0ou8KPgHw==", + "dependencies": { + "@babel/core": "^7.23.3", + "@babel/preset-typescript": "^7.23.3", + "@types/babel__core": "^7.20.4", + "babel-preset-solid": "^1.8.4", + "merge-anything": "^5.1.7", + "solid-refresh": "^0.6.3", + "vitefu": "^0.2.5" }, "peerDependencies": { "solid-js": "^1.7.2", - "vite": "^3.0.0 || ^4.0.0" - } - }, - "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", - "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } } }, - "node_modules/vue": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.14.tgz", - "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==", - "dependencies": { - "@vue/compiler-sfc": "2.7.14", - "csstype": "^3.1.0" + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", + "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/vue/node_modules/@vue/compiler-sfc": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz", - "integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==", - "dependencies": { - "@babel/parser": "^7.18.4", - "postcss": "^8.4.14", - "source-map": "^0.6.1" + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", + "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/web": { - "resolved": "packages/web", - "link": true - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", + "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", + "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", + "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/which-builtin-type/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", + "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", + "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/winston": { - "version": "0.8.3", - "dev": true, - "dependencies": { - "async": "0.2.x", - "colors": "0.6.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "pkginfo": "0.3.x", - "stack-trace": "0.0.x" - }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", + "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.6.0" + "node": ">=12" } }, - "node_modules/winston/node_modules/colors": { - "version": "0.6.2", - "dev": true, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", + "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.1.90" + "node": ">=12" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", + "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=12" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", + "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", + "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=7.0.0" + "node": ">=12" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.5.0", - "dev": true, - "license": "MIT", + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", + "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": ">=12" } }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dev": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", + "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4.0.0" + "node": ">=12" } }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "dev": true, - "license": "MIT", + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", + "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4.0" - } - }, - "node_modules/xterm": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.1.0.tgz", - "integrity": "sha512-LovENH4WDzpwynj+OTkLyZgJPeDom9Gra4DMlGAgz6pZhIDCQ+YuO7yfwanY+gVbn/mmZIStNOnVRU/ikQuAEQ==" - }, - "node_modules/xterm-addon-fit": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz", - "integrity": "sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==", - "peerDependencies": { - "xterm": "^5.0.0" + "node": ">=12" } }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", + "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 14" + "node": ">=12" } }, - "node_modules/yargs": { - "version": "16.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", + "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "dev": true, - "license": "ISC", + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", + "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/yargs/node_modules/y18n": { - "version": "5.0.8", - "dev": true, - "license": "ISC", + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", + "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", + "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", + "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "packages/html-reporter": { - "version": "0.0.0", - "dependencies": { - "ansi-to-html": "^0.7.2" + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", + "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "packages/playwright": { - "version": "1.41.1", - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.41.1" - }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", + "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "hasInstallScript": true, "bin": { - "playwright": "cli.js" + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=16" + "node": ">=12" }, "optionalDependencies": { - "fsevents": "2.3.2" + "@esbuild/aix-ppc64": "0.19.11", + "@esbuild/android-arm": "0.19.11", + "@esbuild/android-arm64": "0.19.11", + "@esbuild/android-x64": "0.19.11", + "@esbuild/darwin-arm64": "0.19.11", + "@esbuild/darwin-x64": "0.19.11", + "@esbuild/freebsd-arm64": "0.19.11", + "@esbuild/freebsd-x64": "0.19.11", + "@esbuild/linux-arm": "0.19.11", + "@esbuild/linux-arm64": "0.19.11", + "@esbuild/linux-ia32": "0.19.11", + "@esbuild/linux-loong64": "0.19.11", + "@esbuild/linux-mips64el": "0.19.11", + "@esbuild/linux-ppc64": "0.19.11", + "@esbuild/linux-riscv64": "0.19.11", + "@esbuild/linux-s390x": "0.19.11", + "@esbuild/linux-x64": "0.19.11", + "@esbuild/netbsd-x64": "0.19.11", + "@esbuild/openbsd-x64": "0.19.11", + "@esbuild/sunos-x64": "0.19.11", + "@esbuild/win32-arm64": "0.19.11", + "@esbuild/win32-ia32": "0.19.11", + "@esbuild/win32-x64": "0.19.11" } }, - "packages/playwright-browser-chromium": { - "name": "@playwright/browser-chromium", - "version": "1.41.1", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.41.1" + "node_modules/vitefu": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", + "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, - "engines": { - "node": ">=16" + "peerDependenciesMeta": { + "vite": { + "optional": true + } } }, - "packages/playwright-browser-firefox": { - "name": "@playwright/browser-firefox", - "version": "1.41.1", - "hasInstallScript": true, - "license": "Apache-2.0", + "node_modules/vue": { + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz", + "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==", + "peer": true, "dependencies": { - "playwright-core": "1.41.1" + "@vue/compiler-dom": "3.4.15", + "@vue/compiler-sfc": "3.4.15", + "@vue/runtime-dom": "3.4.15", + "@vue/server-renderer": "3.4.15", + "@vue/shared": "3.4.15" }, - "engines": { - "node": ">=16" + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "packages/playwright-browser-webkit": { - "name": "@playwright/browser-webkit", - "version": "1.41.1", - "hasInstallScript": true, - "license": "Apache-2.0", + "node_modules/web": { + "resolved": "packages/web", + "link": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { - "playwright-core": "1.41.1" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=16" + "node": ">= 8" } }, - "packages/playwright-chromium": { - "version": "1.41.1", - "hasInstallScript": true, - "license": "Apache-2.0", + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, "dependencies": { - "playwright-core": "1.41.1" - }, - "bin": { - "playwright": "cli.js" + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" }, - "engines": { - "node": ">=16" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "packages/playwright-core": { - "version": "1.41.1", - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" }, "engines": { - "node": ">=16" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "packages/playwright-ct-core": { - "name": "@playwright/experimental-ct-core", - "version": "1.41.1", - "license": "Apache-2.0", + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, "dependencies": { - "playwright": "1.41.1", - "playwright-core": "1.41.1", - "vite": "^4.4.12" - }, - "bin": { - "playwright": "cli.js" + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" }, - "engines": { - "node": ">=16" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "packages/playwright-ct-react": { - "name": "@playwright/experimental-ct-react", - "version": "1.41.1", - "license": "Apache-2.0", + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-react": "^4.0.0" - }, - "bin": { - "playwright": "cli.js" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=16" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "packages/playwright-ct-react/node_modules/@vitejs/plugin-react": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.0.tgz", - "integrity": "sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==", + "node_modules/winston": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha512-fPoamsHq8leJ62D1M9V/f15mjQ1UHe4+7j1wpAT3fqgA5JqhJkk4aIfPEjfMTI9x6ZTjaLOpMAjluLtmgO5b6g==", + "dev": true, "dependencies": { - "@babel/core": "^7.21.4", - "@babel/plugin-transform-react-jsx-self": "^7.21.0", - "@babel/plugin-transform-react-jsx-source": "^7.19.6", - "react-refresh": "^0.14.0" + "async": "0.2.x", + "colors": "0.6.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0" + "node": ">= 0.6.0" } }, - "packages/playwright-ct-react17": { - "name": "@playwright/experimental-ct-react17", - "version": "1.41.1", - "license": "Apache-2.0", - "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-react": "^4.0.0" - }, - "bin": { - "playwright": "cli.js" - }, + "node_modules/winston/node_modules/colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==", + "dev": true, "engines": { - "node": ">=16" + "node": ">=0.1.90" } }, - "packages/playwright-ct-react17/node_modules/@vitejs/plugin-react": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.0.tgz", - "integrity": "sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==", + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { - "@babel/core": "^7.21.4", - "@babel/plugin-transform-react-jsx-self": "^7.21.0", - "@babel/plugin-transform-react-jsx-source": "^7.19.6", - "react-refresh": "^0.14.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">=10" }, - "peerDependencies": { - "vite": "^4.2.0" + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "packages/playwright-ct-solid": { - "name": "@playwright/experimental-ct-solid", - "version": "1.41.1", - "license": "Apache-2.0", + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", - "vite-plugin-solid": "^2.7.0" - }, - "bin": { - "playwright": "cli.js" - }, - "devDependencies": { - "solid-js": "^1.7.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=16" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "packages/playwright-ct-svelte": { - "name": "@playwright/experimental-ct-svelte", - "version": "1.41.1", - "license": "Apache-2.0", + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", - "@sveltejs/vite-plugin-svelte": "^3.0.1" - }, - "bin": { - "playwright": "cli.js" - }, - "devDependencies": { - "svelte": "^4.2.8" + "color-name": "~1.1.4" }, "engines": { - "node": ">=16" + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0" + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { - "@sveltejs/vite-plugin-svelte": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { "optional": true } } }, - "packages/playwright-ct-svelte/node_modules/@esbuild/linux-loong64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz", - "integrity": "sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "packages/playwright-ct-svelte/node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.1.tgz", - "integrity": "sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==", + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0-next.0 || ^2.0.0", - "debug": "^4.3.4", - "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "svelte-hmr": "^0.15.3", - "vitefu": "^0.2.5" + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" }, "engines": { - "node": "^18.0.0 || >=20" - }, - "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "node": ">=4.0.0" } }, - "packages/playwright-ct-svelte/node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.0.0.tgz", - "integrity": "sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==", - "dependencies": { - "debug": "^4.3.4" - }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, "engines": { - "node": "^18.0.0 || >=20" - }, + "node": ">=4.0" + } + }, + "node_modules/xterm": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz", + "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==" + }, + "node_modules/xterm-addon-fit": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz", + "integrity": "sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==", "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "xterm": "^5.0.0" } }, - "packages/playwright-ct-svelte/node_modules/@types/node": { - "version": "20.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", - "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", - "optional": true, - "peer": true, - "dependencies": { - "undici-types": "~5.26.4" + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" } }, - "packages/playwright-ct-svelte/node_modules/esbuild": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.9.tgz", - "integrity": "sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==", - "hasInstallScript": true, - "peer": true, - "bin": { - "esbuild": "bin/esbuild" - }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.19.9", - "@esbuild/android-arm64": "0.19.9", - "@esbuild/android-x64": "0.19.9", - "@esbuild/darwin-arm64": "0.19.9", - "@esbuild/darwin-x64": "0.19.9", - "@esbuild/freebsd-arm64": "0.19.9", - "@esbuild/freebsd-x64": "0.19.9", - "@esbuild/linux-arm": "0.19.9", - "@esbuild/linux-arm64": "0.19.9", - "@esbuild/linux-ia32": "0.19.9", - "@esbuild/linux-loong64": "0.19.9", - "@esbuild/linux-mips64el": "0.19.9", - "@esbuild/linux-ppc64": "0.19.9", - "@esbuild/linux-riscv64": "0.19.9", - "@esbuild/linux-s390x": "0.19.9", - "@esbuild/linux-x64": "0.19.9", - "@esbuild/netbsd-x64": "0.19.9", - "@esbuild/openbsd-x64": "0.19.9", - "@esbuild/sunos-x64": "0.19.9", - "@esbuild/win32-arm64": "0.19.9", - "@esbuild/win32-ia32": "0.19.9", - "@esbuild/win32-x64": "0.19.9" - } - }, - "packages/playwright-ct-svelte/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { - "@types/estree": "^1.0.0" + "node": ">= 14" } }, - "packages/playwright-ct-svelte/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=10" } }, - "packages/playwright-ct-svelte/node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/playwright-ct-svelte/node_modules/rollup": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.0.tgz", - "integrity": "sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A==", - "peer": true, + "packages/html-reporter": { + "version": "0.0.0", + "dependencies": { + "ansi-to-html": "^0.7.2" + } + }, + "packages/playwright": { + "version": "1.42.0", + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.42.0" + }, "bin": { - "rollup": "dist/bin/rollup" + "playwright": "cli.js" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">=16" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.0", - "@rollup/rollup-android-arm64": "4.9.0", - "@rollup/rollup-darwin-arm64": "4.9.0", - "@rollup/rollup-darwin-x64": "4.9.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.0", - "@rollup/rollup-linux-arm64-gnu": "4.9.0", - "@rollup/rollup-linux-arm64-musl": "4.9.0", - "@rollup/rollup-linux-riscv64-gnu": "4.9.0", - "@rollup/rollup-linux-x64-gnu": "4.9.0", - "@rollup/rollup-linux-x64-musl": "4.9.0", - "@rollup/rollup-win32-arm64-msvc": "4.9.0", - "@rollup/rollup-win32-ia32-msvc": "4.9.0", - "@rollup/rollup-win32-x64-msvc": "4.9.0", - "fsevents": "~2.3.2" + "fsevents": "2.3.2" } }, - "packages/playwright-ct-svelte/node_modules/svelte": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.8.tgz", - "integrity": "sha512-hU6dh1MPl8gh6klQZwK/n73GiAHiR95IkFsesLPbMeEZi36ydaXL/ZAb4g9sayT0MXzpxyZjR28yderJHxcmYA==", + "packages/playwright-browser-chromium": { + "name": "@playwright/browser-chromium", + "version": "1.42.0", + "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^3.2.1", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" + "playwright-core": "1.42.0" }, "engines": { "node": ">=16" } }, - "packages/playwright-ct-svelte/node_modules/vite": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.8.tgz", - "integrity": "sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==", - "peer": true, + "packages/playwright-browser-firefox": { + "name": "@playwright/browser-firefox", + "version": "1.42.0", + "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.32", - "rollup": "^4.2.0" - }, - "bin": { - "vite": "bin/vite.js" + "playwright-core": "1.42.0" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": ">=16" + } + }, + "packages/playwright-browser-webkit": { + "name": "@playwright/browser-webkit", + "version": "1.42.0", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.42.0" }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" + "engines": { + "node": ">=16" + } + }, + "packages/playwright-chromium": { + "version": "1.42.0", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.42.0" }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "bin": { + "playwright": "cli.js" }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "engines": { + "node": ">=16" + } + }, + "packages/playwright-core": { + "version": "1.42.0", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "engines": { + "node": ">=16" } }, - "packages/playwright-ct-vue": { - "name": "@playwright/experimental-ct-vue", - "version": "1.41.1", + "packages/playwright-ct-core": { + "name": "@playwright/experimental-ct-core", + "version": "1.42.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-vue": "^4.2.1" + "playwright": "1.42.0", + "playwright-core": "1.42.0", + "vite": "^5.0.12" }, "bin": { "playwright": "cli.js" @@ -7985,92 +8227,155 @@ "node": ">=16" } }, - "packages/playwright-ct-vue/node_modules/@vitejs/plugin-vue": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.1.tgz", - "integrity": "sha512-ZTZjzo7bmxTRTkb8GSTwkPOYDIP7pwuyV+RV53c9PYUouwcbkIZIvWvNWlX2b1dYZqtOv7D6iUAnJLVNGcLrSw==", - "engines": { - "node": "^14.18.0 || >=16.0.0" + "packages/playwright-ct-react": { + "name": "@playwright/experimental-ct-react", + "version": "1.42.0", + "license": "Apache-2.0", + "dependencies": { + "@playwright/experimental-ct-core": "1.42.0", + "@vitejs/plugin-react": "^4.2.1" }, - "peerDependencies": { - "vite": "^4.0.0", - "vue": "^3.2.25" + "bin": { + "playwright": "cli.js", + "pw-react": "cli.js" + }, + "engines": { + "node": ">=16" } }, - "packages/playwright-ct-vue/node_modules/@vue/server-renderer": { - "version": "3.2.36", - "license": "MIT", - "peer": true, + "packages/playwright-ct-react17": { + "name": "@playwright/experimental-ct-react17", + "version": "1.42.0", + "license": "Apache-2.0", "dependencies": { - "@vue/compiler-ssr": "3.2.36", - "@vue/shared": "3.2.36" + "@playwright/experimental-ct-core": "1.42.0", + "@vitejs/plugin-react": "^4.2.1" }, - "peerDependencies": { - "vue": "3.2.36" + "bin": { + "playwright": "cli.js", + "pw-react17": "cli.js" + }, + "engines": { + "node": ">=16" } }, - "packages/playwright-ct-vue/node_modules/vue": { - "version": "3.2.36", - "license": "MIT", - "peer": true, + "packages/playwright-ct-solid": { + "name": "@playwright/experimental-ct-solid", + "version": "1.42.0", + "license": "Apache-2.0", "dependencies": { - "@vue/compiler-dom": "3.2.36", - "@vue/compiler-sfc": "3.2.36", - "@vue/runtime-dom": "3.2.36", - "@vue/server-renderer": "3.2.36", - "@vue/shared": "3.2.36" + "@playwright/experimental-ct-core": "1.42.0", + "vite-plugin-solid": "^2.7.0" + }, + "bin": { + "playwright": "cli.js", + "pw-solid": "cli.js" + }, + "devDependencies": { + "solid-js": "^1.7.0" + }, + "engines": { + "node": ">=16" } }, - "packages/playwright-ct-vue2": { - "name": "@playwright/experimental-ct-vue2", - "version": "1.41.1", + "packages/playwright-ct-svelte": { + "name": "@playwright/experimental-ct-svelte", + "version": "1.42.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-vue2": "^2.2.0" + "@playwright/experimental-ct-core": "1.42.0", + "@sveltejs/vite-plugin-svelte": "^3.0.1" }, "bin": { - "playwright": "cli.js" + "playwright": "cli.js", + "pw-svelte": "cli.js" }, "devDependencies": { - "vue": "^2.7.14" + "svelte": "^4.2.8" }, "engines": { "node": ">=16" } }, - "packages/playwright-firefox": { - "version": "1.41.1", - "hasInstallScript": true, + "packages/playwright-ct-vue": { + "name": "@playwright/experimental-ct-vue", + "version": "1.42.0", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.41.1" + "@playwright/experimental-ct-core": "1.42.0", + "@vitejs/plugin-vue": "^4.2.1" }, "bin": { - "playwright": "cli.js" + "playwright": "cli.js", + "pw-vue": "cli.js" }, "engines": { "node": ">=16" } }, - "packages/playwright-grid": { - "name": "@playwright/experimental-grid", - "version": "0.0.1", - "extraneous": true, + "packages/playwright-ct-vue2": { + "name": "@playwright/experimental-ct-vue2", + "version": "1.42.0", "license": "Apache-2.0", "dependencies": { - "commander": "^11.0.0", - "debug": "^4.3.2", - "playwright-core": "1.38.0-alpha-aug-10-2023", - "ws": "^8.1.0" + "@playwright/experimental-ct-core": "1.42.0", + "@vitejs/plugin-vue2": "^2.2.0" }, "bin": { - "playwright-grid": "cli.js" + "playwright": "cli.js", + "pw-vue2": "cli.js" }, "devDependencies": { - "@types/commander": "^2.12.2", - "@types/debug": "^4.1.8", - "@types/ws": "^8.5.5" + "vue": "^2.7.14" + }, + "engines": { + "node": ">=16" + } + }, + "packages/playwright-ct-vue2/node_modules/@vitejs/plugin-vue2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue2/-/plugin-vue2-2.3.1.tgz", + "integrity": "sha512-/ksaaz2SRLN11JQhLdEUhDzOn909WEk99q9t9w+N12GjQCljzv7GyvAbD/p20aBUjHkvpGOoQ+FCOkG+mjDF4A==", + "engines": { + "node": "^14.18.0 || >= 16.0.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0", + "vue": "^2.7.0-0" + } + }, + "packages/playwright-ct-vue2/node_modules/@vue/compiler-sfc": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", + "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", + "dependencies": { + "@babel/parser": "^7.23.5", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "packages/playwright-ct-vue2/node_modules/vue": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", + "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", + "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", + "dependencies": { + "@vue/compiler-sfc": "2.7.16", + "csstype": "^3.1.0" + } + }, + "packages/playwright-firefox": { + "version": "1.42.0", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.42.0" + }, + "bin": { + "playwright": "cli.js" }, "engines": { "node": ">=16" @@ -8078,10 +8383,10 @@ }, "packages/playwright-test": { "name": "@playwright/test", - "version": "1.41.1", + "version": "1.42.0", "license": "Apache-2.0", "dependencies": { - "playwright": "1.41.1" + "playwright": "1.42.0" }, "bin": { "playwright": "cli.js" @@ -8091,11 +8396,11 @@ } }, "packages/playwright-webkit": { - "version": "1.41.1", + "version": "1.42.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.42.0" }, "bin": { "playwright": "cli.js" @@ -8104,8 +8409,21 @@ "node": ">=16" } }, - "packages/recorder": { - "version": "0.0.0" + "packages/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "packages/recorder": { + "version": "0.0.0" }, "packages/trace-viewer": { "version": "0.0.0" @@ -8122,5214 +8440,5 @@ "version": "0.0.1", "dev": true } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@actions/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", - "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", - "dev": true, - "requires": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "@actions/http-client": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", - "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", - "dev": true, - "requires": { - "tunnel": "^0.0.6" - } - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/cli": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.23.0.tgz", - "integrity": "sha512-17E1oSkGk2IwNILM4jtfAvgjt+ohmpfBky8aLerUfYZhiPNg7ca+CRCxZn8QDxwNhV/upsc2VHBCqGFIR+iBfA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.17", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^4.0.1", - "convert-source-map": "^2.0.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - } - } - }, - "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - } - }, - "@babel/compat-data": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz", - "integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==" - }, - "@babel/core": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.8.tgz", - "integrity": "sha512-75+KxFB4CZqYRXjx4NlR4J7yGvKumBuZTmV4NV6v09dVXXkuYVYLT68N6HCzLvfJ+fWCxQsntNzKwwIXL4bHnw==", - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2" - } - }, - "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz", - "integrity": "sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA==", - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-validator-option": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "requires": { - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "dependencies": { - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "requires": { - "@babel/types": "^7.22.15" - } - } - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==" - }, - "@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" - }, - "@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==" - }, - "@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", - "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" - } - }, - "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", - "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", - "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", - "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", - "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz", - "integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.15" - }, - "dependencies": { - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.15" - } - } - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", - "dev": true, - "requires": { - "@babel/plugin-transform-react-jsx": "^7.22.5" - } - }, - "@babel/plugin-transform-react-jsx-self": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz", - "integrity": "sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==", - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-transform-react-jsx-source": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz", - "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", - "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", - "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" - } - }, - "@babel/preset-react": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.15.tgz", - "integrity": "sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-react-display-name": "^7.22.5", - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.22.5" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "@electron/get": { - "version": "1.14.1", - "dev": true, - "requires": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "global-agent": "^3.0.0", - "global-tunnel-ng": "^2.7.1", - "got": "^9.6.0", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - } - }, - "@esbuild/android-arm": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.9.tgz", - "integrity": "sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==", - "optional": true, - "peer": true - }, - "@esbuild/android-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz", - "integrity": "sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==", - "optional": true, - "peer": true - }, - "@esbuild/android-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.9.tgz", - "integrity": "sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==", - "optional": true, - "peer": true - }, - "@esbuild/darwin-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz", - "integrity": "sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==", - "optional": true, - "peer": true - }, - "@esbuild/darwin-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz", - "integrity": "sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==", - "optional": true, - "peer": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz", - "integrity": "sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==", - "optional": true, - "peer": true - }, - "@esbuild/freebsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz", - "integrity": "sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==", - "optional": true, - "peer": true - }, - "@esbuild/linux-arm": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz", - "integrity": "sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==", - "optional": true, - "peer": true - }, - "@esbuild/linux-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz", - "integrity": "sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==", - "optional": true, - "peer": true - }, - "@esbuild/linux-ia32": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz", - "integrity": "sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==", - "optional": true, - "peer": true - }, - "@esbuild/linux-loong64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.11.tgz", - "integrity": "sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw==", - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz", - "integrity": "sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==", - "optional": true, - "peer": true - }, - "@esbuild/linux-ppc64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz", - "integrity": "sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==", - "optional": true, - "peer": true - }, - "@esbuild/linux-riscv64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz", - "integrity": "sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==", - "optional": true, - "peer": true - }, - "@esbuild/linux-s390x": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz", - "integrity": "sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==", - "optional": true, - "peer": true - }, - "@esbuild/linux-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz", - "integrity": "sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==", - "optional": true, - "peer": true - }, - "@esbuild/netbsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz", - "integrity": "sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==", - "optional": true, - "peer": true - }, - "@esbuild/openbsd-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz", - "integrity": "sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==", - "optional": true, - "peer": true - }, - "@esbuild/sunos-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz", - "integrity": "sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==", - "optional": true, - "peer": true - }, - "@esbuild/win32-arm64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz", - "integrity": "sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==", - "optional": true, - "peer": true - }, - "@esbuild/win32-ia32": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz", - "integrity": "sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==", - "optional": true, - "peer": true - }, - "@esbuild/win32-x64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz", - "integrity": "sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==", - "optional": true, - "peer": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - } - } - }, - "@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" - }, - "@jridgewell/set-array": { - "version": "1.1.1" - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "dev": true, - "optional": true - }, - "@nicolo-ribaudo/semver-v6": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", - "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==" - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@playwright/browser-chromium": { - "version": "file:packages/playwright-browser-chromium", - "requires": { - "playwright-core": "1.41.1" - } - }, - "@playwright/browser-firefox": { - "version": "file:packages/playwright-browser-firefox", - "requires": { - "playwright-core": "1.41.1" - } - }, - "@playwright/browser-webkit": { - "version": "file:packages/playwright-browser-webkit", - "requires": { - "playwright-core": "1.41.1" - } - }, - "@playwright/experimental-ct-core": { - "version": "file:packages/playwright-ct-core", - "requires": { - "playwright": "1.41.1", - "playwright-core": "1.41.1", - "vite": "^4.4.12" - } - }, - "@playwright/experimental-ct-react": { - "version": "file:packages/playwright-ct-react", - "requires": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-react": "^4.0.0" - }, - "dependencies": { - "@vitejs/plugin-react": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.0.tgz", - "integrity": "sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==", - "requires": { - "@babel/core": "^7.21.4", - "@babel/plugin-transform-react-jsx-self": "^7.21.0", - "@babel/plugin-transform-react-jsx-source": "^7.19.6", - "react-refresh": "^0.14.0" - } - } - } - }, - "@playwright/experimental-ct-react17": { - "version": "file:packages/playwright-ct-react17", - "requires": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-react": "^4.0.0" - }, - "dependencies": { - "@vitejs/plugin-react": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.0.tgz", - "integrity": "sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==", - "requires": { - "@babel/core": "^7.21.4", - "@babel/plugin-transform-react-jsx-self": "^7.21.0", - "@babel/plugin-transform-react-jsx-source": "^7.19.6", - "react-refresh": "^0.14.0" - } - } - } - }, - "@playwright/experimental-ct-solid": { - "version": "file:packages/playwright-ct-solid", - "requires": { - "@playwright/experimental-ct-core": "1.41.1", - "solid-js": "^1.7.0", - "vite-plugin-solid": "^2.7.0" - } - }, - "@playwright/experimental-ct-svelte": { - "version": "file:packages/playwright-ct-svelte", - "requires": { - "@playwright/experimental-ct-core": "1.41.1", - "@sveltejs/vite-plugin-svelte": "^3.0.1", - "svelte": "^4.2.8" - }, - "dependencies": { - "@esbuild/linux-loong64": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz", - "integrity": "sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==", - "optional": true, - "peer": true - }, - "@sveltejs/vite-plugin-svelte": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.1.tgz", - "integrity": "sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==", - "requires": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0-next.0 || ^2.0.0", - "debug": "^4.3.4", - "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "svelte-hmr": "^0.15.3", - "vitefu": "^0.2.5" - } - }, - "@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.0.0.tgz", - "integrity": "sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==", - "requires": { - "debug": "^4.3.4" - } - }, - "@types/node": { - "version": "20.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", - "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", - "optional": true, - "peer": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "esbuild": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.9.tgz", - "integrity": "sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==", - "peer": true, - "requires": { - "@esbuild/android-arm": "0.19.9", - "@esbuild/android-arm64": "0.19.9", - "@esbuild/android-x64": "0.19.9", - "@esbuild/darwin-arm64": "0.19.9", - "@esbuild/darwin-x64": "0.19.9", - "@esbuild/freebsd-arm64": "0.19.9", - "@esbuild/freebsd-x64": "0.19.9", - "@esbuild/linux-arm": "0.19.9", - "@esbuild/linux-arm64": "0.19.9", - "@esbuild/linux-ia32": "0.19.9", - "@esbuild/linux-loong64": "0.19.9", - "@esbuild/linux-mips64el": "0.19.9", - "@esbuild/linux-ppc64": "0.19.9", - "@esbuild/linux-riscv64": "0.19.9", - "@esbuild/linux-s390x": "0.19.9", - "@esbuild/linux-x64": "0.19.9", - "@esbuild/netbsd-x64": "0.19.9", - "@esbuild/openbsd-x64": "0.19.9", - "@esbuild/sunos-x64": "0.19.9", - "@esbuild/win32-arm64": "0.19.9", - "@esbuild/win32-ia32": "0.19.9", - "@esbuild/win32-x64": "0.19.9" - } - }, - "estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "requires": { - "@types/estree": "^1.0.0" - } - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "optional": true, - "peer": true - }, - "magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } - }, - "rollup": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.0.tgz", - "integrity": "sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A==", - "peer": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.9.0", - "@rollup/rollup-android-arm64": "4.9.0", - "@rollup/rollup-darwin-arm64": "4.9.0", - "@rollup/rollup-darwin-x64": "4.9.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.0", - "@rollup/rollup-linux-arm64-gnu": "4.9.0", - "@rollup/rollup-linux-arm64-musl": "4.9.0", - "@rollup/rollup-linux-riscv64-gnu": "4.9.0", - "@rollup/rollup-linux-x64-gnu": "4.9.0", - "@rollup/rollup-linux-x64-musl": "4.9.0", - "@rollup/rollup-win32-arm64-msvc": "4.9.0", - "@rollup/rollup-win32-ia32-msvc": "4.9.0", - "@rollup/rollup-win32-x64-msvc": "4.9.0", - "fsevents": "~2.3.2" - } - }, - "svelte": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.8.tgz", - "integrity": "sha512-hU6dh1MPl8gh6klQZwK/n73GiAHiR95IkFsesLPbMeEZi36ydaXL/ZAb4g9sayT0MXzpxyZjR28yderJHxcmYA==", - "requires": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^3.2.1", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" - } - }, - "vite": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.8.tgz", - "integrity": "sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==", - "peer": true, - "requires": { - "esbuild": "^0.19.3", - "fsevents": "~2.3.3", - "postcss": "^8.4.32", - "rollup": "^4.2.0" - } - } - } - }, - "@playwright/experimental-ct-vue": { - "version": "file:packages/playwright-ct-vue", - "requires": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-vue": "^4.2.1" - }, - "dependencies": { - "@vitejs/plugin-vue": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.1.tgz", - "integrity": "sha512-ZTZjzo7bmxTRTkb8GSTwkPOYDIP7pwuyV+RV53c9PYUouwcbkIZIvWvNWlX2b1dYZqtOv7D6iUAnJLVNGcLrSw==", - "requires": {} - }, - "@vue/server-renderer": { - "version": "3.2.36", - "peer": true, - "requires": { - "@vue/compiler-ssr": "3.2.36", - "@vue/shared": "3.2.36" - } - }, - "vue": { - "version": "3.2.36", - "peer": true, - "requires": { - "@vue/compiler-dom": "3.2.36", - "@vue/compiler-sfc": "3.2.36", - "@vue/runtime-dom": "3.2.36", - "@vue/server-renderer": "3.2.36", - "@vue/shared": "3.2.36" - } - } - } - }, - "@playwright/experimental-ct-vue2": { - "version": "file:packages/playwright-ct-vue2", - "requires": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-vue2": "^2.2.0", - "vue": "^2.7.14" - } - }, - "@playwright/test": { - "version": "file:packages/playwright-test", - "requires": { - "playwright": "1.41.1" - } - }, - "@rollup/rollup-android-arm-eabi": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.0.tgz", - "integrity": "sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA==", - "optional": true, - "peer": true - }, - "@rollup/rollup-android-arm64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.0.tgz", - "integrity": "sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A==", - "optional": true, - "peer": true - }, - "@rollup/rollup-darwin-arm64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.0.tgz", - "integrity": "sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ==", - "optional": true, - "peer": true - }, - "@rollup/rollup-darwin-x64": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.0.tgz", - "integrity": "sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw==", - "optional": true, - "peer": true - }, - "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.0.tgz", - "integrity": "sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg==", - "optional": true, - "peer": true - }, - "@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.0.tgz", - "integrity": "sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw==", - "optional": true, - "peer": true - }, - "@rollup/rollup-linux-arm64-musl": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.0.tgz", - "integrity": "sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w==", - "optional": true, - "peer": true - }, - "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.0.tgz", - "integrity": "sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w==", - "optional": true, - "peer": true - }, - "@rollup/rollup-linux-x64-gnu": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.0.tgz", - "integrity": "sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg==", - "optional": true, - "peer": true - }, - "@rollup/rollup-linux-x64-musl": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.0.tgz", - "integrity": "sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg==", - "optional": true, - "peer": true - }, - "@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.0.tgz", - "integrity": "sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q==", - "optional": true, - "peer": true - }, - "@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.0.tgz", - "integrity": "sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q==", - "optional": true, - "peer": true - }, - "@rollup/rollup-win32-x64-msvc": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.0.tgz", - "integrity": "sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw==", - "optional": true, - "peer": true - }, - "@sindresorhus/is": { - "version": "0.14.0", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/babel__core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", - "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/codemirror": { - "version": "5.60.7", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.7.tgz", - "integrity": "sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==", - "dev": true, - "requires": { - "@types/tern": "*" - } - }, - "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" - }, - "@types/formidable": { - "version": "2.0.4", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "@types/node": { - "version": "16.18.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.34.tgz", - "integrity": "sha512-VmVm7gXwhkUimRfBwVI1CHhwp86jDWR04B5FGebMMyxV90SlCmFujwUHrxTD4oO+SOYU86SoxvhgeRQJY7iXFg==", - "devOptional": true - }, - "@types/prop-types": { - "version": "15.7.3", - "dev": true - }, - "@types/react": { - "version": "18.0.12", - "dev": true, - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "18.0.5", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/resize-observer-browser": { - "version": "0.1.7", - "dev": true - }, - "@types/scheduler": { - "version": "0.16.1", - "dev": true - }, - "@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "@types/tern": { - "version": "0.23.4", - "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.4.tgz", - "integrity": "sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==", - "dev": true, - "requires": { - "@types/estree": "*" - } - }, - "@types/ws": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", - "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/xml2js": { - "version": "0.4.9", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@typescript-eslint/eslint-plugin": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", - "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/type-utils": "6.13.2", - "@typescript-eslint/utils": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/parser": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz", - "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", - "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2" - } - }, - "@typescript-eslint/type-utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz", - "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/utils": "6.13.2", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/types": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", - "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", - "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz", - "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", - "semver": "^7.5.4" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", - "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.13.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "@vitejs/plugin-basic-ssl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.0.1.tgz", - "integrity": "sha512-pcub+YbFtFhaGRTo1832FQHQSHvMrlb43974e2eS8EKleR3p1cDdkJFPci1UhwkEf1J9Bz+wKBSzqpKp7nNj2A==", - "dev": true, - "requires": {} - }, - "@vitejs/plugin-react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz", - "integrity": "sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==", - "dev": true, - "requires": { - "@babel/core": "^7.20.12", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.19.6", - "magic-string": "^0.27.0", - "react-refresh": "^0.14.0" - } - }, - "@vitejs/plugin-vue2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue2/-/plugin-vue2-2.2.0.tgz", - "integrity": "sha512-1km7zEuZ/9QRPvzXSjikbTYGQPG86Mq1baktpC4sXqsXlb02HQKfi+fl8qVS703JM7cgm24Ga9j+RwKmvFn90A==", - "requires": {} - }, - "@vue/compiler-core": { - "version": "3.2.36", - "peer": true, - "requires": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.36", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" - } - }, - "@vue/compiler-dom": { - "version": "3.2.36", - "peer": true, - "requires": { - "@vue/compiler-core": "3.2.36", - "@vue/shared": "3.2.36" - } - }, - "@vue/compiler-sfc": { - "version": "3.2.36", - "peer": true, - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.36", - "@vue/compiler-dom": "3.2.36", - "@vue/compiler-ssr": "3.2.36", - "@vue/reactivity-transform": "3.2.36", - "@vue/shared": "3.2.36", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" - }, - "dependencies": { - "magic-string": { - "version": "0.25.9", - "peer": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - } - } - }, - "@vue/compiler-ssr": { - "version": "3.2.36", - "peer": true, - "requires": { - "@vue/compiler-dom": "3.2.36", - "@vue/shared": "3.2.36" - } - }, - "@vue/reactivity": { - "version": "3.2.36", - "peer": true, - "requires": { - "@vue/shared": "3.2.36" - } - }, - "@vue/reactivity-transform": { - "version": "3.2.36", - "peer": true, - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.36", - "@vue/shared": "3.2.36", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" - }, - "dependencies": { - "magic-string": { - "version": "0.25.9", - "peer": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - } - } - }, - "@vue/runtime-core": { - "version": "3.2.36", - "peer": true, - "requires": { - "@vue/reactivity": "3.2.36", - "@vue/shared": "3.2.36" - } - }, - "@vue/runtime-dom": { - "version": "3.2.36", - "peer": true, - "requires": { - "@vue/runtime-core": "3.2.36", - "@vue/shared": "3.2.36", - "csstype": "^2.6.8" - }, - "dependencies": { - "csstype": { - "version": "2.6.20", - "peer": true - } - } - }, - "@vue/shared": { - "version": "3.2.36", - "peer": true - }, - "@zip.js/zip.js": { - "version": "2.7.29", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.29.tgz", - "integrity": "sha512-KtOa3HY7Vi77ctZAVhx2nsKweDTCP4DVBghZkvg5qyIX6T/Z54QlU6f0q2hFhxu5j+LgUDeMMhiv2xj4ZF6snA==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "dev": true - }, - "acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansi-to-html": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz", - "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", - "requires": { - "entities": "^2.2.0" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "args": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", - "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", - "dev": true, - "requires": { - "camelcase": "5.0.0", - "chalk": "2.4.2", - "leven": "2.1.0", - "mri": "1.1.4" - } - }, - "aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "requires": { - "dequal": "^2.0.3" - } - }, - "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "array-find-index": { - "version": "1.0.2", - "dev": true - }, - "array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.tosorted": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", - "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" - } - }, - "arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - } - }, - "asap": { - "version": "2.0.6", - "dev": true - }, - "async": { - "version": "0.2.10", - "dev": true - }, - "asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.3" - } - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "requires": { - "dequal": "^2.0.3" - } - }, - "babel-plugin-jsx-dom-expressions": { - "version": "0.36.10", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.36.10.tgz", - "integrity": "sha512-QA2k/14WGw+RgcGGnEuLWwnu4em6CGhjeXtjvgOYyFHYS2a+CzPeaVQHDOlfuiBcjq/3hWMspHMIMnPEOIzdBg==", - "requires": { - "@babel/helper-module-imports": "7.18.6", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.20.7", - "html-entities": "2.3.3", - "validate-html-nesting": "^1.2.1" - } - }, - "babel-preset-solid": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.7.3.tgz", - "integrity": "sha512-HOdyrij99zo+CBrmtDxSexBAl54vCBCfBoyueLBvcfVniaEXNd4ftKqSN6XQcLvFfCY28UFO+DHaigXzWKOfzg==", - "requires": { - "babel-plugin-jsx-dom-expressions": "^0.36.9" - } - }, - "balanced-match": { - "version": "1.0.2", - "dev": true - }, - "basic-auth-parser": { - "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz", - "integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==", - "dev": true - }, - "boolean": { - "version": "3.2.0", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", - "requires": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "dev": true - }, - "buffer-from": { - "version": "1.1.2", - "dev": true - }, - "cacheable-request": { - "version": "6.1.0", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "lowercase-keys": { - "version": "2.0.0", - "dev": true - } - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", - "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001514", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001514.tgz", - "integrity": "sha512-ENcIpYBmwAAOm/V2cXgM7rZUrKKaqisZl4ZAI520FIkqGXUxJjmaIssbRW5HVVR5tyV6ygTLIm15aU8LUmQSaQ==" - }, - "chalk": { - "version": "2.4.2", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "anymatch": { - "version": "3.1.2", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - } - } - }, - "cli": { - "version": "0.4.5", - "dev": true, - "requires": { - "glob": ">= 3.1.4" - } - }, - "cliff": { - "version": "0.1.10", - "dev": true, - "requires": { - "colors": "~1.0.3", - "eyes": "~0.1.8", - "winston": "0.8.x" - }, - "dependencies": { - "colors": { - "version": "1.0.3", - "dev": true - } - } - }, - "cliui": { - "version": "7.0.4", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone-response": { - "version": "1.0.2", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - }, - "dependencies": { - "estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "requires": { - "@types/estree": "^1.0.0" - } - } - } - }, - "codemirror-shadow-1": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/codemirror-shadow-1/-/codemirror-shadow-1-0.0.1.tgz", - "integrity": "sha512-kD3OZpCCHr3LHRKfbGx5IogHTWq4Uo9jH2bXPVa7/n6ppkgI66rx4tniQY1BpqWp/JNhQmQsXhQoaZ1TH6t0xQ==" - }, - "color-convert": { - "version": "1.9.3", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3" - }, - "colors": { - "version": "1.4.0", - "dev": true - }, - "commander": { - "version": "4.1.1", - "dev": true - }, - "commonmark": { - "version": "0.30.0", - "dev": true, - "requires": { - "entities": "~2.0", - "mdurl": "~1.0.1", - "minimist": ">=1.2.2", - "string.prototype.repeat": "^0.2.0" - }, - "dependencies": { - "entities": { - "version": "2.0.3", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "concurrently": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", - "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "date-fns": "^2.16.1", - "lodash": "^4.17.21", - "rxjs": "^6.6.3", - "spawn-command": "^0.0.2-1", - "supports-color": "^8.1.0", - "tree-kill": "^1.2.2", - "yargs": "^16.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "color-convert": { - "version": "2.0.1", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "config-chain": { - "version": "1.1.12", - "dev": true, - "optional": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "convert-source-map": { - "version": "1.7.0", - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "core-util-is": { - "version": "1.0.3", - "dev": true - }, - "cross-env": { - "version": "7.0.3", - "dev": true, - "requires": { - "cross-spawn": "^7.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "requires": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - } - }, - "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - }, - "cycle": { - "version": "1.0.3", - "dev": true - }, - "date-fns": { - "version": "2.23.0", - "dev": true - }, - "debug": { - "version": "4.3.4", - "requires": { - "ms": "2.1.2" - } - }, - "debuglog": { - "version": "1.0.1", - "dev": true - }, - "decompress-response": { - "version": "3.3.0", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-is": { - "version": "0.1.3", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" - }, - "defer-to-connect": { - "version": "1.1.3", - "dev": true - }, - "define-data-property": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", - "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - } - }, - "define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" - }, - "detect-node": { - "version": "2.1.0", - "dev": true, - "optional": true - }, - "dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dotenv": { - "version": "16.0.0", - "dev": true - }, - "duplexer3": { - "version": "0.1.4", - "dev": true - }, - "electron": { - "version": "19.0.11", - "resolved": "https://registry.npmjs.org/electron/-/electron-19.0.11.tgz", - "integrity": "sha512-GPM6C1Ze17/gR4koTE171MxrI5unYfFRgXQdkMdpWM2Cd55LMUrVa0QHCsfKpsaloufv9T65lsOn0uZuzCw5UA==", - "dev": true, - "requires": { - "@electron/get": "^1.14.1", - "@types/node": "^16.11.26", - "extract-zip": "^1.0.3" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "extract-zip": { - "version": "1.7.0", - "dev": true, - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - } - }, - "ms": { - "version": "2.0.0", - "dev": true - } - } - }, - "electron-to-chromium": { - "version": "1.4.454", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.454.tgz", - "integrity": "sha512-pmf1rbAStw8UEQ0sr2cdJtWl48ZMuPD9Sto8HVQOq9vx9j2WgDEN6lYoaqFvqEHYOmGA9oRGn7LqWI9ta0YugQ==" - }, - "emoji-regex": { - "version": "8.0.0", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "dev": true, - "optional": true - }, - "end-of-stream": { - "version": "1.4.4", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "entities": { - "version": "2.2.0" - }, - "env-paths": { - "version": "2.2.1", - "dev": true - }, - "es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" - } - }, - "es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", - "dev": true, - "requires": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" - } - }, - "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "dev": true, - "optional": true - }, - "esbuild": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.11.tgz", - "integrity": "sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==", - "requires": { - "@esbuild/android-arm": "0.18.11", - "@esbuild/android-arm64": "0.18.11", - "@esbuild/android-x64": "0.18.11", - "@esbuild/darwin-arm64": "0.18.11", - "@esbuild/darwin-x64": "0.18.11", - "@esbuild/freebsd-arm64": "0.18.11", - "@esbuild/freebsd-x64": "0.18.11", - "@esbuild/linux-arm": "0.18.11", - "@esbuild/linux-arm64": "0.18.11", - "@esbuild/linux-ia32": "0.18.11", - "@esbuild/linux-loong64": "0.18.11", - "@esbuild/linux-mips64el": "0.18.11", - "@esbuild/linux-ppc64": "0.18.11", - "@esbuild/linux-riscv64": "0.18.11", - "@esbuild/linux-s390x": "0.18.11", - "@esbuild/linux-x64": "0.18.11", - "@esbuild/netbsd-x64": "0.18.11", - "@esbuild/openbsd-x64": "0.18.11", - "@esbuild/sunos-x64": "0.18.11", - "@esbuild/win32-arm64": "0.18.11", - "@esbuild/win32-ia32": "0.18.11", - "@esbuild/win32-x64": "0.18.11" - }, - "dependencies": { - "@esbuild/android-arm": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.11.tgz", - "integrity": "sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q==", - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.11.tgz", - "integrity": "sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw==", - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.11.tgz", - "integrity": "sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw==", - "optional": true - }, - "@esbuild/darwin-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.11.tgz", - "integrity": "sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w==", - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz", - "integrity": "sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw==", - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.11.tgz", - "integrity": "sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A==", - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.11.tgz", - "integrity": "sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q==", - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.11.tgz", - "integrity": "sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg==", - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.11.tgz", - "integrity": "sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog==", - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.11.tgz", - "integrity": "sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw==", - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.11.tgz", - "integrity": "sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg==", - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.11.tgz", - "integrity": "sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ==", - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.11.tgz", - "integrity": "sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w==", - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.11.tgz", - "integrity": "sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg==", - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.11.tgz", - "integrity": "sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA==", - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.11.tgz", - "integrity": "sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q==", - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.11.tgz", - "integrity": "sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ==", - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.11.tgz", - "integrity": "sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng==", - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.11.tgz", - "integrity": "sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg==", - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.11.tgz", - "integrity": "sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg==", - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.18.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.11.tgz", - "integrity": "sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA==", - "optional": true - } - } - }, - "escalade": { - "version": "3.1.1" - }, - "escape-string-regexp": { - "version": "1.0.5" - }, - "eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "eslint-plugin-internal-playwright": { - "version": "file:utils/eslint-plugin-internal-playwright" - }, - "eslint-plugin-notice": { - "version": "0.9.10", - "dev": true, - "requires": { - "find-root": "^1.1.0", - "lodash": "^4.17.15", - "metric-lcs": "^0.1.2" - } - }, - "eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.8" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.3.0", - "dev": true, - "requires": {} - }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "estree-walker": { - "version": "2.0.2", - "peer": true - }, - "esutils": { - "version": "2.0.3", - "dev": true - }, - "eyes": { - "version": "0.1.8", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "dev": true - }, - "fastq": { - "version": "1.11.1", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fd-slicer": { - "version": "1.1.0", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-root": { - "version": "1.1.0", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "3.1.1", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "formidable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", - "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==", - "dev": true, - "requires": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - } - }, - "fs-extra": { - "version": "8.1.0", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2" - }, - "get-caller-file": { - "version": "2.0.5", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "get-stream": { - "version": "5.2.0", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.2.0", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "global-agent": { - "version": "3.0.0", - "dev": true, - "optional": true, - "requires": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "global-tunnel-ng": { - "version": "2.7.1", - "dev": true, - "optional": true, - "requires": { - "encodeurl": "^1.0.2", - "lodash": "^4.17.10", - "npm-conf": "^1.1.3", - "tunnel": "^0.0.6" - } - }, - "globals": { - "version": "11.12.0" - }, - "globalthis": { - "version": "1.0.3", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "got": { - "version": "9.6.0", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "get-stream": { - "version": "4.1.0", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } - } - }, - "graceful-fs": { - "version": "4.2.9", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "has": { - "version": "1.0.3", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "3.0.0" - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hexoid": { - "version": "1.0.0", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "dev": true - }, - "html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" - }, - "html-reporter": { - "version": "file:packages/html-reporter", - "requires": { - "ansi-to-html": "^0.7.2" - } - }, - "http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "dev": true - }, - "ini": { - "version": "1.3.8", - "dev": true, - "optional": true - }, - "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.9.0", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "dev": true - }, - "is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true - }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.3", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "requires": { - "@types/estree": "*" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "requires": { - "which-typed-array": "^1.1.11" - } - }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "is-what": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.8.tgz", - "integrity": "sha512-yq8gMao5upkPoGEU9LsB2P+K3Kt8Q3fQFCGyNCWOAnJAMzEXVV9drYb0TXr42TTliLLhKIBvulgAXgtLLnwzGA==" - }, - "isarray": { - "version": "1.0.0", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "dev": true - }, - "iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "js-tokens": { - "version": "4.0.0" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2" - }, - "json-buffer": { - "version": "3.0.0", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "dev": true, - "optional": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - }, - "jsonfile": { - "version": "4.0.0", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - } - }, - "keyv": { - "version": "3.1.0", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "kleur": { - "version": "4.1.5" - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "license-checker": { - "version": "25.0.1", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "read-installed": "~4.0.3", - "semver": "^5.5.0", - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0", - "spdx-satisfies": "^4.0.0", - "treeify": "^1.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lowercase-keys": { - "version": "1.0.1", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.13" - } - }, - "make-dir": { - "version": "2.1.0", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "matcher": { - "version": "3.0.0", - "dev": true, - "optional": true, - "requires": { - "escape-string-regexp": "^4.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "optional": true - } - } - }, - "mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, - "mdurl": { - "version": "1.0.1", - "dev": true - }, - "merge-anything": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.4.tgz", - "integrity": "sha512-7PWKwGOs5WWcpw+/OvbiFiAvEP6bv/QHiicigpqMGKIqPPAtGhBLR8LFJW+Zu6m9TXiR/a8+AiPlGG0ko1ruoQ==", - "requires": { - "is-what": "^4.1.8" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "metric-lcs": { - "version": "0.1.2", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "3.0.0", - "dev": true - }, - "mimic-response": { - "version": "1.0.1", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mri": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", - "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", - "dev": true - }, - "ms": { - "version": "2.1.2" - }, - "nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" - }, - "natural-compare": { - "version": "1.4.0", - "dev": true - }, - "ncp": { - "version": "2.0.0", - "dev": true - }, - "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" - }, - "node-stream-zip": { - "version": "1.15.0", - "dev": true - }, - "nopt": { - "version": "4.0.3", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "dev": true - }, - "normalize-url": { - "version": "4.5.1", - "dev": true - }, - "npm-conf": { - "version": "1.1.3", - "dev": true, - "optional": true, - "requires": { - "config-chain": "^1.1.11", - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "dev": true, - "optional": true - } - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", - "dev": true, - "requires": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "once": { - "version": "1.4.0", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "os-homedir": { - "version": "1.0.2", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-cancelable": { - "version": "1.1.0", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pend": { - "version": "1.2.0", - "dev": true - }, - "periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "requires": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - }, - "dependencies": { - "estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "requires": { - "@types/estree": "^1.0.0" - } - } - } - }, - "picocolors": { - "version": "1.0.0" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "dev": true - }, - "pkginfo": { - "version": "0.4.0-next", - "dev": true - }, - "playwright": { - "version": "file:packages/playwright", - "requires": { - "fsevents": "2.3.2", - "playwright-core": "1.41.1" - } - }, - "playwright-chromium": { - "version": "file:packages/playwright-chromium", - "requires": { - "playwright-core": "1.41.1" - } - }, - "playwright-core": { - "version": "file:packages/playwright-core" - }, - "playwright-firefox": { - "version": "file:packages/playwright-firefox", - "requires": { - "playwright-core": "1.41.1" - } - }, - "playwright-webkit": { - "version": "file:packages/playwright-webkit", - "requires": { - "playwright-core": "1.41.1" - } - }, - "postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", - "requires": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "prelude-ls": { - "version": "1.2.1", - "dev": true - }, - "prepend-http": { - "version": "2.0.0", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "dev": true - }, - "progress": { - "version": "2.0.3", - "dev": true - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "proto-list": { - "version": "1.2.4", - "dev": true, - "optional": true - }, - "proxy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz", - "integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==", - "dev": true, - "requires": { - "args": "^5.0.3", - "basic-auth-parser": "0.0.2-1", - "debug": "^4.3.4" - } - }, - "pump": { - "version": "3.0.0", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "queue-microtask": { - "version": "1.2.3", - "dev": true - }, - "react": { - "version": "18.1.0", - "dev": true, - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-dom": { - "version": "18.1.0", - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.22.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==" - }, - "read-installed": { - "version": "4.0.3", - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "graceful-fs": "^4.1.2", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "slide": "~1.1.3", - "util-extend": "^1.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "read-package-json": { - "version": "2.1.2", - "dev": true, - "requires": { - "glob": "^7.1.1", - "json-parse-even-better-errors": "^2.3.0", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "readdirp": { - "version": "3.6.0", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "recorder": { - "version": "file:packages/recorder" - }, - "reflect.getprototypeof": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", - "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - } - }, - "regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - } - }, - "require-directory": { - "version": "2.1.1", - "dev": true - }, - "resolve": { - "version": "1.22.1", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "reusify": { - "version": "1.0.4", - "dev": true - }, - "roarr": { - "version": "2.15.4", - "dev": true, - "optional": true, - "requires": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - } - }, - "rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", - "requires": { - "fsevents": "~2.3.2" - } - }, - "run-parallel": { - "version": "1.2.0", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "6.6.7", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } - } - }, - "safe-buffer": { - "version": "5.1.2" - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "sax": { - "version": "1.2.4", - "dev": true - }, - "scheduler": { - "version": "0.22.0", - "dev": true, - "requires": { - "loose-envify": "^1.1.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - }, - "semver-compare": { - "version": "1.0.0", - "dev": true, - "optional": true - }, - "serialize-error": { - "version": "7.0.1", - "dev": true, - "optional": true, - "requires": { - "type-fest": "^0.13.1" - }, - "dependencies": { - "type-fest": { - "version": "0.13.1", - "dev": true, - "optional": true - } - } - }, - "seroval": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-0.5.1.tgz", - "integrity": "sha512-ZfhQVB59hmIauJG5Ydynupy8KHyr5imGNtdDhbZG68Ufh1Ynkv9KOYOAABf71oVbQxJ8VkWnMHAjEHE7fWkH5g==" - }, - "set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, - "requires": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "slash": { - "version": "2.0.0", - "dev": true - }, - "slide": { - "version": "1.1.6", - "dev": true - }, - "socksv5": { - "version": "0.0.6", - "dev": true, - "requires": { - "ipv6": "*" - }, - "dependencies": { - "ipv6": { - "version": "3.1.1", - "bundled": true, - "dev": true, - "requires": { - "cli": "0.4.x", - "cliff": "0.1.x", - "sprintf": "0.1.x" - }, - "dependencies": { - "sprintf": { - "version": "0.1.3", - "bundled": true, - "dev": true - } - } - } - } - }, - "solid-js": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.7.3.tgz", - "integrity": "sha512-4hwaF/zV/xbNeBBIYDyu3dcReOZBECbO//mrra6GqOrKy4Soyo+fnKjpZSa0nODm6j1aL0iQRh/7ofYowH+jzw==", - "requires": { - "csstype": "^3.1.0", - "seroval": "^0.5.0" - } - }, - "solid-refresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.5.2.tgz", - "integrity": "sha512-I69HmFj0LsGRJ3n8CEMVjyQFgVtuM2bSjznu2hCnsY+i5oOxh8ioWj00nnHBv0UYD3WpE/Sq4Q3TNw2IKmKN7A==", - "requires": { - "@babel/generator": "^7.21.1", - "@babel/helper-module-imports": "^7.18.6", - "@babel/types": "^7.21.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-js": { - "version": "1.0.2" - }, - "sourcemap-codec": { - "version": "1.4.8", - "peer": true - }, - "spawn-command": { - "version": "0.0.2-1", - "dev": true - }, - "spdx-compare": { - "version": "1.0.0", - "dev": true, - "requires": { - "array-find-index": "^1.0.2", - "spdx-expression-parse": "^3.0.0", - "spdx-ranges": "^2.0.0" - } - }, - "spdx-correct": { - "version": "3.1.1", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.10", - "dev": true - }, - "spdx-ranges": { - "version": "2.1.1", - "dev": true - }, - "spdx-satisfies": { - "version": "4.0.1", - "dev": true, - "requires": { - "spdx-compare": "^1.0.0", - "spdx-expression-parse": "^3.0.0", - "spdx-ranges": "^2.0.0" - } - }, - "sprintf-js": { - "version": "1.1.2", - "dev": true, - "optional": true - }, - "ssim.js": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ssim.js/-/ssim.js-3.5.0.tgz", - "integrity": "sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g==", - "dev": true - }, - "stack-trace": { - "version": "0.0.10", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "string-width": { - "version": "4.2.2", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "string.prototype.matchall": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "set-function-name": "^2.0.0", - "side-channel": "^1.0.4" - } - }, - "string.prototype.repeat": { - "version": "0.2.0", - "dev": true - }, - "string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "sumchecker": { - "version": "3.0.1", - "dev": true, - "requires": { - "debug": "^4.1.0" - } - }, - "supports-color": { - "version": "5.5.0", - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true - }, - "svelte": { - "version": "3.55.1", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz", - "integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==", - "peer": true - }, - "svelte-hmr": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", - "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==", - "requires": {} - }, - "text-table": { - "version": "0.2.0", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0" - }, - "to-readable-stream": { - "version": "1.0.0", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "trace-viewer": { - "version": "file:packages/trace-viewer" - }, - "tree-kill": { - "version": "1.2.2", - "dev": true - }, - "treeify": { - "version": "1.1.0", - "dev": true - }, - "ts-api-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", - "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", - "dev": true, - "requires": {} - }, - "tslib": { - "version": "1.14.1", - "dev": true - }, - "tunnel": { - "version": "0.0.6", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typedarray": { - "version": "0.0.6", - "dev": true - }, - "typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "optional": true, - "peer": true - }, - "universalify": { - "version": "0.1.2", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "dev": true - }, - "util-extend": { - "version": "1.0.3", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "validate-html-nesting": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/validate-html-nesting/-/validate-html-nesting-1.2.2.tgz", - "integrity": "sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vite": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.12.tgz", - "integrity": "sha512-KtPlUbWfxzGVul8Nut8Gw2Qe8sBzWY+8QVc5SL8iRFnpnrcoCaNlzO40c1R6hPmcdTwIPEDkq0Y9+27a5tVbdQ==", - "requires": { - "esbuild": "^0.18.10", - "fsevents": "~2.3.2", - "postcss": "^8.4.27", - "rollup": "^3.27.1" - } - }, - "vite-plugin-solid": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.7.0.tgz", - "integrity": "sha512-avp/Jl5zOp/Itfo67xtDB2O61U7idviaIp4mLsjhCa13PjKNasz+IID0jYTyqUp9SFx6/PmBr6v4KgDppqompg==", - "requires": { - "@babel/core": "^7.20.5", - "@babel/preset-typescript": "^7.18.6", - "@types/babel__core": "^7.1.20", - "babel-preset-solid": "^1.7.2", - "merge-anything": "^5.1.4", - "solid-refresh": "^0.5.0", - "vitefu": "^0.2.3" - } - }, - "vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", - "requires": {} - }, - "vue": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.14.tgz", - "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==", - "requires": { - "@vue/compiler-sfc": "2.7.14", - "csstype": "^3.1.0" - }, - "dependencies": { - "@vue/compiler-sfc": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz", - "integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==", - "requires": { - "@babel/parser": "^7.18.4", - "postcss": "^8.4.14", - "source-map": "^0.6.1" - } - } - } - }, - "web": { - "version": "file:packages/web", - "requires": { - "codemirror-shadow-1": "0.0.1", - "xterm": "^5.1.0", - "xterm-addon-fit": "^0.7.0" - } - }, - "which": { - "version": "2.0.2", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "requires": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } - } - }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - } - }, - "which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "winston": { - "version": "0.8.3", - "dev": true, - "requires": { - "async": "0.2.x", - "colors": "0.6.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "pkginfo": "0.3.x", - "stack-trace": "0.0.x" - }, - "dependencies": { - "colors": { - "version": "0.6.2", - "dev": true - } - } - }, - "wrap-ansi": { - "version": "7.0.0", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "dev": true - } - } - }, - "wrappy": { - "version": "1.0.2", - "dev": true - }, - "ws": { - "version": "8.5.0", - "dev": true, - "requires": {} - }, - "xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "dev": true - }, - "xterm": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.1.0.tgz", - "integrity": "sha512-LovENH4WDzpwynj+OTkLyZgJPeDom9Gra4DMlGAgz6pZhIDCQ+YuO7yfwanY+gVbn/mmZIStNOnVRU/ikQuAEQ==" - }, - "xterm-addon-fit": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz", - "integrity": "sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==", - "requires": {} - }, - "yallist": { - "version": "4.0.0", - "dev": true - }, - "yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "dependencies": { - "y18n": { - "version": "5.0.8", - "dev": true - } - } - }, - "yargs-parser": { - "version": "20.2.9", - "dev": true - }, - "yauzl": { - "version": "2.10.0", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/playwright/package.json b/playwright/package.json index c6cf13785d..c5080058ef 100644 --- a/playwright/package.json +++ b/playwright/package.json @@ -1,7 +1,7 @@ { "name": "playwright-internal", "private": true, - "version": "1.41.1", + "version": "1.42.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -29,14 +29,14 @@ "ttest": "node ./tests/playwright-test/stable-test-runner/node_modules/@playwright/test/cli test --config=tests/playwright-test/playwright.config.ts", "ct": "playwright test tests/components/test-all.spec.js --reporter=list", "test": "playwright test --config=tests/library/playwright.config.ts", - "eslint": "eslint --cache --report-unused-disable-directives --ext ts,tsx .", + "eslint": "eslint --cache --report-unused-disable-directives --ext ts,tsx,js,jsx,mjs .", "tsc": "tsc -p .", "build-installer": "babel -s --extensions \".ts\" --out-dir packages/playwright-core/lib/utils/ packages/playwright-core/src/utils", "doc": "node utils/doclint/cli.js", - "lint": "npm run eslint && npm run tsc && npm run doc && npm run check-deps && node utils/generate_channels.js && node utils/generate_types/ --check-clean && npm run lint-tests && npm run test-types && npm run lint-packages", + "lint": "npm run eslint && npm run tsc && npm run doc && npm run check-deps && node utils/generate_channels.js && node utils/generate_types/ && npm run lint-tests && npm run test-types && npm run lint-packages", "lint-packages": "node utils/workspace.js --ensure-consistent", "lint-tests": "node utils/lint_tests.js", - "flint": "concurrently \"npm run eslint\" \"npm run tsc\" \"npm run doc\" \"npm run check-deps\" \"node utils/generate_channels.js\" \"node utils/generate_types/ --check-clean\" \"npm run lint-tests\" \"npm run test-types\" \"npm run lint-packages\"", + "flint": "concurrently \"npm run eslint\" \"npm run tsc\" \"npm run doc\" \"npm run check-deps\" \"node utils/generate_channels.js\" \"node utils/generate_types/\" \"npm run lint-tests\" \"npm run test-types\" \"npm run lint-packages\"", "clean": "node utils/build/clean.js", "build": "node utils/build/build.js", "watch": "node utils/build/build.js --watch --lint", @@ -51,20 +51,20 @@ ], "devDependencies": { "@actions/core": "^1.10.0", - "@babel/cli": "^7.23.0", - "@babel/code-frame": "^7.22.13", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-typescript": "^7.22.15", - "@babel/preset-react": "^7.22.15", + "@babel/cli": "^7.23.4", + "@babel/code-frame": "^7.23.5", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-typescript": "^7.23.6", + "@babel/preset-react": "^7.23.3", "@types/babel__core": "^7.20.2", "@types/codemirror": "^5.60.7", "@types/formidable": "^2.0.4", - "@types/node": "^16.18.34", + "@types/node": "^18.15.3", "@types/react": "^18.0.12", "@types/react-dom": "^18.0.5", "@types/resize-observer-browser": "^0.1.7", @@ -73,8 +73,8 @@ "@typescript-eslint/eslint-plugin": "^6.13.2", "@typescript-eslint/parser": "^6.13.2", "@typescript-eslint/utils": "^6.13.2", - "@vitejs/plugin-basic-ssl": "^1.0.1", - "@vitejs/plugin-react": "^3.1.0", + "@vitejs/plugin-basic-ssl": "^1.1.0", + "@vitejs/plugin-react": "^4.2.1", "@zip.js/zip.js": "^2.7.29", "chokidar": "^3.5.3", "colors": "^1.4.0", @@ -93,15 +93,13 @@ "formidable": "^2.1.1", "license-checker": "^25.0.1", "mime": "^3.0.0", - "ncp": "^2.0.0", "node-stream-zip": "^1.15.0", - "proxy": "^2.1.1", "react": "^18.1.0", "react-dom": "^18.1.0", "socksv5": "0.0.6", "ssim.js": "^3.5.0", "typescript": "^5.3.2", - "vite": "^4.4.12", + "vite": "^5.0.12", "ws": "^8.5.0", "xml2js": "^0.5.0", "yaml": "^2.2.2" diff --git a/playwright/packages/.eslintrc-with-ts-config.js b/playwright/packages/.eslintrc-with-ts-config.js deleted file mode 100644 index dea9d4ef41..0000000000 --- a/playwright/packages/.eslintrc-with-ts-config.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - extends: ".eslintrc.js", - rules: { - "@typescript-eslint/no-base-to-string": "error", - }, - parserOptions: { - project: "./tsconfig.json" - }, -}; diff --git a/playwright/packages/html-reporter/playwright/index.html b/playwright/packages/html-reporter/playwright/index.html index 9bba58cfa3..b18a1e9c7d 100644 --- a/playwright/packages/html-reporter/playwright/index.html +++ b/playwright/packages/html-reporter/playwright/index.html @@ -24,6 +24,6 @@ </head> <body> <div id='root'></div> - <script type='module' src='/playwright/index.ts'></script> + <script type='module' src='/index.ts'></script> </body> </html> diff --git a/playwright/packages/html-reporter/src/filter.ts b/playwright/packages/html-reporter/src/filter.ts index 0a49c1dc61..6f7ef14525 100644 --- a/playwright/packages/html-reporter/src/filter.ts +++ b/playwright/packages/html-reporter/src/filter.ts @@ -14,7 +14,7 @@ limitations under the License. */ -import { escapeRegExp } from './labelUtils'; +import { testCaseLabels } from './labelUtils'; import type { TestCaseSummary } from './types'; export class Filter { @@ -114,6 +114,7 @@ export class Filter { file: test.location.file, line: String(test.location.line), column: String(test.location.column), + labels: testCaseLabels(test).map(label => label.toLowerCase()), }; (test as any).searchValues = searchValues; } @@ -140,7 +141,7 @@ export class Filter { } } if (this.labels.length) { - const matches = this.labels.every(l => searchValues.text?.match(new RegExp(`(\\s|^)${escapeRegExp(l)}(\\s|$)`, 'g'))); + const matches = this.labels.every(l => searchValues.labels.includes(l)); if (!matches) return false; } @@ -156,5 +157,6 @@ type SearchValues = { file: string; line: string; column: string; + labels: string[]; }; diff --git a/playwright/packages/html-reporter/src/labelUtils.tsx b/playwright/packages/html-reporter/src/labelUtils.tsx index 52d587db2e..57d9c43c9a 100644 --- a/playwright/packages/html-reporter/src/labelUtils.tsx +++ b/playwright/packages/html-reporter/src/labelUtils.tsx @@ -16,25 +16,18 @@ import type { TestCaseSummary } from './types'; -export function escapeRegExp(string: string) { - const reRegExpChar = /[\\^$.*+?()[\]{}|]/g; - const reHasRegExpChar = RegExp(reRegExpChar.source); - - return (string && reHasRegExpChar.test(string)) - ? string.replace(reRegExpChar, '\\$&') - : (string || ''); -} +const labelsSymbol = Symbol('labels'); +// Note: all labels start with "@" export function testCaseLabels(test: TestCaseSummary): string[] { - const tags = matchTags(test.path.join(' ') + ' ' + test.title).sort((a, b) => a.localeCompare(b)); - if (test.botName) - tags.unshift(test.botName); - return tags; -} - -// match all tags in test title -function matchTags(title: string): string[] { - return title.match(/@([\S]+)/g) || []; + if (!(test as any)[labelsSymbol]) { + const labels: string[] = []; + if (test.botName) + labels.push('@' + test.botName); + labels.push(...test.tags); + (test as any)[labelsSymbol] = labels; + } + return (test as any)[labelsSymbol]; } // hash string to integer in range [0, 6] for color index, to get same color for same tag diff --git a/playwright/packages/html-reporter/src/testCaseView.spec.tsx b/playwright/packages/html-reporter/src/testCaseView.spec.tsx index f67a3f2cc5..28fe8247f5 100644 --- a/playwright/packages/html-reporter/src/testCaseView.spec.tsx +++ b/playwright/packages/html-reporter/src/testCaseView.spec.tsx @@ -55,6 +55,7 @@ const testCase: TestCase = { { type: 'annotation', description: 'Annotation text' }, { type: 'annotation', description: 'Another annotation text' }, ], + tags: [], outcome: 'expected', duration: 10, ok: true, diff --git a/playwright/packages/html-reporter/src/testCaseView.tsx b/playwright/packages/html-reporter/src/testCaseView.tsx index 4fcd63baf7..4b79908edf 100644 --- a/playwright/packages/html-reporter/src/testCaseView.tsx +++ b/playwright/packages/html-reporter/src/testCaseView.tsx @@ -95,7 +95,7 @@ const LabelsLinkView: React.FC<React.PropsWithChildren<{ {labels.map(label => ( <a key={label} style={{ textDecoration: 'none', color: 'var(--color-fg-default)' }} href={`#?q=${label}`} > <span style={{ margin: '6px 0 0 6px', cursor: 'pointer' }} className={'label label-color-' + (hashStringToInt(label))}> - {label.startsWith('@') ? label.slice(1) : label} + {label.slice(1)} </span> </a> ))} diff --git a/playwright/packages/html-reporter/src/testFileView.tsx b/playwright/packages/html-reporter/src/testFileView.tsx index 4d6560afb2..619d6263d2 100644 --- a/playwright/packages/html-reporter/src/testFileView.tsx +++ b/playwright/packages/html-reporter/src/testFileView.tsx @@ -98,8 +98,7 @@ const LabelsClickView: React.FC<React.PropsWithChildren<{ // If metaKey or ctrlKey is pressed, add tag to search query without replacing existing tags. // If metaKey or ctrlKey is pressed and tag is already in search query, remove tag from search query. - // Always toggle non-@-tag labels. - if (e.metaKey || e.ctrlKey || !label.startsWith('@')) { + if (e.metaKey || e.ctrlKey) { if (!q.includes(label)) q = `${q} ${label}`.trim(); else @@ -118,7 +117,7 @@ const LabelsClickView: React.FC<React.PropsWithChildren<{ <> {labels.map(label => ( <span key={label} style={{ margin: '6px 0 0 6px', cursor: 'pointer' }} className={'label label-color-' + (hashStringToInt(label))} onClick={e => onClickHandle(e, label)}> - {label.startsWith('@') ? label.slice(1) : label} + {label.slice(1)} </span> ))} </> diff --git a/playwright/packages/html-reporter/src/types.ts b/playwright/packages/html-reporter/src/types.ts index 3a1f14f56e..d7ada9cbe6 100644 --- a/playwright/packages/html-reporter/src/types.ts +++ b/playwright/packages/html-reporter/src/types.ts @@ -69,6 +69,7 @@ export type TestCaseSummary = { botName?: string; location: Location; annotations: TestCaseAnnotation[]; + tags: string[]; outcome: 'skipped' | 'expected' | 'unexpected' | 'flaky'; duration: number; ok: boolean; diff --git a/playwright/packages/html-reporter/tsconfig.json b/playwright/packages/html-reporter/tsconfig.json index 93a8b7cbd0..4fe82eab4e 100644 --- a/playwright/packages/html-reporter/tsconfig.json +++ b/playwright/packages/html-reporter/tsconfig.json @@ -4,7 +4,7 @@ "useDefineForClassFields": true, "lib": ["DOM", "DOM.Iterable", "ESNext"], "allowJs": true, - "skipLibCheck": false, + "skipLibCheck": true, "esModuleInterop": false, "allowSyntheticDefaultImports": true, "strict": true, diff --git a/playwright/packages/playwright-browser-chromium/package.json b/playwright/packages/playwright-browser-chromium/package.json index 8efd7d2876..b296464b89 100644 --- a/playwright/packages/playwright-browser-chromium/package.json +++ b/playwright/packages/playwright-browser-chromium/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-chromium", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright package that automatically installs Chromium", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.42.0" } } diff --git a/playwright/packages/playwright-browser-firefox/package.json b/playwright/packages/playwright-browser-firefox/package.json index 981490d2fe..2eed72c14d 100644 --- a/playwright/packages/playwright-browser-firefox/package.json +++ b/playwright/packages/playwright-browser-firefox/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-firefox", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright package that automatically installs Firefox", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.42.0" } } diff --git a/playwright/packages/playwright-browser-webkit/package.json b/playwright/packages/playwright-browser-webkit/package.json index a5abfdc504..b0d6e14ea4 100644 --- a/playwright/packages/playwright-browser-webkit/package.json +++ b/playwright/packages/playwright-browser-webkit/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-webkit", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright package that automatically installs WebKit", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.42.0" } } diff --git a/playwright/packages/playwright-chromium/cli.js b/playwright/packages/playwright-chromium/cli.js index bd995de114..86adb86a84 100755 --- a/playwright/packages/playwright-chromium/cli.js +++ b/playwright/packages/playwright-chromium/cli.js @@ -14,4 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('playwright-core/lib/cli/cli'); + +const { program } = require('playwright-core/lib/program'); +program.parse(process.argv); diff --git a/playwright/packages/playwright-chromium/package.json b/playwright/packages/playwright-chromium/package.json index f3d6916375..2d1e6cf859 100644 --- a/playwright/packages/playwright-chromium/package.json +++ b/playwright/packages/playwright-chromium/package.json @@ -1,6 +1,6 @@ { "name": "playwright-chromium", - "version": "1.41.1", + "version": "1.42.0", "description": "A high-level API to automate Chromium", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.42.0" } } diff --git a/playwright/packages/playwright-core/.eslintrc.js b/playwright/packages/playwright-core/.eslintrc.js index ae8768db65..84888f1ae3 100644 --- a/playwright/packages/playwright-core/.eslintrc.js +++ b/playwright/packages/playwright-core/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - extends: "../.eslintrc-with-ts-config.js", + extends: "../../.eslintrc-with-ts-config.js", }; diff --git a/playwright/packages/playwright-core/ThirdPartyNotices.txt b/playwright/packages/playwright-core/ThirdPartyNotices.txt index f18b4af05a..6480e85f29 100644 --- a/playwright/packages/playwright-core/ThirdPartyNotices.txt +++ b/playwright/packages/playwright-core/ThirdPartyNotices.txt @@ -23,7 +23,7 @@ This project incorporates components from the projects listed below. The origina - get-stream@5.2.0 (https://github.com/sindresorhus/get-stream) - graceful-fs@4.2.10 (https://github.com/isaacs/node-graceful-fs) - https-proxy-agent@5.0.0 (https://github.com/TooTallNate/node-https-proxy-agent) -- ip@2.0.0 (https://github.com/indutny/node-ip) +- ip@2.0.1 (https://github.com/indutny/node-ip) - is-docker@2.2.1 (https://github.com/sindresorhus/is-docker) - is-wsl@2.2.0 (https://github.com/sindresorhus/is-wsl) - jpeg-js@0.4.4 (https://github.com/eugeneware/jpeg-js) @@ -740,7 +740,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF https-proxy-agent@5.0.0 AND INFORMATION -%% ip@2.0.0 NOTICES AND INFORMATION BEGIN HERE +%% ip@2.0.1 NOTICES AND INFORMATION BEGIN HERE ========================================= # IP [![](https://badge.fury.io/js/ip.svg)](https://www.npmjs.com/package/ip) @@ -833,7 +833,7 @@ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF ip@2.0.0 AND INFORMATION +END OF ip@2.0.1 AND INFORMATION %% is-docker@2.2.1 NOTICES AND INFORMATION BEGIN HERE ========================================= diff --git a/playwright/packages/playwright-core/bin/reinstall_chrome_beta_win.ps1 b/playwright/packages/playwright-core/bin/reinstall_chrome_beta_win.ps1 index dca835f5fd..3fbe5515ad 100644 --- a/playwright/packages/playwright-core/bin/reinstall_chrome_beta_win.ps1 +++ b/playwright/packages/playwright-core/bin/reinstall_chrome_beta_win.ps1 @@ -18,6 +18,7 @@ if (Test-Path "${env:ProgramFiles(x86)}$suffix") { } elseif (Test-Path "${env:ProgramFiles}$suffix") { (Get-Item "${env:ProgramFiles}$suffix").VersionInfo } else { - Write-Host "ERROR: failed to install Google Chrome Beta" + Write-Host "ERROR: Failed to install Google Chrome Beta." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." exit 1 } diff --git a/playwright/packages/playwright-core/bin/reinstall_chrome_stable_win.ps1 b/playwright/packages/playwright-core/bin/reinstall_chrome_stable_win.ps1 index 0162dd7494..7ca2dbaf5a 100644 --- a/playwright/packages/playwright-core/bin/reinstall_chrome_stable_win.ps1 +++ b/playwright/packages/playwright-core/bin/reinstall_chrome_stable_win.ps1 @@ -18,6 +18,7 @@ if (Test-Path "${env:ProgramFiles(x86)}$suffix") { } elseif (Test-Path "${env:ProgramFiles}$suffix") { (Get-Item "${env:ProgramFiles}$suffix").VersionInfo } else { - Write-Host "ERROR: failed to install Google Chrome" + Write-Host "ERROR: Failed to install Google Chrome." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." exit 1 } diff --git a/playwright/packages/playwright-core/bin/reinstall_msedge_beta_win.ps1 b/playwright/packages/playwright-core/bin/reinstall_msedge_beta_win.ps1 index 3f5ed9aafe..cce0d0bf70 100644 --- a/playwright/packages/playwright-core/bin/reinstall_msedge_beta_win.ps1 +++ b/playwright/packages/playwright-core/bin/reinstall_msedge_beta_win.ps1 @@ -17,6 +17,7 @@ if (Test-Path "${env:ProgramFiles(x86)}$suffix") { } elseif (Test-Path "${env:ProgramFiles}$suffix") { (Get-Item "${env:ProgramFiles}$suffix").VersionInfo } else { - Write-Host "ERROR: failed to install Microsoft Edge" + Write-Host "ERROR: Failed to install Microsoft Edge Beta." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." exit 1 } diff --git a/playwright/packages/playwright-core/bin/reinstall_msedge_dev_win.ps1 b/playwright/packages/playwright-core/bin/reinstall_msedge_dev_win.ps1 index 0e72cc49ff..22e6db84b8 100644 --- a/playwright/packages/playwright-core/bin/reinstall_msedge_dev_win.ps1 +++ b/playwright/packages/playwright-core/bin/reinstall_msedge_dev_win.ps1 @@ -17,6 +17,7 @@ if (Test-Path "${env:ProgramFiles(x86)}$suffix") { } elseif (Test-Path "${env:ProgramFiles}$suffix") { (Get-Item "${env:ProgramFiles}$suffix").VersionInfo } else { - Write-Host "ERROR: failed to install Microsoft Edge" + Write-Host "ERROR: Failed to install Microsoft Edge Dev." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." exit 1 } diff --git a/playwright/packages/playwright-core/bin/reinstall_msedge_stable_win.ps1 b/playwright/packages/playwright-core/bin/reinstall_msedge_stable_win.ps1 index cf856d7b45..31fdf51386 100644 --- a/playwright/packages/playwright-core/bin/reinstall_msedge_stable_win.ps1 +++ b/playwright/packages/playwright-core/bin/reinstall_msedge_stable_win.ps1 @@ -18,6 +18,7 @@ if (Test-Path "${env:ProgramFiles(x86)}$suffix") { } elseif (Test-Path "${env:ProgramFiles}$suffix") { (Get-Item "${env:ProgramFiles}$suffix").VersionInfo } else { - Write-Host "ERROR: failed to install Microsoft Edge" + Write-Host "ERROR: Failed to install Microsoft Edge." + Write-Host "ERROR: This could be due to insufficient privileges, in which case re-running as Administrator may help." exit 1 } \ No newline at end of file diff --git a/playwright/packages/playwright-core/browsers.json b/playwright/packages/playwright-core/browsers.json index 31ccfa3430..bf862ae74c 100644 --- a/playwright/packages/playwright-core/browsers.json +++ b/playwright/packages/playwright-core/browsers.json @@ -3,50 +3,49 @@ "browsers": [ { "name": "chromium", - "revision": "1097", + "revision": "1105", "installByDefault": true, - "browserVersion": "121.0.6167.57" + "browserVersion": "123.0.6312.4" }, { "name": "chromium-with-symbols", - "revision": "1097", + "revision": "1105", "installByDefault": false, - "browserVersion": "121.0.6167.57" + "browserVersion": "123.0.6312.4" }, { "name": "chromium-tip-of-tree", - "revision": "1184", + "revision": "1195", "installByDefault": false, - "browserVersion": "122.0.6240.0" + "browserVersion": "123.0.6312.0" }, { "name": "firefox", - "revision": "1438", + "revision": "1440", "installByDefault": true, - "browserVersion": "121.0" + "browserVersion": "123.0" }, { "name": "firefox-asan", - "revision": "1438", + "revision": "1440", "installByDefault": false, - "browserVersion": "121.0" + "browserVersion": "123.0" }, { "name": "firefox-beta", - "revision": "1437", + "revision": "1440", "installByDefault": false, - "browserVersion": "121.0b8" + "browserVersion": "124.0b3" }, { "name": "webkit", - "revision": "1967", + "revision": "1983", "installByDefault": true, "revisionOverrides": { "mac10.14": "1446", "mac10.15": "1616", "mac11": "1816", - "mac11-arm64": "1816", - "ubuntu18.04-x64": "1728" + "mac11-arm64": "1816" }, "browserVersion": "17.4" }, diff --git a/playwright/packages/playwright-core/bundles/utils/package-lock.json b/playwright/packages/playwright-core/bundles/utils/package-lock.json index db72b4bc09..dc46c9d6bd 100644 --- a/playwright/packages/playwright-core/bundles/utils/package-lock.json +++ b/playwright/packages/playwright-core/bundles/utils/package-lock.json @@ -224,9 +224,9 @@ } }, "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" }, "node_modules/is-docker": { "version": "2.2.1", @@ -563,9 +563,9 @@ } }, "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" }, "is-docker": { "version": "2.2.1", diff --git a/playwright/packages/playwright-core/cli.js b/playwright/packages/playwright-core/cli.js index bd995de114..fb309ead0a 100755 --- a/playwright/packages/playwright-core/cli.js +++ b/playwright/packages/playwright-core/cli.js @@ -14,4 +14,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('playwright-core/lib/cli/cli'); +const { program } = require('./lib/cli/programWithTestStub'); +program.parse(process.argv); diff --git a/playwright/packages/playwright-core/package.json b/playwright/packages/playwright-core/package.json index d641be5ade..0162c9e12c 100644 --- a/playwright/packages/playwright-core/package.json +++ b/playwright/packages/playwright-core/package.json @@ -1,6 +1,6 @@ { "name": "playwright-core", - "version": "1.41.1", + "version": "1.42.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -27,7 +27,6 @@ "./lib/image_tools/compare": "./lib/image_tools/compare.js", "./lib/image_tools/imageChannel": "./lib/image_tools/imageChannel.js", "./lib/image_tools/colorUtils": "./lib/image_tools/colorUtils.js", - "./lib/cli/cli": "./lib/cli/cli.js", "./lib/cli/program": "./lib/cli/program.js", "./lib/server/registry/index": "./lib/server/registry/index.js", "./lib/remote/playwrightServer": "./lib/remote/playwrightServer.js", diff --git a/playwright/packages/playwright-core/src/cli/driver.ts b/playwright/packages/playwright-core/src/cli/driver.ts index 245c733483..189ec4d2f6 100644 --- a/playwright/packages/playwright-core/src/cli/driver.ts +++ b/playwright/packages/playwright-core/src/cli/driver.ts @@ -38,7 +38,15 @@ export function runDriver() { }); const transport = new PipeTransport(process.stdout, process.stdin); transport.onmessage = (message: string) => dispatcherConnection.dispatch(JSON.parse(message)); - dispatcherConnection.onmessage = message => transport.send(JSON.stringify(message)); + // Certain Language Binding JSON parsers (e.g. .NET) do not like strings with lone surrogates. + const isJavaScriptLanguageBinding = !process.env.PW_LANG_NAME || process.env.PW_LANG_NAME === 'javascript'; + const replacer = !isJavaScriptLanguageBinding && (String.prototype as any).toWellFormed ? (key: string, value: any): any => { + if (typeof value === 'string') + // @ts-expect-error + return value.toWellFormed(); + return value; + } : undefined; + dispatcherConnection.onmessage = message => transport.send(JSON.stringify(message, replacer)); transport.onclose = () => { // Drop any messages during shutdown on the floor. dispatcherConnection.onmessage = () => {}; diff --git a/playwright/packages/playwright-core/src/cli/program.ts b/playwright/packages/playwright-core/src/cli/program.ts index 6e67df3f38..c38683a832 100644 --- a/playwright/packages/playwright-core/src/cli/program.ts +++ b/playwright/packages/playwright-core/src/cli/program.ts @@ -21,6 +21,7 @@ import os from 'os'; import path from 'path'; import type { Command } from '../utilsBundle'; import { program } from '../utilsBundle'; +export { program } from '../utilsBundle'; import { runDriver, runServer, printApiJson, launchBrowserServer } from './driver'; import type { OpenTraceViewerOptions } from '../server/trace/viewer/traceViewer'; import { openTraceInBrowser, openTraceViewerApp } from '../server/trace/viewer/traceViewer'; @@ -64,7 +65,7 @@ Examples: commandWithOpenOptions('codegen [url]', 'open page and generate code for user actions', [ ['-o, --output <file name>', 'saves the generated script to a file'], - ['--target <language>', `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, java`, codegenId()], + ['--target <language>', `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, java, java-junit`, codegenId()], ['--save-trace <filename>', 'record a trace for the session and save it to a file'], ['--test-id-attribute <attributeName>', 'use the specified attribute to generate data test ID selectors'], ]).action(function(url, options) { @@ -159,6 +160,10 @@ program } else { const forceReinstall = hasNoArguments ? false : !!options.force; await registry.install(executables, forceReinstall); + await registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || 'javascript').catch((e: Error) => { + e.name = 'Playwright Host validation warning'; + console.error(e); + }); } } catch (e) { console.log(`Failed to install browsers\n${e}`); @@ -690,5 +695,3 @@ function buildBasePlaywrightCLICommand(cliTargetLang: string | undefined): strin } } } - -export default program; diff --git a/playwright/packages/playwright-core/src/cli/cli.ts b/playwright/packages/playwright-core/src/cli/programWithTestStub.ts old mode 100755 new mode 100644 similarity index 96% rename from playwright/packages/playwright-core/src/cli/cli.ts rename to playwright/packages/playwright-core/src/cli/programWithTestStub.ts index 8681efe73a..1c11c14ec2 --- a/playwright/packages/playwright-core/src/cli/cli.ts +++ b/playwright/packages/playwright-core/src/cli/programWithTestStub.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env node - /** * Copyright (c) Microsoft Corporation. * @@ -19,7 +17,8 @@ /* eslint-disable no-console */ import { getPackageManager, gracefullyProcessExitDoNotHang } from '../utils'; -import program from './program'; +import { program } from './program'; +export { program } from './program'; function printPlaywrightTestError(command: string) { const packages: string[] = []; @@ -66,5 +65,3 @@ function addExternalPlaywrightTestCommands() { if (!process.env.PW_LANG_NAME) addExternalPlaywrightTestCommands(); - -program.parse(process.argv); diff --git a/playwright/packages/playwright-core/src/client/browserContext.ts b/playwright/packages/playwright-core/src/client/browserContext.ts index 6f32385ee8..39140f904c 100644 --- a/playwright/packages/playwright-core/src/client/browserContext.ts +++ b/playwright/packages/playwright-core/src/client/browserContext.ts @@ -526,7 +526,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions function toAcceptDownloadsProtocol(acceptDownloads?: boolean) { if (acceptDownloads === undefined) return undefined; - if (acceptDownloads === true) + if (acceptDownloads) return 'accept'; return 'deny'; } diff --git a/playwright/packages/playwright-core/src/client/channelOwner.ts b/playwright/packages/playwright-core/src/client/channelOwner.ts index b493e7d5d5..d12fb41dc5 100644 --- a/playwright/packages/playwright-core/src/client/channelOwner.ts +++ b/playwright/packages/playwright-core/src/client/channelOwner.ts @@ -17,7 +17,7 @@ import { EventEmitter } from 'events'; import type * as channels from '@protocol/channels'; import { maybeFindValidator, ValidationError, type ValidatorContext } from '../protocol/validator'; -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import type { ExpectZone } from '../utils/stackTrace'; import { captureRawStack, captureLibraryStackTrace, stringifyStackFrames } from '../utils/stackTrace'; import { isUnderTest } from '../utils'; diff --git a/playwright/packages/playwright-core/src/client/clientHelper.ts b/playwright/packages/playwright-core/src/client/clientHelper.ts index f2a617ea09..540230a4fc 100644 --- a/playwright/packages/playwright-core/src/client/clientHelper.ts +++ b/playwright/packages/playwright-core/src/client/clientHelper.ts @@ -43,8 +43,12 @@ export async function evaluationScript(fun: Function | string | { path?: string, if (fun.path !== undefined) { let source = await fs.promises.readFile(fun.path, 'utf8'); if (addSourceUrl) - source += '\n//# sourceURL=' + fun.path.replace(/\n/g, ''); + source = addSourceUrlToScript(source, fun.path); return source; } throw new Error('Either path or content property must be present'); } + +export function addSourceUrlToScript(source: string, path: string): string { + return `${source}\n//# sourceURL=${path.replace(/\n/g, '')}`; +} diff --git a/playwright/packages/playwright-core/src/client/connection.ts b/playwright/packages/playwright-core/src/client/connection.ts index bc124d91fa..f468e3ff41 100644 --- a/playwright/packages/playwright-core/src/client/connection.ts +++ b/playwright/packages/playwright-core/src/client/connection.ts @@ -32,7 +32,7 @@ import { Electron, ElectronApplication } from './electron'; import type * as channels from '@protocol/channels'; import { Stream } from './stream'; import { WritableStream } from './writableStream'; -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import { SelectorsOwner } from './selectors'; import { Android, AndroidSocket, AndroidDevice } from './android'; import { Artifact } from './artifact'; diff --git a/playwright/packages/playwright-core/src/client/consoleMessage.ts b/playwright/packages/playwright-core/src/client/consoleMessage.ts index 1c55639ab5..fcdc3fd149 100644 --- a/playwright/packages/playwright-core/src/client/consoleMessage.ts +++ b/playwright/packages/playwright-core/src/client/consoleMessage.ts @@ -25,10 +25,10 @@ type ConsoleMessageLocation = channels.BrowserContextConsoleEvent['location']; export class ConsoleMessage implements api.ConsoleMessage { private _page: Page | null; - private _event: channels.BrowserContextConsoleEvent; + private _event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent; - constructor(event: channels.BrowserContextConsoleEvent) { - this._page = event.page ? Page.from(event.page) : null; + constructor(event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent) { + this._page = ('page' in event && event.page) ? Page.from(event.page) : null; this._event = event; } diff --git a/playwright/packages/playwright-core/src/client/electron.ts b/playwright/packages/playwright-core/src/client/electron.ts index 247a584367..f20bbfba19 100644 --- a/playwright/packages/playwright-core/src/client/electron.ts +++ b/playwright/packages/playwright-core/src/client/electron.ts @@ -26,6 +26,7 @@ import { envObjectToArray } from './clientHelper'; import { Events } from './events'; import { JSHandle, parseResult, serializeArgument } from './jsHandle'; import type { Page } from './page'; +import { ConsoleMessage } from './consoleMessage'; import type { Env, WaitForEventOptions, Headers, BrowserContextOptions } from './types'; import { Waiter } from './waiter'; import { TargetClosedError } from './errors'; @@ -81,6 +82,10 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati this._isClosed = true; this.emit(Events.ElectronApplication.Close); }); + this._channel.on('console', event => this.emit(Events.ElectronApplication.Console, new ConsoleMessage(event))); + this._setEventToSubscriptionMapping(new Map<string, channels.ElectronApplicationUpdateSubscriptionParams['event']>([ + [Events.ElectronApplication.Console, 'console'], + ])); } process(): childProcess.ChildProcess { diff --git a/playwright/packages/playwright-core/src/client/events.ts b/playwright/packages/playwright-core/src/client/events.ts index 287e5915e5..a074b26f3d 100644 --- a/playwright/packages/playwright-core/src/client/events.ts +++ b/playwright/packages/playwright-core/src/client/events.ts @@ -91,6 +91,7 @@ export const Events = { ElectronApplication: { Close: 'close', + Console: 'console', Window: 'window', }, }; diff --git a/playwright/packages/playwright-core/src/client/frame.ts b/playwright/packages/playwright-core/src/client/frame.ts index 6845d8be6b..582490d0af 100644 --- a/playwright/packages/playwright-core/src/client/frame.ts +++ b/playwright/packages/playwright-core/src/client/frame.ts @@ -35,6 +35,7 @@ import { kLifecycleEvents } from './types'; import { urlMatches } from '../utils/network'; import type * as api from '../../types/types'; import type * as structs from '../../types/structs'; +import { addSourceUrlToScript } from './clientHelper'; export type WaitForNavigationOptions = { timeout?: number, @@ -266,7 +267,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr const copy = { ...options }; if (copy.path) { copy.content = (await fs.promises.readFile(copy.path)).toString(); - copy.content += '//# sourceURL=' + copy.path.replace(/\n/g, ''); + copy.content = addSourceUrlToScript(copy.content, copy.path); } return ElementHandle.from((await this._channel.addScriptTag({ ...copy })).element); } diff --git a/playwright/packages/playwright-core/src/client/harRouter.ts b/playwright/packages/playwright-core/src/client/harRouter.ts index a05945d80c..5fe32a39be 100644 --- a/playwright/packages/playwright-core/src/client/harRouter.ts +++ b/playwright/packages/playwright-core/src/client/harRouter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import type { BrowserContext } from './browserContext'; import type { LocalUtils } from './localUtils'; import type { Route } from './network'; @@ -62,6 +62,13 @@ export class HarRouter { } if (response.action === 'fulfill') { + // If the response status is -1, the request was canceled or stalled, so we just stall it here. + // See https://github.com/microsoft/playwright/issues/29311. + // TODO: it'd be better to abort such requests, but then we likely need to respect the timing, + // because the request might have been stalled for a long time until the very end of the + // test when HAR was recorded but we'd abort it immediately. + if (response.status === -1) + return; await route.fulfill({ status: response.status, headers: Object.fromEntries(response.headers!.map(h => [h.name, h.value])), diff --git a/playwright/packages/playwright-core/src/client/page.ts b/playwright/packages/playwright-core/src/client/page.ts index 570e391cb7..8fd1394a6f 100644 --- a/playwright/packages/playwright-core/src/client/page.ts +++ b/playwright/packages/playwright-core/src/client/page.ts @@ -62,14 +62,13 @@ type PDFOptions = Omit<channels.PagePdfParams, 'width' | 'height' | 'margin'> & path?: string, }; -type ExpectScreenshotOptions = Omit<channels.PageExpectScreenshotOptions, 'screenshotOptions' | 'locator' | 'expected'> & { +export type ExpectScreenshotOptions = Omit<channels.PageExpectScreenshotOptions, 'locator' | 'expected' | 'mask'> & { expected?: Buffer, - locator?: Locator, + locator?: api.Locator, isNot: boolean, - screenshotOptions: Omit<channels.PageExpectScreenshotOptions['screenshotOptions'], 'mask'> & { mask?: Locator[] } + mask?: api.Locator[], }; - export class Page extends ChannelOwner<channels.PageChannel> implements api.Page { private _browserContext: BrowserContext; _ownedContext: BrowserContext | undefined; @@ -97,6 +96,8 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page _closeWasCalled: boolean = false; private _harRouters: HarRouter[] = []; + private _locatorHandlers = new Map<number, Function>(); + static from(page: channels.PageChannel): Page { return (page as any)._object; } @@ -133,6 +134,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page this._channel.on('fileChooser', ({ element, isMultiple }) => this.emit(Events.Page.FileChooser, new FileChooser(this, ElementHandle.from(element), isMultiple))); this._channel.on('frameAttached', ({ frame }) => this._onFrameAttached(Frame.from(frame))); 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('video', ({ artifact }) => { const artifactObject = Artifact.from(artifact); @@ -360,6 +362,22 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page return Response.fromNullable((await this._channel.reload({ ...options, waitUntil })).response); } + async addLocatorHandler(locator: Locator, handler: Function): Promise<void> { + if (locator._frame !== this._mainFrame) + throw new Error(`Locator must belong to the main frame of this page`); + const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector }); + this._locatorHandlers.set(uid, handler); + } + + private async _onLocatorHandlerTriggered(uid: number) { + try { + const handler = this._locatorHandlers.get(uid); + await handler?.(); + } finally { + this._wrapApiCall(() => this._channel.resolveLocatorHandlerNoReply({ uid }), true).catch(() => {}); + } + } + async waitForLoadState(state?: LifecycleEvent, options?: { timeout?: number }): Promise<void> { return await this._mainFrame.waitForLoadState(state, options); } @@ -528,22 +546,19 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page } async _expectScreenshot(options: ExpectScreenshotOptions): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[]}> { - const mask = options.screenshotOptions?.mask ? options.screenshotOptions?.mask.map(locator => ({ - frame: locator._frame._channel, - selector: locator._selector, + const mask = options?.mask ? options?.mask.map(locator => ({ + frame: (locator as Locator)._frame._channel, + selector: (locator as Locator)._selector, })) : undefined; const locator = options.locator ? { - frame: options.locator._frame._channel, - selector: options.locator._selector, + frame: (options.locator as Locator)._frame._channel, + selector: (options.locator as Locator)._selector, } : undefined; return await this._channel.expectScreenshot({ ...options, isNot: !!options.isNot, locator, - screenshotOptions: { - ...options.screenshotOptions, - mask, - } + mask, }); } diff --git a/playwright/packages/playwright-core/src/common/socksProxy.ts b/playwright/packages/playwright-core/src/common/socksProxy.ts index 0c78611e93..ebdf7298f2 100644 --- a/playwright/packages/playwright-core/src/common/socksProxy.ts +++ b/playwright/packages/playwright-core/src/common/socksProxy.ts @@ -17,7 +17,7 @@ import EventEmitter from 'events'; import type { AddressInfo } from 'net'; import net from 'net'; -import { debugLogger } from './debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import { createSocket } from '../utils/happy-eyeballs'; import { assert, createGuid, } from '../utils'; diff --git a/playwright/packages/playwright-core/src/outofprocess.ts b/playwright/packages/playwright-core/src/outofprocess.ts index 537f37c28d..81deaead67 100644 --- a/playwright/packages/playwright-core/src/outofprocess.ts +++ b/playwright/packages/playwright-core/src/outofprocess.ts @@ -34,7 +34,7 @@ class PlaywrightClient { private _closePromise = new ManualPromise<void>(); constructor(env: any) { - this._driverProcess = childProcess.fork(path.join(__dirname, 'cli', 'cli.js'), ['run-driver'], { + this._driverProcess = childProcess.fork(path.join(__dirname, '..', 'cli.js'), ['run-driver'], { stdio: 'pipe', detached: true, env: { diff --git a/playwright/packages/playwright-core/src/protocol/validator.ts b/playwright/packages/playwright-core/src/protocol/validator.ts index 5a6e9363d8..f33a3891b6 100644 --- a/playwright/packages/playwright-core/src/protocol/validator.ts +++ b/playwright/packages/playwright-core/src/protocol/validator.ts @@ -382,13 +382,6 @@ scheme.DebugControllerSourceChangedEvent = tObject({ scheme.DebugControllerPausedEvent = tObject({ paused: tBoolean, }); -scheme.DebugControllerBrowsersChangedEvent = tObject({ - browsers: tArray(tObject({ - contexts: tArray(tObject({ - pages: tArray(tString), - })), - })), -}); scheme.DebugControllerInitializeParams = tObject({ codegenId: tString, sdkLanguage: tEnum(['javascript', 'python', 'java', 'csharp']), @@ -772,7 +765,6 @@ scheme.BrowserContextBindingCallEvent = tObject({ binding: tChannel(['BindingCall']), }); scheme.BrowserContextConsoleEvent = tObject({ - page: tChannel(['Page']), type: tString, text: tString, args: tArray(tChannel(['ElementHandle', 'JSHandle'])), @@ -781,6 +773,7 @@ scheme.BrowserContextConsoleEvent = tObject({ lineNumber: tNumber, columnNumber: tNumber, }), + page: tChannel(['Page']), }); scheme.BrowserContextCloseEvent = tOptional(tObject({})); scheme.BrowserContextDialogEvent = tObject({ @@ -983,6 +976,9 @@ scheme.PageFrameAttachedEvent = tObject({ scheme.PageFrameDetachedEvent = tObject({ frame: tChannel(['Frame']), }); +scheme.PageLocatorHandlerTriggeredEvent = tObject({ + uid: tNumber, +}); scheme.PageRouteEvent = tObject({ route: tChannel(['Route']), }); @@ -1038,6 +1034,16 @@ scheme.PageGoForwardParams = tObject({ scheme.PageGoForwardResult = tObject({ response: tOptional(tChannel(['Response'])), }); +scheme.PageRegisterLocatorHandlerParams = tObject({ + selector: tString, +}); +scheme.PageRegisterLocatorHandlerResult = tObject({ + uid: tNumber, +}); +scheme.PageResolveLocatorHandlerNoReplyParams = tObject({ + uid: tNumber, +}); +scheme.PageResolveLocatorHandlerNoReplyResult = tOptional(tObject({})); scheme.PageReloadParams = tObject({ timeout: tOptional(tNumber), waitUntil: tOptional(tType('LifecycleEvent')), @@ -1053,26 +1059,22 @@ scheme.PageExpectScreenshotParams = tObject({ frame: tChannel(['Frame']), selector: tString, })), - comparatorOptions: tOptional(tObject({ - comparator: tOptional(tString), - maxDiffPixels: tOptional(tNumber), - maxDiffPixelRatio: tOptional(tNumber), - threshold: tOptional(tNumber), - })), - screenshotOptions: tOptional(tObject({ - fullPage: tOptional(tBoolean), - clip: tOptional(tType('Rect')), - omitBackground: tOptional(tBoolean), - caret: tOptional(tEnum(['hide', 'initial'])), - animations: tOptional(tEnum(['disabled', 'allow'])), - scale: tOptional(tEnum(['css', 'device'])), - mask: tOptional(tArray(tObject({ - frame: tChannel(['Frame']), - selector: tString, - }))), - maskColor: tOptional(tString), - style: tOptional(tString), - })), + comparator: tOptional(tString), + maxDiffPixels: tOptional(tNumber), + maxDiffPixelRatio: tOptional(tNumber), + threshold: tOptional(tNumber), + fullPage: tOptional(tBoolean), + clip: tOptional(tType('Rect')), + omitBackground: tOptional(tBoolean), + caret: tOptional(tEnum(['hide', 'initial'])), + animations: tOptional(tEnum(['disabled', 'allow'])), + scale: tOptional(tEnum(['css', 'device'])), + mask: tOptional(tArray(tObject({ + frame: tChannel(['Frame']), + selector: tString, + }))), + maskColor: tOptional(tString), + style: tOptional(tString), }); scheme.PageExpectScreenshotResult = tObject({ diff: tOptional(tBinary), @@ -1201,6 +1203,8 @@ scheme.PagePdfParams = tObject({ left: tOptional(tString), right: tOptional(tString), })), + tagged: tOptional(tBoolean), + outline: tOptional(tBoolean), }); scheme.PagePdfResult = tObject({ pdf: tBinary, @@ -2252,6 +2256,16 @@ scheme.ElectronApplicationInitializer = tObject({ context: tChannel(['BrowserContext']), }); scheme.ElectronApplicationCloseEvent = tOptional(tObject({})); +scheme.ElectronApplicationConsoleEvent = tObject({ + type: tString, + text: tString, + args: tArray(tChannel(['ElementHandle', 'JSHandle'])), + location: tObject({ + url: tString, + lineNumber: tNumber, + columnNumber: tNumber, + }), +}); scheme.ElectronApplicationBrowserWindowParams = tObject({ page: tChannel(['Page']), }); @@ -2274,6 +2288,11 @@ scheme.ElectronApplicationEvaluateExpressionHandleParams = tObject({ scheme.ElectronApplicationEvaluateExpressionHandleResult = tObject({ handle: tChannel(['ElementHandle', 'JSHandle']), }); +scheme.ElectronApplicationUpdateSubscriptionParams = tObject({ + event: tEnum(['console']), + enabled: tBoolean, +}); +scheme.ElectronApplicationUpdateSubscriptionResult = tOptional(tObject({})); scheme.ElectronApplicationCloseParams = tOptional(tObject({})); scheme.ElectronApplicationCloseResult = tOptional(tObject({})); scheme.AndroidInitializer = tOptional(tObject({})); diff --git a/playwright/packages/playwright-core/src/remote/playwrightConnection.ts b/playwright/packages/playwright-core/src/remote/playwrightConnection.ts index c6898f7148..cce31207a8 100644 --- a/playwright/packages/playwright-core/src/remote/playwrightConnection.ts +++ b/playwright/packages/playwright-core/src/remote/playwrightConnection.ts @@ -27,7 +27,7 @@ import { AndroidDevice } from '../server/android/android'; import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher'; import { startProfiling, stopProfiling } from '../utils'; import { monotonicTime } from '../utils'; -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; export type ClientType = 'controller' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android'; diff --git a/playwright/packages/playwright-core/src/remote/playwrightServer.ts b/playwright/packages/playwright-core/src/remote/playwrightServer.ts index 664a5fce77..121cc2d83a 100644 --- a/playwright/packages/playwright-core/src/remote/playwrightServer.ts +++ b/playwright/packages/playwright-core/src/remote/playwrightServer.ts @@ -14,24 +14,18 @@ * limitations under the License. */ -import { wsServer } from '../utilsBundle'; -import type { WebSocketServer } from '../utilsBundle'; -import type http from 'http'; import type { Browser } from '../server/browser'; import type { Playwright } from '../server/playwright'; import { createPlaywright } from '../server/playwright'; import { PlaywrightConnection } from './playwrightConnection'; import type { ClientType } from './playwrightConnection'; import type { LaunchOptions } from '../server/types'; -import { ManualPromise } from '../utils/manualPromise'; +import { Semaphore } from '../utils/semaphore'; import type { AndroidDevice } from '../server/android/android'; import type { SocksProxy } from '../common/socksProxy'; -import { debugLogger } from '../common/debugLogger'; -import { createHttpServer, userAgentVersionMatchesErrorMessage } from '../utils'; -import { perMessageDeflate } from '../server/transport'; - -let lastConnectionId = 0; -const kConnectionSymbol = Symbol('kConnection'); +import { debugLogger } from '../utils/debugLogger'; +import { userAgentVersionMatchesErrorMessage } from '../utils'; +import { WSServer } from '../utils/wsServer'; type ServerOptions = { path: string; @@ -44,9 +38,8 @@ type ServerOptions = { export class PlaywrightServer { private _preLaunchedPlaywright: Playwright | undefined; - private _wsServer: WebSocketServer | undefined; - private _server: http.Server | undefined; private _options: ServerOptions; + private _wsServer: WSServer; constructor(options: ServerOptions) { this._options = options; @@ -54,183 +47,85 @@ export class PlaywrightServer { this._preLaunchedPlaywright = options.preLaunchedBrowser.attribution.playwright; if (options.preLaunchedAndroidDevice) this._preLaunchedPlaywright = options.preLaunchedAndroidDevice._android.attribution.playwright; - } - - async listen(port: number = 0, hostname?: string): Promise<string> { - debugLogger.log('server', `Server started at ${new Date()}`); - - const server = createHttpServer((request: http.IncomingMessage, response: http.ServerResponse) => { - if (request.method === 'GET' && request.url === '/json') { - response.setHeader('Content-Type', 'application/json'); - response.end(JSON.stringify({ - wsEndpointPath: this._options.path, - })); - return; - } - response.end('Running'); - }); - server.on('error', error => debugLogger.log('server', String(error))); - this._server = server; - - const wsEndpoint = await new Promise<string>((resolve, reject) => { - server.listen(port, hostname, () => { - const address = server.address(); - if (!address) { - reject(new Error('Could not bind server socket')); - return; - } - const wsEndpoint = typeof address === 'string' ? `${address}${this._options.path}` : `ws://${hostname || 'localhost'}:${address.port}${this._options.path}`; - resolve(wsEndpoint); - }).on('error', reject); - }); - debugLogger.log('server', 'Listening at ' + wsEndpoint); - this._wsServer = new wsServer({ - noServer: true, - perMessageDeflate, - }); const browserSemaphore = new Semaphore(this._options.maxConnections); const controllerSemaphore = new Semaphore(1); const reuseBrowserSemaphore = new Semaphore(1); - if (process.env.PWTEST_SERVER_WS_HEADERS) { - this._wsServer.on('headers', (headers, request) => { - headers.push(process.env.PWTEST_SERVER_WS_HEADERS!); - }); - } - server.on('upgrade', (request, socket, head) => { - const pathname = new URL('http://localhost' + request.url!).pathname; - if (pathname !== this._options.path) { - socket.write(`HTTP/${request.httpVersion} 400 Bad Request\r\n\r\n`); - socket.destroy(); - return; - } - const uaError = userAgentVersionMatchesErrorMessage(request.headers['user-agent'] || ''); - if (uaError) { - socket.write(`HTTP/${request.httpVersion} 428 Precondition Required\r\n\r\n${uaError}`); - socket.destroy(); - return; - } - - this._wsServer?.handleUpgrade(request, socket, head, ws => this._wsServer?.emit('connection', ws, request)); - }); - this._wsServer.on('connection', (ws, request) => { - debugLogger.log('server', 'Connected client ws.extension=' + ws.extensions); - const url = new URL('http://localhost' + (request.url || '')); - const browserHeader = request.headers['x-playwright-browser']; - const browserName = url.searchParams.get('browser') || (Array.isArray(browserHeader) ? browserHeader[0] : browserHeader) || null; - const proxyHeader = request.headers['x-playwright-proxy']; - const proxyValue = url.searchParams.get('proxy') || (Array.isArray(proxyHeader) ? proxyHeader[0] : proxyHeader); - - const launchOptionsHeader = request.headers['x-playwright-launch-options'] || ''; - const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader; - const launchOptionsParam = url.searchParams.get('launch-options'); - let launchOptions: LaunchOptions = {}; - try { - launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue); - } catch (e) { - } + this._wsServer = new WSServer({ + onUpgrade: (request, socket) => { + const uaError = userAgentVersionMatchesErrorMessage(request.headers['user-agent'] || ''); + if (uaError) + return { error: `HTTP/${request.httpVersion} 428 Precondition Required\r\n\r\n${uaError}` }; + }, + + onHeaders: headers => { + if (process.env.PWTEST_SERVER_WS_HEADERS) + headers.push(process.env.PWTEST_SERVER_WS_HEADERS!); + }, + + onConnection: (request, url, ws, id) => { + const browserHeader = request.headers['x-playwright-browser']; + const browserName = url.searchParams.get('browser') || (Array.isArray(browserHeader) ? browserHeader[0] : browserHeader) || null; + const proxyHeader = request.headers['x-playwright-proxy']; + const proxyValue = url.searchParams.get('proxy') || (Array.isArray(proxyHeader) ? proxyHeader[0] : proxyHeader); + + const launchOptionsHeader = request.headers['x-playwright-launch-options'] || ''; + const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader; + const launchOptionsParam = url.searchParams.get('launch-options'); + let launchOptions: LaunchOptions = {}; + try { + launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue); + } catch (e) { + } - const id = String(++lastConnectionId); - debugLogger.log('server', `[${id}] serving connection: ${request.url}`); + // Instantiate playwright for the extension modes. + const isExtension = this._options.mode === 'extension'; + if (isExtension) { + if (!this._preLaunchedPlaywright) + this._preLaunchedPlaywright = createPlaywright({ sdkLanguage: 'javascript', isServer: true }); + } - // Instantiate playwright for the extension modes. - const isExtension = this._options.mode === 'extension'; - if (isExtension) { - if (!this._preLaunchedPlaywright) - this._preLaunchedPlaywright = createPlaywright({ sdkLanguage: 'javascript', isServer: true }); - } + let clientType: ClientType = 'launch-browser'; + let semaphore: Semaphore = browserSemaphore; + if (isExtension && url.searchParams.has('debug-controller')) { + clientType = 'controller'; + semaphore = controllerSemaphore; + } else if (isExtension) { + clientType = 'reuse-browser'; + semaphore = reuseBrowserSemaphore; + } else if (this._options.mode === 'launchServer') { + clientType = 'pre-launched-browser-or-android'; + semaphore = browserSemaphore; + } - let clientType: ClientType = 'launch-browser'; - let semaphore: Semaphore = browserSemaphore; - if (isExtension && url.searchParams.has('debug-controller')) { - clientType = 'controller'; - semaphore = controllerSemaphore; - } else if (isExtension) { - clientType = 'reuse-browser'; - semaphore = reuseBrowserSemaphore; - } else if (this._options.mode === 'launchServer') { - clientType = 'pre-launched-browser-or-android'; - semaphore = browserSemaphore; + return new PlaywrightConnection( + semaphore.acquire(), + clientType, ws, + { socksProxyPattern: proxyValue, browserName, launchOptions }, + { + playwright: this._preLaunchedPlaywright, + browser: this._options.preLaunchedBrowser, + androidDevice: this._options.preLaunchedAndroidDevice, + socksProxy: this._options.preLaunchedSocksProxy, + }, + id, () => semaphore.release()); + }, + + onClose: async () => { + debugLogger.log('server', 'closing browsers'); + if (this._preLaunchedPlaywright) + await Promise.all(this._preLaunchedPlaywright.allBrowsers().map(browser => browser.close({ reason: 'Playwright Server stopped' }))); + debugLogger.log('server', 'closed browsers'); } - - const connection = new PlaywrightConnection( - semaphore.acquire(), - clientType, ws, - { socksProxyPattern: proxyValue, browserName, launchOptions }, - { - playwright: this._preLaunchedPlaywright, - browser: this._options.preLaunchedBrowser, - androidDevice: this._options.preLaunchedAndroidDevice, - socksProxy: this._options.preLaunchedSocksProxy, - }, - id, () => semaphore.release()); - (ws as any)[kConnectionSymbol] = connection; }); - - return wsEndpoint; - } - - async close() { - const server = this._wsServer; - if (!server) - return; - debugLogger.log('server', 'closing websocket server'); - const waitForClose = new Promise(f => server.close(f)); - // First disconnect all remaining clients. - await Promise.all(Array.from(server.clients).map(async ws => { - const connection = (ws as any)[kConnectionSymbol] as PlaywrightConnection | undefined; - if (connection) - await connection.close(); - try { - ws.terminate(); - } catch (e) { - } - })); - await waitForClose; - debugLogger.log('server', 'closing http server'); - if (this._server) - await new Promise(f => this._server!.close(f)); - this._wsServer = undefined; - this._server = undefined; - debugLogger.log('server', 'closed server'); - - debugLogger.log('server', 'closing browsers'); - if (this._preLaunchedPlaywright) - await Promise.all(this._preLaunchedPlaywright.allBrowsers().map(browser => browser.close({ reason: 'Playwright Server stopped' }))); - debugLogger.log('server', 'closed browsers'); } -} -export class Semaphore { - private _max: number; - private _acquired = 0; - private _queue: ManualPromise[] = []; - - constructor(max: number) { - this._max = max; - } - - setMax(max: number) { - this._max = max; - } - - acquire(): Promise<void> { - const lock = new ManualPromise(); - this._queue.push(lock); - this._flush(); - return lock; - } - - release() { - --this._acquired; - this._flush(); + async listen(port: number = 0, hostname?: string): Promise<string> { + return this._wsServer.listen(port, hostname, this._options.path); } - private _flush() { - while (this._acquired < this._max && this._queue.length) { - ++this._acquired; - this._queue.shift()!.resolve(); - } + async close() { + await this._wsServer.close(); } } diff --git a/playwright/packages/playwright-core/src/server/android/android.ts b/playwright/packages/playwright-core/src/server/android/android.ts index fd54088ecf..0b4cb331b0 100644 --- a/playwright/packages/playwright-core/src/server/android/android.ts +++ b/playwright/packages/playwright-core/src/server/android/android.ts @@ -30,7 +30,7 @@ import { ProgressController } from '../progress'; import { CRBrowser } from '../chromium/crBrowser'; import { helper } from '../helper'; import { PipeTransport } from '../../protocol/transport'; -import { RecentLogsCollector } from '../../common/debugLogger'; +import { RecentLogsCollector } from '../../utils/debugLogger'; import { gracefullyCloseSet } from '../../utils/processLauncher'; import { TimeoutSettings } from '../../common/timeoutSettings'; import type * as channels from '@protocol/channels'; diff --git a/playwright/packages/playwright-core/src/server/browser.ts b/playwright/packages/playwright-core/src/server/browser.ts index 425b812b31..15d8e3b792 100644 --- a/playwright/packages/playwright-core/src/server/browser.ts +++ b/playwright/packages/playwright-core/src/server/browser.ts @@ -21,7 +21,7 @@ import { Page } from './page'; import { Download } from './download'; import type { ProxySettings } from './types'; import type { ChildProcess } from 'child_process'; -import type { RecentLogsCollector } from '../common/debugLogger'; +import type { RecentLogsCollector } from '../utils/debugLogger'; import type { CallMetadata } from './instrumentation'; import { SdkObject } from './instrumentation'; import { Artifact } from './artifact'; diff --git a/playwright/packages/playwright-core/src/server/browserType.ts b/playwright/packages/playwright-core/src/server/browserType.ts index 0a7600cd6b..cfa8862b84 100644 --- a/playwright/packages/playwright-core/src/server/browserType.ts +++ b/playwright/packages/playwright-core/src/server/browserType.ts @@ -35,7 +35,7 @@ import { DEFAULT_TIMEOUT, TimeoutSettings } from '../common/timeoutSettings'; import { debugMode } from '../utils'; import { existsAsync } from '../utils/fileUtils'; import { helper } from './helper'; -import { RecentLogsCollector } from '../common/debugLogger'; +import { RecentLogsCollector } from '../utils/debugLogger'; import type { CallMetadata } from './instrumentation'; import { SdkObject } from './instrumentation'; import { ManualPromise } from '../utils/manualPromise'; @@ -180,7 +180,7 @@ export abstract class BrowserType extends SdkObject { if (!registryExecutable || registryExecutable.browserName !== this._name) throw new Error(`Unsupported ${this._name} channel "${options.channel}"`); executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage); - await registryExecutable.validateHostRequirements(this.attribution.playwright.options.sdkLanguage); + await registry.validateHostRequirementsForExecutablesIfNeeded([registryExecutable], this.attribution.playwright.options.sdkLanguage); } const waitForWSEndpoint = (options.useWebSocket || options.args?.some(a => a.startsWith('--remote-debugging-port'))) ? new ManualPromise<string>() : undefined; @@ -225,7 +225,7 @@ export abstract class BrowserType extends SdkObject { }, }); async function closeOrKill(timeout: number): Promise<void> { - let timer: NodeJS.Timer; + let timer: NodeJS.Timeout; try { await Promise.race([ gracefullyClose(), diff --git a/playwright/packages/playwright-core/src/server/chromium/chromium.ts b/playwright/packages/playwright-core/src/server/chromium/chromium.ts index c3988d4648..0849c1c6fe 100644 --- a/playwright/packages/playwright-core/src/server/chromium/chromium.ts +++ b/playwright/packages/playwright-core/src/server/chromium/chromium.ts @@ -37,7 +37,7 @@ import { getUserAgent } from '../../utils/userAgent'; import { wrapInASCIIBox } from '../../utils/ascii'; import { debugMode, headersArrayToObject, headersObjectToArray, } from '../../utils'; import { removeFolders } from '../../utils/fileUtils'; -import { RecentLogsCollector } from '../../common/debugLogger'; +import { RecentLogsCollector } from '../../utils/debugLogger'; import type { Progress } from '../progress'; import { ProgressController } from '../progress'; import { TimeoutSettings } from '../../common/timeoutSettings'; @@ -82,7 +82,7 @@ export class Chromium extends BrowserType { const artifactsDir = await fs.promises.mkdtemp(ARTIFACTS_FOLDER); - const wsEndpoint = await urlToWSEndpoint(progress, endpointURL); + const wsEndpoint = await urlToWSEndpoint(progress, endpointURL, headersMap); progress.throwIfAborted(); const chromeTransport = await WebSocketTransport.connect(progress, wsEndpoint, headersMap); @@ -351,13 +351,14 @@ export class Chromium extends BrowserType { } } -async function urlToWSEndpoint(progress: Progress, endpointURL: string) { +async function urlToWSEndpoint(progress: Progress, endpointURL: string, headers: { [key: string]: string; }) { if (endpointURL.startsWith('ws')) return endpointURL; progress.log(`<ws preparing> retrieving websocket url from ${endpointURL}`); const httpURL = endpointURL.endsWith('/') ? `${endpointURL}json/version/` : `${endpointURL}/json/version/`; const json = await fetchData({ url: httpURL, + headers, }, async (_, resp) => new Error(`Unexpected status ${resp.statusCode} when connecting to ${httpURL}.\n` + `This does not look like a DevTools server, try connecting via ws://.`) ); diff --git a/playwright/packages/playwright-core/src/server/chromium/crBrowser.ts b/playwright/packages/playwright-core/src/server/chromium/crBrowser.ts index 0217005427..7715d8b23f 100644 --- a/playwright/packages/playwright-core/src/server/chromium/crBrowser.ts +++ b/playwright/packages/playwright-core/src/server/chromium/crBrowser.ts @@ -594,7 +594,8 @@ export class CRBrowserContext extends BrowserContext { targetId = (page._delegate as CRPage)._targetId; } else if (page instanceof Frame) { const session = (page._page._delegate as CRPage)._sessions.get(page._id); - if (!session) throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`); + if (!session) + throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`); targetId = session._targetId; } else { throw new Error('page: expected Page or Frame'); diff --git a/playwright/packages/playwright-core/src/server/chromium/crConnection.ts b/playwright/packages/playwright-core/src/server/chromium/crConnection.ts index bcf883f238..ccec4663d1 100644 --- a/playwright/packages/playwright-core/src/server/chromium/crConnection.ts +++ b/playwright/packages/playwright-core/src/server/chromium/crConnection.ts @@ -19,8 +19,8 @@ import { type RegisteredListener, assert, eventsHelper } from '../../utils'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import type { Protocol } from './protocol'; import { EventEmitter } from 'events'; -import type { RecentLogsCollector } from '../../common/debugLogger'; -import { debugLogger } from '../../common/debugLogger'; +import type { RecentLogsCollector } from '../../utils/debugLogger'; +import { debugLogger } from '../../utils/debugLogger'; import type { ProtocolLogger } from '../types'; import { helper } from '../helper'; import { ProtocolError } from '../protocolError'; diff --git a/playwright/packages/playwright-core/src/server/chromium/crNetworkManager.ts b/playwright/packages/playwright-core/src/server/chromium/crNetworkManager.ts index ad07d24eac..f3952b82fd 100644 --- a/playwright/packages/playwright-core/src/server/chromium/crNetworkManager.ts +++ b/playwright/packages/playwright-core/src/server/chromium/crNetworkManager.ts @@ -28,6 +28,7 @@ import type * as types from '../types'; import type { CRPage } from './crPage'; import { assert, headersObjectToArray } from '../../utils'; import type { CRServiceWorker } from './crServiceWorker'; +import { isProtocolError } from '../protocolError'; type SessionInfo = { session: CRSession; @@ -286,11 +287,8 @@ export class CRNetworkManager { if (requestPausedEvent) { // We do not support intercepting redirects. if (redirectedFrom || (!this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled)) { - let headers = undefined; - const previousHeaderOverrides = redirectedFrom?._originalRequestRoute?._alreadyContinuedParams?.headers; // Chromium does not preserve header overrides between redirects, so we have to do it ourselves. - if (previousHeaderOverrides) - headers = network.mergeHeaders([headersObjectToArray(requestPausedEvent.request.headers, '\n'), previousHeaderOverrides]); + const headers = redirectedFrom?._originalRequestRoute?._alreadyContinuedParams?.headers; this._session._sendMayFail('Fetch.continueRequest', { requestId: requestPausedEvent.requestId, headers }); } else { route = new RouteImpl(this._session, requestPausedEvent.requestId); @@ -571,34 +569,50 @@ class RouteImpl implements network.RouteDelegate { method: overrides.method, postData: overrides.postData ? overrides.postData.toString('base64') : undefined }; - await this._session.send('Fetch.continueRequest', this._alreadyContinuedParams); + await catchDisallowedErrors(async () => { + await this._session.send('Fetch.continueRequest', this._alreadyContinuedParams); + }); } async fulfill(response: types.NormalizedFulfillResponse) { const body = response.isBase64 ? response.body : Buffer.from(response.body).toString('base64'); const responseHeaders = splitSetCookieHeader(response.headers); - await this._session.send('Fetch.fulfillRequest', { - requestId: this._interceptionId!, - responseCode: response.status, - responsePhrase: network.STATUS_TEXTS[String(response.status)], - responseHeaders, - body, + await catchDisallowedErrors(async () => { + await this._session.send('Fetch.fulfillRequest', { + requestId: this._interceptionId!, + responseCode: response.status, + responsePhrase: network.STATUS_TEXTS[String(response.status)], + responseHeaders, + body, + }); }); } async abort(errorCode: string = 'failed') { const errorReason = errorReasons[errorCode]; assert(errorReason, 'Unknown error code: ' + errorCode); - // In certain cases, protocol will return error if the request was already canceled - // or the page was closed. We should tolerate these errors. - await this._session._sendMayFail('Fetch.failRequest', { - requestId: this._interceptionId!, - errorReason + await catchDisallowedErrors(async () => { + await this._session.send('Fetch.failRequest', { + requestId: this._interceptionId!, + errorReason + }); }); } } +// In certain cases, protocol will return error if the request was already canceled +// or the page was closed. We should tolerate these errors but propagate other. +async function catchDisallowedErrors(callback: () => Promise<void>) { + try { + return await callback(); + } catch (e) { + if (isProtocolError(e) && e.message.includes('Invalid http status code or phrase')) + throw e; + } +} + + function splitSetCookieHeader(headers: types.HeadersArray): types.HeadersArray { const index = headers.findIndex(({ name }) => name.toLowerCase() === 'set-cookie'); if (index === -1) diff --git a/playwright/packages/playwright-core/src/server/chromium/crPage.ts b/playwright/packages/playwright-core/src/server/chromium/crPage.ts index 1dd5237368..2c0d3f94b4 100644 --- a/playwright/packages/playwright-core/src/server/chromium/crPage.ts +++ b/playwright/packages/playwright-core/src/server/chromium/crPage.ts @@ -1094,7 +1094,11 @@ class FrameSession { async _updateUserAgent(): Promise<void> { const options = this._crPage._browserContext._options; - await this._client.send('Emulation.setUserAgentOverride', { userAgent: options.userAgent || '', acceptLanguage: options.locale }); + await this._client.send('Emulation.setUserAgentOverride', { + userAgent: options.userAgent || '', + acceptLanguage: options.locale, + userAgentMetadata: calculateUserAgentMetadata(options), + }); } private async _setDefaultFontFamilies(session: CRSession) { @@ -1257,3 +1261,48 @@ async function emulateTimezone(session: CRSession, timezoneId: string) { } const contextDelegateSymbol = Symbol('delegate'); + +// Chromium reference: https://source.chromium.org/chromium/chromium/src/+/main:components/embedder_support/user_agent_utils.cc;l=434;drc=70a6711e08e9f9e0d8e4c48e9ba5cab62eb010c2 +function calculateUserAgentMetadata(options: channels.BrowserNewContextParams) { + const ua = options.userAgent; + if (!ua) + return undefined; + const metadata: Protocol.Emulation.UserAgentMetadata = { + mobile: !!options.isMobile, + model: '', + architecture: 'x64', + platform: 'Windows', + platformVersion: '', + }; + const androidMatch = ua.match(/Android (\d+(\.\d+)?(\.\d+)?)/); + const iPhoneMatch = ua.match(/iPhone OS (\d+(_\d+)?)/); + const iPadMatch = ua.match(/iPad; CPU OS (\d+(_\d+)?)/); + const macOSMatch = ua.match(/Mac OS X (\d+(_\d+)?(_\d+)?)/); + const windowsMatch = ua.match(/Windows\D+(\d+(\.\d+)?(\.\d+)?)/); + if (androidMatch) { + metadata.platform = 'Android'; + metadata.platformVersion = androidMatch[1]; + metadata.architecture = 'arm'; + } else if (iPhoneMatch) { + metadata.platform = 'iOS'; + metadata.platformVersion = iPhoneMatch[1]; + metadata.architecture = 'arm'; + } else if (iPadMatch) { + metadata.platform = 'iOS'; + metadata.platformVersion = iPadMatch[1]; + metadata.architecture = 'arm'; + } else if (macOSMatch) { + metadata.platform = 'macOS'; + metadata.platformVersion = macOSMatch[1]; + if (!ua.includes('Intel')) + metadata.architecture = 'arm'; + } else if (windowsMatch) { + metadata.platform = 'Windows'; + metadata.platformVersion = windowsMatch[1]; + } else if (ua.toLowerCase().includes('linux')) { + metadata.platform = 'Linux'; + } + if (ua.includes('ARM')) + metadata.architecture = 'arm'; + return metadata; +} diff --git a/playwright/packages/playwright-core/src/server/chromium/crPdf.ts b/playwright/packages/playwright-core/src/server/chromium/crPdf.ts index 0219c18444..8dae2d1fa1 100644 --- a/playwright/packages/playwright-core/src/server/chromium/crPdf.ts +++ b/playwright/packages/playwright-core/src/server/chromium/crPdf.ts @@ -78,6 +78,8 @@ export class CRPDF { pageRanges = '', preferCSSPageSize = false, margin = {}, + tagged = false, + outline = false } = options; let paperWidth = 8.5; @@ -96,7 +98,8 @@ export class CRPDF { const marginLeft = convertPrintParameterToInches(margin.left) || 0; const marginBottom = convertPrintParameterToInches(margin.bottom) || 0; const marginRight = convertPrintParameterToInches(margin.right) || 0; - + const generateDocumentOutline = outline; + const generateTaggedPDF = tagged; const result = await this._client.send('Page.printToPDF', { transferMode: 'ReturnAsStream', landscape, @@ -112,7 +115,9 @@ export class CRPDF { marginLeft, marginRight, pageRanges, - preferCSSPageSize + preferCSSPageSize, + generateTaggedPDF, + generateDocumentOutline }); return await readProtocolStream(this._client, result.stream!); } diff --git a/playwright/packages/playwright-core/src/server/chromium/protocol.d.ts b/playwright/packages/playwright-core/src/server/chromium/protocol.d.ts index 49adee0f8b..83b8d235ce 100644 --- a/playwright/packages/playwright-core/src/server/chromium/protocol.d.ts +++ b/playwright/packages/playwright-core/src/server/chromium/protocol.d.ts @@ -245,7 +245,7 @@ If omitted, the full tree is returned. depth?: number; /** * The frame for whose document the AX tree should be retrieved. -If omited, the root frame is used. +If omitted, the root frame is used. */ frameId?: Page.FrameId; } @@ -305,7 +305,7 @@ If omitted, the root frame is used. /** * Query a DOM node's accessibility subtree for accessible name and role. This command computes the name and role for all nodes in the subtree, including those that are -ignored for accessibility, and returns those that mactch the specified name and role. If no DOM +ignored for accessibility, and returns those that match the specified name and role. If no DOM node is specified, or the DOM node does not exist, the command returns an error. If neither `accessibleName` or `role` is specified, it returns all the accessibility nodes in the subtree. */ @@ -367,6 +367,9 @@ including nodes that are ignored for accessibility. playbackRate: number; /** * `Animation`'s start time. +Milliseconds for time based animations and +percentage [0 - 100] for scroll driven animations +(i.e. when viewOrScrollTimeline exists). */ startTime: number; /** @@ -386,6 +389,39 @@ including nodes that are ignored for accessibility. animation/transition. */ cssId?: string; + /** + * View or scroll timeline + */ + viewOrScrollTimeline?: ViewOrScrollTimeline; + } + /** + * Timeline instance + */ + export interface ViewOrScrollTimeline { + /** + * Scroll container node + */ + sourceNodeId?: DOM.BackendNodeId; + /** + * Represents the starting scroll position of the timeline +as a length offset in pixels from scroll origin. + */ + startOffset?: number; + /** + * Represents the ending scroll position of the timeline +as a length offset in pixels from scroll origin. + */ + endOffset?: number; + /** + * The element whose principal box's visibility in the +scrollport defined the progress of the timeline. +Does not exist for animations with ScrollTimeline + */ + subjectNodeId?: DOM.BackendNodeId; + /** + * Orientation of the scroll + */ + axis: DOM.ScrollOrientation; } /** * AnimationEffect instance @@ -409,6 +445,9 @@ animation/transition. iterations: number; /** * `AnimationEffect`'s iteration duration. +Milliseconds for time based animations and +percentage [0 - 100] for scroll driven animations +(i.e. when viewOrScrollTimeline exists). */ duration: number; /** @@ -675,7 +714,7 @@ may be used by the front-end as additional context. request?: AffectedRequest; } export type MixedContentResolutionStatus = "MixedContentBlocked"|"MixedContentAutomaticallyUpgraded"|"MixedContentWarning"; - export type MixedContentResourceType = "AttributionSrc"|"Audio"|"Beacon"|"CSPReport"|"Download"|"EventSource"|"Favicon"|"Font"|"Form"|"Frame"|"Image"|"Import"|"Manifest"|"Ping"|"PluginData"|"PluginResource"|"Prefetch"|"Resource"|"Script"|"ServiceWorker"|"SharedWorker"|"SpeculationRules"|"Stylesheet"|"Track"|"Video"|"Worker"|"XMLHttpRequest"|"XSLT"; + export type MixedContentResourceType = "AttributionSrc"|"Audio"|"Beacon"|"CSPReport"|"Download"|"EventSource"|"Favicon"|"Font"|"Form"|"Frame"|"Image"|"Import"|"JSON"|"Manifest"|"Ping"|"PluginData"|"PluginResource"|"Prefetch"|"Resource"|"Script"|"ServiceWorker"|"SharedWorker"|"SpeculationRules"|"Stylesheet"|"Track"|"Video"|"Worker"|"XMLHttpRequest"|"XSLT"; export interface MixedContentIssueDetails { /** * The type of resource causing the mixed content issue (css, js, iframe, @@ -1132,7 +1171,7 @@ Munich 81456 */ export interface AddressUI { /** - * A two dimension array containing the repesentation of values from an address profile. + * A two dimension array containing the representation of values from an address profile. */ addressFields: AddressFields[]; } @@ -1165,6 +1204,14 @@ Munich 81456 * The filling strategy */ fillingStrategy: FillingStrategy; + /** + * The frame the field belongs to + */ + frameId: Page.FrameId; + /** + * The form field's DOM node + */ + fieldId: DOM.BackendNodeId; } /** @@ -1363,7 +1410,7 @@ events afterwards if enabled and recording. */ windowState?: WindowState; } - export type PermissionType = "accessibilityEvents"|"audioCapture"|"backgroundSync"|"backgroundFetch"|"clipboardReadWrite"|"clipboardSanitizedWrite"|"displayCapture"|"durableStorage"|"flash"|"geolocation"|"idleDetection"|"localFonts"|"midi"|"midiSysex"|"nfc"|"notifications"|"paymentHandler"|"periodicBackgroundSync"|"protectedMediaIdentifier"|"sensors"|"storageAccess"|"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"|"windowManagement"; export type PermissionSetting = "granted"|"denied"|"prompt"; /** * Definition of PermissionDescriptor defined in the Permissions API: @@ -1536,7 +1583,7 @@ Note that userVisibleOnly = true is the only currently supported type. /** * Whether to allow all or deny all download requests, or use default Chrome behavior if available (otherwise deny). |allowAndName| allows download and names files according to -their dowmload guids. +their download guids. */ behavior: "deny"|"allow"|"allowAndName"|"default"; /** @@ -1884,7 +1931,7 @@ pseudo-classes. frameId: Page.FrameId; /** * Stylesheet resource URL. Empty if this is a constructed stylesheet created using -new CSSStyleSheet() (but non-empty if this is a constructed sylesheet imported +new CSSStyleSheet() (but non-empty if this is a constructed stylesheet imported as a CSS module script). */ sourceURL: string; @@ -2609,6 +2656,12 @@ position specified by `location`. * Text position of a new rule in the target style sheet. */ location: SourceRange; + /** + * NodeId for the DOM node in whose context custom property declarations for registered properties should be +validated. If omitted, declarations in the new rule text can only be validated statically, which may produce +incorrect results if the declaration contains a var() for example. + */ + nodeForPropertySyntaxValidation?: DOM.NodeId; } export type addRuleReturnValue = { /** @@ -2983,6 +3036,12 @@ property */ export type setStyleTextsParameters = { edits: StyleDeclarationEdit[]; + /** + * NodeId for the DOM node in whose context custom property declarations for registered properties should be +validated. If omitted, declarations in the new rule text can only be validated statically, which may produce +incorrect results if the declaration contains a var() for example. + */ + nodeForPropertySyntaxValidation?: DOM.NodeId; } export type setStyleTextsReturnValue = { /** @@ -3362,6 +3421,10 @@ front-end. * ContainerSelector logical axes */ export type LogicalAxes = "Inline"|"Block"|"Both"; + /** + * Physical scroll orientation + */ + export type ScrollOrientation = "horizontal"|"vertical"; /** * DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type. @@ -3935,7 +3998,7 @@ be called for that search. */ export type getAttributesParameters = { /** - * Id of the node to retrieve attibutes for. + * Id of the node to retrieve attributes for. */ nodeId: NodeId; } @@ -5663,14 +5726,14 @@ resource fetches. */ export type VirtualTimePolicy = "advance"|"pause"|"pauseIfNetworkFetchesPending"; /** - * Used to specify User Agent Cient Hints to emulate. See https://wicg.github.io/ua-client-hints + * Used to specify User Agent Client Hints to emulate. See https://wicg.github.io/ua-client-hints */ export interface UserAgentBrandVersion { brand: string; version: string; } /** - * Used to specify User Agent Cient Hints to emulate. See https://wicg.github.io/ua-client-hints + * Used to specify User Agent Client Hints to emulate. See https://wicg.github.io/ua-client-hints Missing optional values will be filled in by the target with what it would normally use. */ export interface UserAgentMetadata { @@ -5974,7 +6037,7 @@ Sensor.start() will attempt to use a real sensor instead. export type setSensorOverrideEnabledReturnValue = { } /** - * Updates the sensor readings reported by a sensor type previously overriden + * Updates the sensor readings reported by a sensor type previously overridden by setSensorOverrideEnabled. */ export type setSensorOverrideReadingsParameters = { @@ -6097,8 +6160,9 @@ restores default host system locale. */ export type setTimezoneOverrideParameters = { /** - * The timezone identifier. If empty, disables the override and -restores default host system timezone. + * The timezone identifier. List of supported timezones: +https://source.chromium.org/chromium/chromium/deps/icu.git/+/faee8bc70570192d82d2978a71e2a615788597d1:source/data/misc/metaZones.txt +If empty, disables the override and restores default host system timezone. */ timezoneId: string; } @@ -6139,6 +6203,7 @@ on Android. } /** * Allows overriding user agent with the given string. +`userAgentMetadata` must be set for Client Hint headers to be sent. */ export type setUserAgentOverrideParameters = { /** @@ -6284,7 +6349,7 @@ display. Reported for diagnostic uses, may be removed in the future. */ handle: StreamHandle; /** - * Seek to the specified offset before reading (if not specificed, proceed with offset + * Seek to the specified offset before reading (if not specified, proceed with offset following the last read). Some types of streams may only support sequential reads. */ offset?: number; @@ -6910,7 +6975,7 @@ for example an emoji keyboard or an IME. export type insertTextReturnValue = { } /** - * This method sets the current candidate text for ime. + * This method sets the current candidate text for IME. Use imeCommitComposition to commit the final text. Use imeSetComposition with empty string as text to cancel composition. */ @@ -7409,7 +7474,7 @@ transform/scrolling purposes only. } export type layerTreeDidChangePayload = { /** - * Layer tree, absent if not in the comspositing mode. + * Layer tree, absent if not in the compositing mode. */ layers?: Layer[]; } @@ -8020,7 +8085,7 @@ passed by the developer (e.g. via "fetch") as understood by the backend. trustTokenParams?: TrustTokenParams; /** * True if this resource request is considered to be the 'same site' as the -request correspondinfg to the main frame. +request corresponding to the main frame. */ isSameSite?: boolean; } @@ -8172,8 +8237,13 @@ records. * The reason why Chrome uses a specific transport protocol for HTTP semantics. */ export type AlternateProtocolUsage = "alternativeJobWonWithoutRace"|"alternativeJobWonRace"|"mainJobWonRace"|"mappingMissing"|"broken"|"dnsAlpnH3JobWonWithoutRace"|"dnsAlpnH3JobWonRace"|"unspecifiedReason"; + /** + * Source of service worker router. + */ + export type ServiceWorkerRouterSource = "network"|"cache"|"fetch-event"|"race-network-and-fetch-handler"; export interface ServiceWorkerRouterInfo { ruleIdMatched: number; + matchedSourceType: ServiceWorkerRouterSource; } /** * HTTP response data. @@ -8203,6 +8273,10 @@ records. * Resource mimeType as determined by the browser. */ mimeType: string; + /** + * Resource charset as determined by the browser (if applicable). + */ + charset: string; /** * Refined HTTP request headers that were actually transmitted over the network. */ @@ -8240,7 +8314,7 @@ records. */ fromPrefetchCache?: boolean; /** - * Infomation about how Service Worker Static Router was used. + * Information about how Service Worker Static Router was used. */ serviceWorkerRouterInfo?: ServiceWorkerRouterInfo; /** @@ -8469,6 +8543,10 @@ of the request to the endpoint that set the cookie. * Types of reasons why a cookie may not be sent with a request. */ export type CookieBlockedReason = "SecureOnly"|"NotOnPath"|"DomainMismatch"|"SameSiteStrict"|"SameSiteLax"|"SameSiteUnspecifiedTreatedAsLax"|"SameSiteNoneInsecure"|"UserPreferences"|"ThirdPartyPhaseout"|"ThirdPartyBlockedInFirstPartySet"|"UnknownError"|"SchemefulSameSiteStrict"|"SchemefulSameSiteLax"|"SchemefulSameSiteUnspecifiedTreatedAsLax"|"SamePartyFromCrossPartyContext"|"NameValuePairExceedsMaxSize"; + /** + * 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"; /** * A cookie which was not stored from a response with the corresponding reason. */ @@ -8490,17 +8568,37 @@ errors. cookie?: Cookie; } /** - * A cookie with was not sent with a request with the corresponding reason. + * A cookie should have been blocked by 3PCD but is exempted and stored from a response with the +corresponding reason. A cookie could only have at most one exemption reason. */ - export interface BlockedCookieWithReason { + export interface ExemptedSetCookieWithReason { /** - * The reason(s) the cookie was blocked. + * The reason the cookie was exempted. */ - blockedReasons: CookieBlockedReason[]; + exemptionReason: CookieExemptionReason; + /** + * The cookie object representing the cookie. + */ + cookie: Cookie; + } + /** + * A cookie associated with the request which may or may not be sent with it. +Includes the cookies itself and reasons for blocking or exemption. + */ + export interface AssociatedCookie { /** * The cookie object representing the cookie which was not sent. */ cookie: Cookie; + /** + * The reason(s) the cookie was blocked. If empty means the cookie is included. + */ + blockedReasons: CookieBlockedReason[]; + /** + * The reason the cookie should have been blocked by 3PCD but is exempted. A cookie could +only have at most one exemption reason. + */ + exemptionReason?: CookieExemptionReason; } /** * Cookie parameter object @@ -8739,7 +8837,7 @@ https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges- */ securityDetails?: SecurityDetails; /** - * Errors occurred while handling the signed exchagne. + * Errors occurred while handling the signed exchange. */ errors?: SignedExchangeError[]; } @@ -8883,6 +8981,10 @@ CORB and streaming. * Actual bytes received (might be less than dataLength for compressed encodings). */ encodedDataLength: number; + /** + * Data that was received. + */ + data?: binary; } /** * Fired when EventSource message is received. @@ -8926,7 +9028,7 @@ CORB and streaming. */ type: ResourceType; /** - * User friendly error message. + * Error message. List of network errors: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ errorText: string; /** @@ -9326,9 +9428,9 @@ or requestWillBeSentExtraInfo will be fired first for the same request. requestId: RequestId; /** * A list of cookies potentially associated to the requested URL. This includes both cookies sent with -the request and the ones not sent; the latter are distinguished by having blockedReason field set. +the request and the ones not sent; the latter are distinguished by having blockedReasons field set. */ - associatedCookies: BlockedCookieWithReason[]; + associatedCookies: AssociatedCookie[]; /** * Raw request headers as they will be sent over the wire. */ @@ -9388,9 +9490,14 @@ Only sent when partitioned cookies are enabled. */ cookiePartitionKey?: string; /** - * True if partitioned cookies are enabled, but the partition key is not serializeable to string. + * True if partitioned cookies are enabled, but the partition key is not serializable to string. */ cookiePartitionKeyOpaque?: boolean; + /** + * A list of cookies which should have been blocked by 3PCD but are exempted and stored from +the response with the corresponding reason. + */ + exemptedCookies?: ExemptedSetCookieWithReason[]; } /** * Fired exactly once for each Trust Token operation. Depending on @@ -9621,7 +9728,7 @@ authChallenge. export type continueInterceptedRequestReturnValue = { } /** - * Deletes browser cookies with matching name and url or domain/path pair. + * Deletes browser cookies with matching name and url or domain/path/partitionKey pair. */ export type deleteCookiesParameters = { /** @@ -9641,6 +9748,11 @@ provided URL. * If specified, deletes only cookies with the exact path. */ path?: string; + /** + * If specified, deletes only cookies with the the given name and partitionKey where domain +matches provided URL. + */ + partitionKey?: string; } export type deleteCookiesReturnValue = { } @@ -10019,6 +10131,22 @@ continueInterceptedRequest call. } export type setUserAgentOverrideReturnValue = { } + /** + * Enables streaming of the response for the given requestId. +If enabled, the dataReceived event contains the data that was received during streaming. + */ + export type streamResourceContentParameters = { + /** + * Identifier of the request to stream. + */ + requestId: RequestId; + } + export type streamResourceContentReturnValue = { + /** + * Data that has been buffered until streaming is enabled. + */ + bufferedData: binary; + } /** * Returns information about the COEP/COOP isolation status. */ @@ -10075,7 +10203,7 @@ should be omitted for worker targets. */ export interface SourceOrderConfig { /** - * the color to outline the givent element in. + * the color to outline the given element in. */ parentOutlineColor: DOM.RGBA; /** @@ -10408,7 +10536,7 @@ should be omitted for worker targets. */ showCSS: boolean; /** - * Seleted platforms to show the overlay. + * Selected platforms to show the overlay. */ selectedPlatform: string; /** @@ -10576,8 +10704,8 @@ user manually inspects an element. } /** * Highlights owner element of the frame with given id. -Deprecated: Doesn't work reliablity and cannot be fixed due to process -separatation (the owner node might be in a different process). Determine +Deprecated: Doesn't work reliably and cannot be fixed due to process +separation (the owner node might be in a different process). Determine the owner node in the client and use highlightNode. */ export type highlightFrameParameters = { @@ -10937,7 +11065,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"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"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-factor"|"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"|"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"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"storage-access"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"window-placement"|"xr-spatial-tracking"; + export type PermissionsPolicyFeature = "accelerometer"|"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-factor"|"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"|"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"|"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"|"window-placement"|"xr-spatial-tracking"; /** * Reason for a permissions policy feature to be disabled. */ @@ -11189,7 +11317,7 @@ Example URLs: http://www.google.com/file.html -> "google.com" */ message: string; /** - * If criticial, this is a non-recoverable parse error. + * If critical, this is a non-recoverable parse error. */ critical: number; /** @@ -11396,7 +11524,7 @@ Example URLs: http://www.google.com/file.html -> "google.com" eager?: boolean; } /** - * Enum of possible auto-reponse for permisison / prompt dialogs. + * Enum of possible auto-response for permission / prompt dialogs. */ export type AutoResponseMode = "none"|"autoAccept"|"autoReject"|"autoOptOut"; /** @@ -11406,7 +11534,7 @@ Example URLs: http://www.google.com/file.html -> "google.com" /** * 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"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"DedicatedWorkerOrWorklet"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"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"; + 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"|"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"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"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"; /** * Types of not restored reasons for back-forward cache. */ @@ -11683,7 +11811,7 @@ open. */ type: DialogType; /** - * True iff browser is capable showing or acting on the given dialog. When browser has no + * True if browser is capable showing or acting on the given dialog. When browser has no dialog handler for given target, calling alert while Page domain is engaged will stall the page execution. Execution can be resumed via calling Page.handleJavaScriptDialog. */ @@ -11716,7 +11844,7 @@ when bfcache navigation fails. */ export type backForwardCacheNotUsedPayload = { /** - * The loader id for the associated navgation. + * The loader id for the associated navigation. */ loaderId: Network.LoaderId; /** @@ -12664,7 +12792,7 @@ https://github.com/WICG/web-lifecycle/ } /** * Requests backend to produce compilation cache for the specified scripts. -`scripts` are appeneded to the list of scripts for which the cache +`scripts` are appended to the list of scripts for which the cache would be produced. The list may be reset during page navigation. When script with a matching URL is encountered, the cache is optionally produced upon backend discretion, based on internal heuristics. @@ -12883,7 +13011,7 @@ https://w3c.github.io/performance-timeline/#dom-performanceobserver. frameId: Page.FrameId; /** * The event type, as specified in https://w3c.github.io/performance-timeline/#dom-performanceentry-entrytype -This determines which of the optional "details" fiedls is present. +This determines which of the optional "details" fields is present. */ type: string; /** @@ -12993,7 +13121,7 @@ https://www.w3.org/TR/mixed-content/#categories */ certificateNetworkError?: string; /** - * True if the certificate uses a weak signature aglorithm. + * True if the certificate uses a weak signature algorithm. */ certificateHasWeakSignature: boolean; /** @@ -13389,10 +13517,22 @@ Tokens from that issuer. issuerOrigin: string; count: number; } + /** + * Protected audience interest group auction identifier. + */ + export type InterestGroupAuctionId = string; /** * Enum of interest group access types. */ - export type InterestGroupAccessType = "join"|"leave"|"update"|"loaded"|"bid"|"win"|"additionalBid"|"additionalBidWin"|"clear"; + export type InterestGroupAccessType = "join"|"leave"|"update"|"loaded"|"bid"|"win"|"additionalBid"|"additionalBidWin"|"topLevelBid"|"topLevelAdditionalBid"|"clear"; + /** + * Enum of auction events. + */ + export type InterestGroupAuctionEventType = "started"|"configResolved"; + /** + * Enum of network fetches auctions can do. + */ + export type InterestGroupAuctionFetchType = "bidderJs"|"bidderWasm"|"sellerJs"|"bidderTrustedSignals"|"sellerTrustedSignals"; /** * Ad advertising element inside an interest group. */ @@ -13432,9 +13572,23 @@ Tokens from that issuer. * Details for an origin's shared storage. */ export interface SharedStorageMetadata { + /** + * Time when the origin's shared storage was last created. + */ creationTime: Network.TimeSinceEpoch; + /** + * Number of key-value pairs stored in origin's shared storage. + */ length: number; + /** + * Current amount of bits of entropy remaining in the navigation budget. + */ remainingBudget: number; + /** + * Total number of bytes stored as key-value pairs in origin's shared +storage. + */ + bytesUsed: number; } /** * Pair of reporting metadata details for a candidate URL for `selectURL()`. @@ -13536,6 +13690,17 @@ SharedStorageAccessType.workletSet. key: string; values: string[]; } + export interface AttributionReportingFilterConfig { + filterValues: AttributionReportingFilterDataEntry[]; + /** + * duration in seconds + */ + lookbackWindow?: number; + } + export interface AttributionReportingFilterPair { + filters: AttributionReportingFilterConfig[]; + notFilters: AttributionReportingFilterConfig[]; + } export interface AttributionReportingAggregationKeysEntry { key: string; value: UnsignedInt128AsBase16; @@ -13582,6 +13747,48 @@ int triggerDataMatching: AttributionReportingTriggerDataMatching; } export type AttributionReportingSourceRegistrationResult = "success"|"internalError"|"insufficientSourceCapacity"|"insufficientUniqueDestinationCapacity"|"excessiveReportingOrigins"|"prohibitedByBrowserPolicy"|"successNoised"|"destinationReportingLimitReached"|"destinationGlobalLimitReached"|"destinationBothLimitsReached"|"reportingOriginsPerSiteLimitReached"|"exceedsMaxChannelCapacity"; + export type AttributionReportingSourceRegistrationTimeConfig = "include"|"exclude"; + export interface AttributionReportingAggregatableValueDictEntry { + key: string; + /** + * number instead of integer because not all uint32 can be represented by +int + */ + value: number; + } + export interface AttributionReportingAggregatableValueEntry { + values: AttributionReportingAggregatableValueDictEntry[]; + filters: AttributionReportingFilterPair; + } + export interface AttributionReportingEventTriggerData { + data: UnsignedInt64AsBase10; + priority: SignedInt64AsBase10; + dedupKey?: UnsignedInt64AsBase10; + filters: AttributionReportingFilterPair; + } + export interface AttributionReportingAggregatableTriggerData { + keyPiece: UnsignedInt128AsBase16; + sourceKeys: string[]; + filters: AttributionReportingFilterPair; + } + export interface AttributionReportingAggregatableDedupKey { + dedupKey?: UnsignedInt64AsBase10; + filters: AttributionReportingFilterPair; + } + export interface AttributionReportingTriggerRegistration { + filters: AttributionReportingFilterPair; + debugKey?: UnsignedInt64AsBase10; + aggregatableDedupKeys: AttributionReportingAggregatableDedupKey[]; + eventTriggerData: AttributionReportingEventTriggerData[]; + aggregatableTriggerData: AttributionReportingAggregatableTriggerData[]; + aggregatableValues: AttributionReportingAggregatableValueEntry[]; + debugReporting: boolean; + aggregationCoordinatorOrigin?: string; + sourceRegistrationTimeConfig: AttributionReportingSourceRegistrationTimeConfig; + triggerContextId?: 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"; /** * A cache's contents have been modified. @@ -13664,13 +13871,61 @@ int bucketId: string; } /** - * One of the interest groups was accessed by the associated page. + * One of the interest groups was accessed. Note that these events are global +to all targets sharing an interest group store. */ export type interestGroupAccessedPayload = { accessTime: Network.TimeSinceEpoch; type: InterestGroupAccessType; ownerOrigin: string; name: string; + /** + * For topLevelBid/topLevelAdditionalBid, and when appropriate, +win and additionalBidWin + */ + componentSellerOrigin?: string; + /** + * For bid or somethingBid event, if done locally and not on a server. + */ + bid?: number; + bidCurrency?: string; + /** + * For non-global events --- links to interestGroupAuctionEvent + */ + uniqueAuctionId?: InterestGroupAuctionId; + } + /** + * An auction involving interest groups is taking place. These events are +target-specific. + */ + export type interestGroupAuctionEventOccurredPayload = { + eventTime: Network.TimeSinceEpoch; + type: InterestGroupAuctionEventType; + uniqueAuctionId: InterestGroupAuctionId; + /** + * Set for child auctions. + */ + parentAuctionId?: InterestGroupAuctionId; + /** + * Set for started and configResolved + */ + auctionConfig?: { [key: string]: string }; + } + /** + * Specifies which auctions a particular network fetch may be related to, and +in what role. Note that it is not ordered with respect to +Network.requestWillBeSent (but will happen before loadingFinished +loadingFailed). + */ + export type interestGroupAuctionNetworkRequestCreatedPayload = { + type: InterestGroupAuctionFetchType; + requestId: Network.RequestId; + /** + * This is the set of the auctions using the worklet that issued this +request. In the case of trusted signals, it's possible that only some of +them actually care about the keys being queried. + */ + auctions: InterestGroupAuctionId[]; } /** * Shared storage was accessed by the associated page. @@ -13694,7 +13949,7 @@ The following parameters are included in all events. */ ownerOrigin: string; /** - * The sub-parameters warapped by `params` are all optional and their + * The sub-parameters wrapped by `params` are all optional and their presence/absence depends on `type`. */ params: SharedStorageAccessParams; @@ -13705,14 +13960,15 @@ presence/absence depends on `type`. export type storageBucketDeletedPayload = { bucketId: string; } - /** - * TODO(crbug.com/1458532): Add other Attribution Reporting events, e.g. -trigger registration. - */ export type attributionReportingSourceRegisteredPayload = { registration: AttributionReportingSourceRegistration; result: AttributionReportingSourceRegistrationResult; } + export type attributionReportingTriggerRegisteredPayload = { + registration: AttributionReportingTriggerRegistration; + eventLevel: AttributionReportingEventLevelResult; + aggregatable: AttributionReportingAggregatableResult; + } /** * Returns a storage key given a frame id. @@ -13970,6 +14226,15 @@ Leaves other stored data, including the issuer's Redemption Records, intact. } export type setInterestGroupTrackingReturnValue = { } + /** + * Enables/Disables issuing of interestGroupAuctionEventOccurred and +interestGroupAuctionNetworkRequestCreated. + */ + export type setInterestGroupAuctionTrackingParameters = { + enable: boolean; + } + export type setInterestGroupAuctionTrackingReturnValue = { + } /** * Gets metadata for an origin's shared storage. */ @@ -14318,6 +14583,9 @@ supported. export interface TargetInfo { targetId: TargetID; type: string; + /** + * List of types: https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/devtools_agent_host_impl.cc?ss=chromium&q=f:devtools%20-f:out%20%22::kTypeTab%5B%5D%22 + */ title: string; url: string; /** @@ -14348,7 +14616,7 @@ the type of "page", this may be set to "portal" or "prerender". */ export interface FilterEntry { /** - * If set, causes exclusion of mathcing targets from the list. + * If set, causes exclusion of matching targets from the list. */ exclude?: boolean; /** @@ -14499,7 +14767,7 @@ channel with browser target. Injected object will be available as `window[bindingName]`. -The object has the follwing API: +The object has the following API: - `binding.send(json)` - a method to send messages over the remote debugging protocol - `binding.onmessage = json => handleMessage(json)` - a callback that will be called for the protocol notifications and command responses. */ @@ -15712,6 +15980,18 @@ See https://w3c.github.io/webauthn/#signature-counter See https://w3c.github.io/webauthn/#sctn-large-blob-extension */ largeBlob?: binary; + /** + * Assertions returned by this credential will have the backup eligibility +(BE) flag set to this value. Defaults to the authenticator's +defaultBackupEligibility value. + */ + backupEligibility?: boolean; + /** + * Assertions returned by this credential will have the backup state (BS) +flag set to this value. Defaults to the authenticator's +defaultBackupState value. + */ + backupState?: boolean; } /** @@ -15858,6 +16138,18 @@ The default is true. } export type setAutomaticPresenceSimulationReturnValue = { } + /** + * Allows setting credential properties. +https://w3c.github.io/webauthn/#sctn-automation-set-credential-properties + */ + export type setCredentialPropertiesParameters = { + authenticatorId: AuthenticatorId; + credentialId: binary; + backupEligibility?: boolean; + backupState?: boolean; + } + export type setCredentialPropertiesReturnValue = { + } } /** @@ -16093,7 +16385,7 @@ See also: requestId?: Network.RequestId; /** * Error information -`errorMessage` is null iff `errorType` is null. +`errorMessage` is null if `errorType` is null. */ errorType?: RuleSetErrorType; /** @@ -16132,7 +16424,7 @@ still keyed with the initial URL. that had a speculation rule that triggered the attempt, and the BackendNodeIds of <a href> or <area href> elements that triggered the attempt (in the case of attempts triggered by a document rule). It is -possible for mulitple rule sets and links to trigger a single attempt. +possible for multiple rule sets and links to trigger a single attempt. */ export interface PreloadingAttemptSource { key: PreloadingAttemptKey; @@ -16152,7 +16444,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"|"PrefetchEvicted"|"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"|"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"; /** * Information of headers to be displayed when the header mismatch occurred. */ @@ -16244,6 +16536,10 @@ whether this account has ever been used to sign in to this RP before. * The buttons on the FedCM dialog. */ export type DialogButton = "ConfirmIdpLoginContinue"|"ErrorGotIt"|"ErrorMoreDetails"; + /** + * The URLs that each account has + */ + export type AccountUrlType = "TermsOfService"|"PrivacyPolicy"; /** * Corresponds to IdentityRequestAccount */ @@ -16308,6 +16604,13 @@ normally happen, if this is unimportant to what's being tested. } export type clickDialogButtonReturnValue = { } + export type openUrlParameters = { + dialogId: string; + accountIndex: number; + accountUrlType: AccountUrlType; + } + export type openUrlReturnValue = { + } export type dismissDialogParameters = { dialogId: string; triggerCooldown?: boolean; @@ -19076,10 +19379,13 @@ Error was thrown. "Storage.indexedDBContentUpdated": Storage.indexedDBContentUpdatedPayload; "Storage.indexedDBListUpdated": Storage.indexedDBListUpdatedPayload; "Storage.interestGroupAccessed": Storage.interestGroupAccessedPayload; + "Storage.interestGroupAuctionEventOccurred": Storage.interestGroupAuctionEventOccurredPayload; + "Storage.interestGroupAuctionNetworkRequestCreated": Storage.interestGroupAuctionNetworkRequestCreatedPayload; "Storage.sharedStorageAccessed": Storage.sharedStorageAccessedPayload; "Storage.storageBucketCreatedOrUpdated": Storage.storageBucketCreatedOrUpdatedPayload; "Storage.storageBucketDeleted": Storage.storageBucketDeletedPayload; "Storage.attributionReportingSourceRegistered": Storage.attributionReportingSourceRegisteredPayload; + "Storage.attributionReportingTriggerRegistered": Storage.attributionReportingTriggerRegisteredPayload; "Target.attachedToTarget": Target.attachedToTargetPayload; "Target.detachedFromTarget": Target.detachedFromTargetPayload; "Target.receivedMessageFromTarget": Target.receivedMessageFromTargetPayload; @@ -19430,6 +19736,7 @@ Error was thrown. "Network.setAttachDebugStack": Network.setAttachDebugStackParameters; "Network.setRequestInterception": Network.setRequestInterceptionParameters; "Network.setUserAgentOverride": Network.setUserAgentOverrideParameters; + "Network.streamResourceContent": Network.streamResourceContentParameters; "Network.getSecurityIsolationStatus": Network.getSecurityIsolationStatusParameters; "Network.enableReportingApi": Network.enableReportingApiParameters; "Network.loadNetworkResource": Network.loadNetworkResourceParameters; @@ -19565,6 +19872,7 @@ Error was thrown. "Storage.clearTrustTokens": Storage.clearTrustTokensParameters; "Storage.getInterestGroupDetails": Storage.getInterestGroupDetailsParameters; "Storage.setInterestGroupTracking": Storage.setInterestGroupTrackingParameters; + "Storage.setInterestGroupAuctionTracking": Storage.setInterestGroupAuctionTrackingParameters; "Storage.getSharedStorageMetadata": Storage.getSharedStorageMetadataParameters; "Storage.getSharedStorageEntries": Storage.getSharedStorageEntriesParameters; "Storage.setSharedStorageEntry": Storage.setSharedStorageEntryParameters; @@ -19628,6 +19936,7 @@ Error was thrown. "WebAuthn.clearCredentials": WebAuthn.clearCredentialsParameters; "WebAuthn.setUserVerified": WebAuthn.setUserVerifiedParameters; "WebAuthn.setAutomaticPresenceSimulation": WebAuthn.setAutomaticPresenceSimulationParameters; + "WebAuthn.setCredentialProperties": WebAuthn.setCredentialPropertiesParameters; "Media.enable": Media.enableParameters; "Media.disable": Media.disableParameters; "DeviceAccess.enable": DeviceAccess.enableParameters; @@ -19640,6 +19949,7 @@ Error was thrown. "FedCm.disable": FedCm.disableParameters; "FedCm.selectAccount": FedCm.selectAccountParameters; "FedCm.clickDialogButton": FedCm.clickDialogButtonParameters; + "FedCm.openUrl": FedCm.openUrlParameters; "FedCm.dismissDialog": FedCm.dismissDialogParameters; "FedCm.resetCooldown": FedCm.resetCooldownParameters; "Console.clearMessages": Console.clearMessagesParameters; @@ -20008,6 +20318,7 @@ Error was thrown. "Network.setAttachDebugStack": Network.setAttachDebugStackReturnValue; "Network.setRequestInterception": Network.setRequestInterceptionReturnValue; "Network.setUserAgentOverride": Network.setUserAgentOverrideReturnValue; + "Network.streamResourceContent": Network.streamResourceContentReturnValue; "Network.getSecurityIsolationStatus": Network.getSecurityIsolationStatusReturnValue; "Network.enableReportingApi": Network.enableReportingApiReturnValue; "Network.loadNetworkResource": Network.loadNetworkResourceReturnValue; @@ -20143,6 +20454,7 @@ Error was thrown. "Storage.clearTrustTokens": Storage.clearTrustTokensReturnValue; "Storage.getInterestGroupDetails": Storage.getInterestGroupDetailsReturnValue; "Storage.setInterestGroupTracking": Storage.setInterestGroupTrackingReturnValue; + "Storage.setInterestGroupAuctionTracking": Storage.setInterestGroupAuctionTrackingReturnValue; "Storage.getSharedStorageMetadata": Storage.getSharedStorageMetadataReturnValue; "Storage.getSharedStorageEntries": Storage.getSharedStorageEntriesReturnValue; "Storage.setSharedStorageEntry": Storage.setSharedStorageEntryReturnValue; @@ -20206,6 +20518,7 @@ Error was thrown. "WebAuthn.clearCredentials": WebAuthn.clearCredentialsReturnValue; "WebAuthn.setUserVerified": WebAuthn.setUserVerifiedReturnValue; "WebAuthn.setAutomaticPresenceSimulation": WebAuthn.setAutomaticPresenceSimulationReturnValue; + "WebAuthn.setCredentialProperties": WebAuthn.setCredentialPropertiesReturnValue; "Media.enable": Media.enableReturnValue; "Media.disable": Media.disableReturnValue; "DeviceAccess.enable": DeviceAccess.enableReturnValue; @@ -20218,6 +20531,7 @@ Error was thrown. "FedCm.disable": FedCm.disableReturnValue; "FedCm.selectAccount": FedCm.selectAccountReturnValue; "FedCm.clickDialogButton": FedCm.clickDialogButtonReturnValue; + "FedCm.openUrl": FedCm.openUrlReturnValue; "FedCm.dismissDialog": FedCm.dismissDialogReturnValue; "FedCm.resetCooldown": FedCm.resetCooldownReturnValue; "Console.clearMessages": Console.clearMessagesReturnValue; diff --git a/playwright/packages/playwright-core/src/server/console.ts b/playwright/packages/playwright-core/src/server/console.ts index 5b0de45a0f..e1faa7fb8f 100644 --- a/playwright/packages/playwright-core/src/server/console.ts +++ b/playwright/packages/playwright-core/src/server/console.ts @@ -14,20 +14,18 @@ * limitations under the License. */ -import { SdkObject } from './instrumentation'; import type * as js from './javascript'; import type { ConsoleMessageLocation } from './types'; import type { Page } from './page'; -export class ConsoleMessage extends SdkObject { +export class ConsoleMessage { private _type: string; private _text?: string; private _args: js.JSHandle[]; private _location: ConsoleMessageLocation; - private _page: Page; + private _page: Page | null; - constructor(page: Page, type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) { - super(page, 'console-message'); + constructor(page: Page | null, type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) { this._page = page; this._type = type; this._text = text; diff --git a/playwright/packages/playwright-core/src/server/debugController.ts b/playwright/packages/playwright-core/src/server/debugController.ts index f084ed1ece..ea2dc9a805 100644 --- a/playwright/packages/playwright-core/src/server/debugController.ts +++ b/playwright/packages/playwright-core/src/server/debugController.ts @@ -30,7 +30,6 @@ const internalMetadata = serverSideCallMetadata(); export class DebugController extends SdkObject { static Events = { - BrowsersChanged: 'browsersChanged', StateChanged: 'stateChanged', InspectRequested: 'inspectRequested', SourceChanged: 'sourceChanged', @@ -194,8 +193,6 @@ export class DebugController extends SdkObject { pageCount += context.pages().length; } } - // TODO: browsers is deprecated, remove it. - this.emit(DebugController.Events.BrowsersChanged, browsers); this.emit(DebugController.Events.StateChanged, { pageCount }); } diff --git a/playwright/packages/playwright-core/src/server/deviceDescriptorsSource.json b/playwright/packages/playwright-core/src/server/deviceDescriptorsSource.json index a86377e616..6191ef5d2e 100644 --- a/playwright/packages/playwright-core/src/server/deviceDescriptorsSource.json +++ b/playwright/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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 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/121.0.6167.57 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 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/121.0.6167.57 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36", "viewport": { "width": 1138, "height": 712 @@ -978,7 +978,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 384, "height": 640 @@ -989,7 +989,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 640, "height": 384 @@ -1000,7 +1000,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 640, "height": 360 @@ -1011,7 +1011,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 360, "height": 640 @@ -1022,7 +1022,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 360, "height": 640 @@ -1033,7 +1033,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 640, "height": 360 @@ -1044,7 +1044,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/121.0.6167.57 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36", "viewport": { "width": 800, "height": 1280 @@ -1055,7 +1055,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/121.0.6167.57 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36", "viewport": { "width": 1280, "height": 800 @@ -1066,7 +1066,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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 384, "height": 640 @@ -1077,7 +1077,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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 640, "height": 384 @@ -1088,7 +1088,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 360, "height": 640 @@ -1099,7 +1099,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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 640, "height": 360 @@ -1110,7 +1110,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1121,7 +1121,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1132,7 +1132,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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1143,7 +1143,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/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1154,7 +1154,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1165,7 +1165,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1176,7 +1176,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/121.0.6167.57 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36", "viewport": { "width": 600, "height": 960 @@ -1187,7 +1187,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/121.0.6167.57 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36", "viewport": { "width": 960, "height": 600 @@ -1242,7 +1242,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 411, "height": 731 @@ -1253,7 +1253,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 731, "height": 411 @@ -1264,7 +1264,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 411, "height": 823 @@ -1275,7 +1275,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 823, "height": 411 @@ -1286,7 +1286,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 393, "height": 786 @@ -1297,7 +1297,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/121.0.6167.57 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/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 786, "height": 393 @@ -1308,7 +1308,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4": { - "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 353, "height": 745 @@ -1319,7 +1319,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 745, "height": 353 @@ -1330,7 +1330,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4a (5G)": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "screen": { "width": 412, "height": 892 @@ -1345,7 +1345,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4a (5G) landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "screen": { "height": 892, "width": 412 @@ -1360,7 +1360,7 @@ "defaultBrowserType": "chromium" }, "Pixel 5": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "screen": { "width": 393, "height": 851 @@ -1375,7 +1375,7 @@ "defaultBrowserType": "chromium" }, "Pixel 5 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "screen": { "width": 851, "height": 393 @@ -1390,7 +1390,7 @@ "defaultBrowserType": "chromium" }, "Pixel 7": { - "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "screen": { "width": 412, "height": 915 @@ -1405,7 +1405,7 @@ "defaultBrowserType": "chromium" }, "Pixel 7 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "screen": { "width": 915, "height": 412 @@ -1420,7 +1420,7 @@ "defaultBrowserType": "chromium" }, "Moto G4": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 360, "height": 640 @@ -1431,7 +1431,7 @@ "defaultBrowserType": "chromium" }, "Moto G4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Mobile Safari/537.36", "viewport": { "width": 640, "height": 360 @@ -1442,7 +1442,7 @@ "defaultBrowserType": "chromium" }, "Desktop Chrome HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Safari/537.36", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36", "screen": { "width": 1792, "height": 1120 @@ -1457,7 +1457,7 @@ "defaultBrowserType": "chromium" }, "Desktop Edge HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Safari/537.36 Edg/121.0.6167.57", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36 Edg/123.0.6312.4", "screen": { "width": 1792, "height": 1120 @@ -1472,7 +1472,7 @@ "defaultBrowserType": "chromium" }, "Desktop Firefox HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0", "screen": { "width": 1792, "height": 1120 @@ -1502,7 +1502,7 @@ "defaultBrowserType": "webkit" }, "Desktop Chrome": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Safari/537.36", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36", "screen": { "width": 1920, "height": 1080 @@ -1517,7 +1517,7 @@ "defaultBrowserType": "chromium" }, "Desktop Edge": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.57 Safari/537.36 Edg/121.0.6167.57", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.4 Safari/537.36 Edg/123.0.6312.4", "screen": { "width": 1920, "height": 1080 @@ -1532,7 +1532,7 @@ "defaultBrowserType": "chromium" }, "Desktop Firefox": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0", "screen": { "width": 1920, "height": 1080 diff --git a/playwright/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/playwright/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index e9505de2c0..b4a06a67b5 100644 --- a/playwright/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/playwright/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -90,8 +90,9 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel this._dispatchEvent('pageError', { error: serializeError(error), page: PageDispatcher.from(this, page) }); }); this.addObjectListener(BrowserContext.Events.Console, (message: ConsoleMessage) => { - if (this._shouldDispatchEvent(message.page(), 'console')) { - const pageDispatcher = PageDispatcher.from(this, message.page()); + const page = message.page()!; + if (this._shouldDispatchEvent(page, 'console')) { + const pageDispatcher = PageDispatcher.from(this, page); this._dispatchEvent('console', { page: pageDispatcher, type: message.type(), diff --git a/playwright/packages/playwright-core/src/server/dispatchers/dispatcher.ts b/playwright/packages/playwright-core/src/server/dispatchers/dispatcher.ts index 6fffb9d83b..0b250fe76e 100644 --- a/playwright/packages/playwright-core/src/server/dispatchers/dispatcher.ts +++ b/playwright/packages/playwright-core/src/server/dispatchers/dispatcher.ts @@ -346,10 +346,12 @@ export class DispatcherConnection { } await sdkObject?.instrumentation.onBeforeCall(sdkObject, callMetadata); + const response: any = { id }; try { const result = await dispatcher._handleCommand(callMetadata, method, validParams); const validator = findValidator(dispatcher._type, method, 'Result'); - callMetadata.result = validator(result, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' }); + response.result = validator(result, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' }); + callMetadata.result = result; } catch (e) { if (isTargetClosedError(e) && sdkObject) { const reason = closeReason(sdkObject); @@ -363,19 +365,16 @@ export class DispatcherConnection { rewriteErrorMessage(e, 'Target crashed ' + e.browserLogMessage()); } } - callMetadata.error = serializeError(e); + response.error = serializeError(e); + // The command handler could have set error in the metada, do not reset it if there was no exception. + callMetadata.error = response.error; } finally { callMetadata.endTime = monotonicTime(); await sdkObject?.instrumentation.onAfterCall(sdkObject, callMetadata); } - const response: any = { id }; - if (callMetadata.result) - response.result = callMetadata.result; - if (callMetadata.error) { - response.error = callMetadata.error; + if (response.error) response.log = callMetadata.log; - } this.onmessage(response); } } diff --git a/playwright/packages/playwright-core/src/server/dispatchers/electronDispatcher.ts b/playwright/packages/playwright-core/src/server/dispatchers/electronDispatcher.ts index a636ac8c53..a4e28da26f 100644 --- a/playwright/packages/playwright-core/src/server/dispatchers/electronDispatcher.ts +++ b/playwright/packages/playwright-core/src/server/dispatchers/electronDispatcher.ts @@ -21,6 +21,7 @@ import { ElectronApplication } from '../electron/electron'; import type * as channels from '@protocol/channels'; import { BrowserContextDispatcher } from './browserContextDispatcher'; import type { PageDispatcher } from './pageDispatcher'; +import type { ConsoleMessage } from '../console'; import { parseArgument, serializeResult } from './jsHandleDispatcher'; import { ElementHandleDispatcher } from './elementHandlerDispatcher'; @@ -40,6 +41,7 @@ export class ElectronDispatcher extends Dispatcher<Electron, channels.ElectronCh export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplication, channels.ElectronApplicationChannel, ElectronDispatcher> implements channels.ElectronApplicationChannel { _type_EventTarget = true; _type_ElectronApplication = true; + private readonly _subscriptions = new Set<channels.ElectronApplicationUpdateSubscriptionParams['event']>(); constructor(scope: ElectronDispatcher, electronApplication: ElectronApplication) { super(scope, electronApplication, 'ElectronApplication', { @@ -49,6 +51,16 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio this._dispatchEvent('close'); this._dispose(); }); + this.addObjectListener(ElectronApplication.Events.Console, (message: ConsoleMessage) => { + if (!this._subscriptions.has('console')) + return; + this._dispatchEvent('console', { + type: message.type(), + text: message.text(), + args: message.args().map(a => ElementHandleDispatcher.fromJSHandle(this, a)), + location: message.location() + }); + }); } async browserWindow(params: channels.ElectronApplicationBrowserWindowParams): Promise<channels.ElectronApplicationBrowserWindowResult> { @@ -67,6 +79,13 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio return { handle: ElementHandleDispatcher.fromJSHandle(this, result) }; } + async updateSubscription(params: channels.ElectronApplicationUpdateSubscriptionParams): Promise<void> { + if (params.enabled) + this._subscriptions.add(params.event); + else + this._subscriptions.delete(params.event); + } + async close(): Promise<void> { await this._object.close(); } diff --git a/playwright/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/playwright/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index 70ccf16638..fb2e1f0468 100644 --- a/playwright/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/playwright/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -85,6 +85,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows })); this.addObjectListener(Page.Events.FrameAttached, frame => this._onFrameAttached(frame)); this.addObjectListener(Page.Events.FrameDetached, frame => this._onFrameDetached(frame)); + this.addObjectListener(Page.Events.LocatorHandlerTriggered, (uid: number) => this._dispatchEvent('locatorHandlerTriggered', { uid })); this.addObjectListener(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this, webSocket) })); this.addObjectListener(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this, worker) })); this.addObjectListener(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(parentScope, artifact) })); @@ -136,6 +137,15 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(metadata, params)) }; } + async registerLocatorHandler(params: channels.PageRegisterLocatorHandlerParams, metadata: CallMetadata): Promise<channels.PageRegisterLocatorHandlerResult> { + const uid = this._page.registerLocatorHandler(params.selector); + return { uid }; + } + + async resolveLocatorHandlerNoReply(params: channels.PageResolveLocatorHandlerNoReplyParams, metadata: CallMetadata): Promise<void> { + this._page.resolveLocatorHandler(params.uid); + } + async emulateMedia(params: channels.PageEmulateMediaParams, metadata: CallMetadata): Promise<void> { await this._page.emulateMedia({ media: params.media, @@ -169,7 +179,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows } async expectScreenshot(params: channels.PageExpectScreenshotParams, metadata: CallMetadata): Promise<channels.PageExpectScreenshotResult> { - const mask: { frame: Frame, selector: string }[] = (params.screenshotOptions?.mask || []).map(({ frame, selector }) => ({ + const mask: { frame: Frame, selector: string }[] = (params.mask || []).map(({ frame, selector }) => ({ frame: (frame as FrameDispatcher)._object, selector, })); @@ -180,14 +190,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows return await this._page.expectScreenshot(metadata, { ...params, locator, - comparatorOptions: { - ...params.comparatorOptions, - _comparator: params.comparatorOptions?.comparator, - }, - screenshotOptions: { - ...params.screenshotOptions, - mask, - }, + mask, }); } diff --git a/playwright/packages/playwright-core/src/server/dom.ts b/playwright/packages/playwright-core/src/server/dom.ts index ded96eec5f..ef7b152b88 100644 --- a/playwright/packages/playwright-core/src/server/dom.ts +++ b/playwright/packages/playwright-core/src/server/dom.ts @@ -19,7 +19,7 @@ import * as injectedScriptSource from '../generated/injectedScriptSource'; import { isSessionClosedError } from './protocolError'; import type { ScreenshotOptions } from './screenshotter'; import type * as frames from './frames'; -import type { InjectedScript, InjectedScriptPoll, LogEntry, HitTargetInterceptionResult, ElementState } from './injected/injectedScript'; +import type { InjectedScript, HitTargetInterceptionResult, ElementState } from './injected/injectedScript'; import type { CallMetadata } from './instrumentation'; import * as js from './javascript'; import type { Page } from './page'; @@ -36,6 +36,7 @@ export type InputFilesItems = { }; type ActionName = 'click' | 'hover' | 'dblclick' | 'tap' | 'move and up' | 'move and down'; +type PerformActionResult = 'error:notvisible' | 'error:notconnected' | 'error:notinviewport' | 'error:optionsnotfound' | { missingState: ElementState } | { hitTargetDescription: string } | 'done'; export class NonRecoverableDOMError extends Error { } @@ -156,19 +157,6 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { } } - async evaluatePoll<R, Arg>(progress: Progress, pageFunction: js.Func1<[js.JSHandle<InjectedScript>, ElementHandle<T>, Arg], InjectedScriptPoll<R>>, arg: Arg): Promise<R | 'error:notconnected'> { - try { - const utility = await this._frame._utilityContext(); - const poll = await utility.evaluateHandle(pageFunction, [await utility.injectedScript(), this, arg]); - const pollHandler = new InjectedScriptPollHandler(progress, poll); - return await pollHandler.finish(); - } catch (e) { - if (js.isJavaScriptErrorInEvaluate(e) || isSessionClosedError(e)) - throw e; - return 'error:notconnected'; - } - } - async ownerFrame(): Promise<frames.Frame | null> { const frameId = await this._page._delegate.getOwnerFrame(this); if (!frameId) @@ -224,24 +212,16 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { } async _waitAndScrollIntoViewIfNeeded(progress: Progress, waitForVisible: boolean): Promise<void> { - const timeouts = [0, 50, 100, 250]; - while (progress.isRunning()) { - assertDone(throwRetargetableDOMError(await this._waitForElementStates(progress, waitForVisible ? ['visible', 'stable'] : ['stable'], false /* force */))); - progress.throwIfAborted(); // Avoid action that has side-effects. - const result = throwRetargetableDOMError(await this._scrollRectIntoViewIfNeeded()); - if (result === 'error:notvisible') { - if (!waitForVisible) { - // Wait for a timeout to avoid retrying too often when not waiting for visible. - // If we wait for visible, this should be covered by _waitForElementStates instead. - const timeout = timeouts.shift() ?? 500; - progress.log(` element is not displayed, retrying in ${timeout}ms`); - await new Promise(f => setTimeout(f, timeout)); - } - continue; - } - assertDone(result); - return; - } + const result = await this._retryAction(progress, 'scroll into view', async () => { + progress.log(` waiting for element to be stable`); + const waitResult = await this.evaluateInUtility(async ([injected, node, { waitForVisible }]) => { + return await injected.checkElementStates(node, waitForVisible ? ['visible', 'stable'] : ['stable']); + }, { waitForVisible }); + if (waitResult) + return waitResult; + return await this._scrollRectIntoViewIfNeeded(); + }, {}); + assertDone(throwRetargetableDOMError(result)); } async scrollIntoViewIfNeeded(metadata: CallMetadata, options: types.TimeoutOptions = {}) { @@ -308,23 +288,11 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { }; } - async _retryPointerAction(progress: Progress, actionName: ActionName, waitForEnabled: boolean, action: (point: types.Point) => Promise<void>, - options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { + async _retryAction(progress: Progress, actionName: string, action: (retry: number) => Promise<PerformActionResult>, options: { trial?: boolean, force?: boolean, skipLocatorHandlersCheckpoint?: boolean }): Promise<'error:notconnected' | 'done'> { let retry = 0; // We progressively wait longer between retries, up to 500ms. const waitTime = [0, 20, 100, 100, 500]; - // By default, we scroll with protocol method to reveal the action point. - // However, that might not work to scroll from under position:sticky elements - // that overlay the target element. To fight this, we cycle through different - // scroll alignments. This works in most scenarios. - const scrollOptions: (ScrollIntoViewOptions | undefined)[] = [ - undefined, - { block: 'end', inline: 'end' }, - { block: 'center', inline: 'center' }, - { block: 'start', inline: 'start' }, - ]; - while (progress.isRunning()) { if (retry) { progress.log(`retrying ${actionName} action${options.trial ? ' (trial run)' : ''}, attempt #${retry}`); @@ -338,8 +306,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { } else { progress.log(`attempting ${actionName} action${options.trial ? ' (trial run)' : ''}`); } - const forceScrollOptions = scrollOptions[retry % scrollOptions.length]; - const result = await this._performPointerAction(progress, actionName, waitForEnabled, action, forceScrollOptions, options); + if (!options.skipLocatorHandlersCheckpoint && !options.force) + await this._frame._page.performLocatorHandlersCheckpoint(progress); + const result = await action(retry); ++retry; if (result === 'error:notvisible') { if (options.force) @@ -353,16 +322,44 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { progress.log(' element is outside of the viewport'); continue; } + if (result === 'error:optionsnotfound') { + progress.log(' did not find some options'); + continue; + } if (typeof result === 'object' && 'hitTargetDescription' in result) { progress.log(` ${result.hitTargetDescription} intercepts pointer events`); continue; } + if (typeof result === 'object' && 'missingState' in result) { + progress.log(` element is not ${result.missingState}`); + continue; + } return result; } return 'done'; } - async _performPointerAction(progress: Progress, actionName: ActionName, waitForEnabled: boolean, action: (point: types.Point) => Promise<void>, forceScrollOptions: ScrollIntoViewOptions | undefined, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notvisible' | 'error:notconnected' | 'error:notinviewport' | { hitTargetDescription: string } | 'done'> { + async _retryPointerAction(progress: Progress, actionName: ActionName, waitForEnabled: boolean, action: (point: types.Point) => Promise<void>, + options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { + // Note: do not perform locator handlers checkpoint to avoid moving the mouse in the middle of a drag operation. + const skipLocatorHandlersCheckpoint = actionName === 'move and up'; + return await this._retryAction(progress, actionName, async retry => { + // By default, we scroll with protocol method to reveal the action point. + // However, that might not work to scroll from under position:sticky elements + // that overlay the target element. To fight this, we cycle through different + // scroll alignments. This works in most scenarios. + const scrollOptions: (ScrollIntoViewOptions | undefined)[] = [ + undefined, + { block: 'end', inline: 'end' }, + { block: 'center', inline: 'center' }, + { block: 'start', inline: 'start' }, + ]; + const forceScrollOptions = scrollOptions[retry % scrollOptions.length]; + return await this._performPointerAction(progress, actionName, waitForEnabled, action, forceScrollOptions, options); + }, { ...options, skipLocatorHandlersCheckpoint }); + } + + async _performPointerAction(progress: Progress, actionName: ActionName, waitForEnabled: boolean, action: (point: types.Point) => Promise<void>, forceScrollOptions: ScrollIntoViewOptions | undefined, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<PerformActionResult> { const { force = false, position } = options; const doScrollIntoView = async () => { @@ -386,9 +383,18 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { if ((options as any).__testHookBeforeStable) await (options as any).__testHookBeforeStable(); - const result = await this._waitForElementStates(progress, waitForEnabled ? ['visible', 'enabled', 'stable'] : ['visible', 'stable'], force); - if (result !== 'done') - return result; + + if (!force) { + const elementStates: ElementState[] = waitForEnabled ? ['visible', 'enabled', 'stable'] : ['visible', 'stable']; + progress.log(` waiting for element to be ${waitForEnabled ? 'visible, enabled and stable' : 'visible and stable'}`); + const result = await this.evaluateInUtility(async ([injected, node, { elementStates }]) => { + return await injected.checkElementStates(node, elementStates); + }, { elementStates }); + if (result) + return result; + progress.log(` element is ${waitForEnabled ? 'visible, enabled and stable' : 'visible and stable'}`); + } + if ((options as any).__testHookAfterStable) await (options as any).__testHookAfterStable(); @@ -407,7 +413,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { await progress.beforeInputAction(this); let hitTargetInterceptionHandle: js.JSHandle<HitTargetInterceptionResult> | undefined; - if (!options.force) { + if (force) { + progress.log(` forcing action`); + } else { if ((options as any).__testHookBeforeHitTarget) await (options as any).__testHookBeforeHitTarget(); @@ -526,16 +534,28 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { } async _selectOption(progress: Progress, elements: ElementHandle[], values: types.SelectOption[], options: types.NavigatingActionWaitOptions & types.ForceOptions): Promise<string[] | 'error:notconnected'> { - const optionsToSelect = [...elements, ...values]; - await progress.beforeInputAction(this); - return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => { - progress.throwIfAborted(); // Avoid action that has side-effects. - progress.log(' selecting specified option(s)'); - const result = await this.evaluatePoll(progress, ([injected, node, { optionsToSelect, force }]) => { - return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled'], force, injected.selectOptions.bind(injected, optionsToSelect)); + let resultingOptions: string[] = []; + await this._retryAction(progress, 'select option', async () => { + await progress.beforeInputAction(this); + if (!options.force) + progress.log(` waiting for element to be visible and enabled`); + const optionsToSelect = [...elements, ...values]; + const result = await this.evaluateInUtility(async ([injected, node, { optionsToSelect, force }]) => { + if (!force) { + const checkResult = await injected.checkElementStates(node, ['visible', 'enabled']); + if (checkResult) + return checkResult; + } + return injected.selectOptions(node, optionsToSelect); }, { optionsToSelect, force: options.force }); + if (Array.isArray(result)) { + progress.log(' selected specified option(s)'); + resultingOptions = result; + return 'done'; + } return result; - }); + }, options); + return resultingOptions; } async fill(metadata: CallMetadata, value: string, options: types.NavigatingActionWaitOptions & types.ForceOptions = {}): Promise<void> { @@ -547,37 +567,49 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { } async _fill(progress: Progress, value: string, options: types.NavigatingActionWaitOptions & types.ForceOptions): Promise<'error:notconnected' | 'done'> { - progress.log(`elementHandle.fill("${value}")`); - await progress.beforeInputAction(this); - return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => { - progress.log(' waiting for element to be visible, enabled and editable'); - const filled = await this.evaluatePoll(progress, ([injected, node, { value, force }]) => { - return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled', 'editable'], force, injected.fill.bind(injected, value)); - }, { value, force: options.force }); - progress.throwIfAborted(); // Avoid action that has side-effects. - if (filled === 'error:notconnected') - return filled; - progress.log(' element is visible, enabled and editable'); - if (filled === 'needsinput') { + progress.log(` fill("${value}")`); + return await this._retryAction(progress, 'fill', async () => { + await progress.beforeInputAction(this); + return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => { + if (!options.force) + progress.log(' waiting for element to be visible, enabled and editable'); + const result = await this.evaluateInUtility(async ([injected, node, { value, force }]) => { + if (!force) { + const checkResult = await injected.checkElementStates(node, ['visible', 'enabled', 'editable']); + if (checkResult) + return checkResult; + } + return injected.fill(node, value); + }, { value, force: options.force }); progress.throwIfAborted(); // Avoid action that has side-effects. - if (value) - await this._page.keyboard.insertText(value); - else - await this._page.keyboard.press('Delete'); - } else { - assertDone(filled); - } - return 'done'; - }, 'input'); + if (result === 'needsinput') { + if (value) + await this._page.keyboard.insertText(value); + else + await this._page.keyboard.press('Delete'); + return 'done'; + } else { + return result; + } + }, 'input'); + }, options); } async selectText(metadata: CallMetadata, options: types.TimeoutOptions & types.ForceOptions = {}): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - progress.throwIfAborted(); // Avoid action that has side-effects. - const result = await this.evaluatePoll(progress, ([injected, node, force]) => { - return injected.waitForElementStatesAndPerformAction(node, ['visible'], force, injected.selectText.bind(injected)); - }, options.force); + const result = await this._retryAction(progress, 'selectText', async () => { + if (!options.force) + progress.log(' waiting for element to be visible'); + return await this.evaluateInUtility(async ([injected, node, { force }]) => { + if (!force) { + const checkResult = await injected.checkElementStates(node, ['visible']); + if (checkResult) + return checkResult; + } + return injected.selectText(node); + }, { force: options.force }); + }, options); assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); } @@ -765,10 +797,12 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { async waitForElementState(metadata: CallMetadata, state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled' | 'editable', options: types.TimeoutOptions = {}): Promise<void> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - progress.log(` waiting for element to be ${state}`); - const result = await this.evaluatePoll(progress, ([injected, node, state]) => { - return injected.waitForElementStatesAndPerformAction(node, [state], false, () => 'done' as const); - }, state); + const actionName = `wait for ${state}`; + const result = await this._retryAction(progress, actionName, async () => { + return await this.evaluateInUtility(async ([injected, node, state]) => { + return (await injected.checkElementStates(node, [state])) || 'done'; + }, state); + }, {}); assertDone(throwRetargetableDOMError(result)); }, this._page._timeoutSettings.timeout(options)); } @@ -786,18 +820,6 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { return this; } - async _waitForElementStates(progress: Progress, states: ElementState[], force: boolean): Promise<'error:notconnected' | 'done'> { - const title = joinWithAnd(states); - progress.log(` waiting for element to be ${title}`); - const result = await this.evaluatePoll(progress, ([injected, node, { states, force }]) => { - return injected.waitForElementStatesAndPerformAction(node, states, force, () => 'done' as const); - }, { states, force }); - if (result === 'error:notconnected') - return result; - progress.log(` element is ${title}`); - return result; - } - async _checkFrameIsHitTarget(point: types.Point): Promise<{ framePoint: types.Point | undefined } | 'error:notconnected' | { hitTargetDescription: string }> { let frame = this._frame; const data: { frame: frames.Frame, frameElement: ElementHandle<Element> | null, pointInFrame: types.Point }[] = []; @@ -835,72 +857,6 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> { } } -// Handles an InjectedScriptPoll running in injected script: -// - streams logs into progress; -// - cancels the poll when progress cancels. -export class InjectedScriptPollHandler<T> { - private _progress: Progress; - private _poll: js.JSHandle<InjectedScriptPoll<T>> | null; - - constructor(progress: Progress, poll: js.JSHandle<InjectedScriptPoll<T>>) { - this._progress = progress; - this._poll = poll; - // Ensure we cancel the poll before progress aborts and returns: - // - no unnecessary work in the page; - // - no possible side effects after progress promise rejects. - this._progress.cleanupWhenAborted(() => this.cancel()); - this._streamLogs(); - } - - private async _streamLogs() { - while (this._poll && this._progress.isRunning()) { - const log = await this._poll.evaluate(poll => poll.takeNextLogs()).catch(e => [] as LogEntry[]); - if (!this._poll || !this._progress.isRunning()) - return; - for (const entry of log) - this._progress.logEntry(entry); - } - } - - async finishHandle(): Promise<js.SmartHandle<T>> { - try { - const result = await this._poll!.evaluateHandle(poll => poll.run()); - await this._finishInternal(); - return result; - } finally { - await this.cancel(); - } - } - - async finish(): Promise<T> { - try { - const result = await this._poll!.evaluate(poll => poll.run()); - await this._finishInternal(); - return result; - } finally { - await this.cancel(); - } - } - - private async _finishInternal() { - if (!this._poll) - return; - // Retrieve all the logs before continuing. - const log = await this._poll.evaluate(poll => poll.takeLastLogs()).catch(e => [] as LogEntry[]); - for (const entry of log) - this._progress.logEntry(entry); - } - - async cancel() { - if (!this._poll) - return; - const copy = this._poll; - this._poll = null; - await copy.evaluate(p => p.cancel()).catch(e => {}); - copy.dispose(); - } -} - export function throwRetargetableDOMError<T>(result: T | 'error:notconnected'): T { if (result === 'error:notconnected') throw new Error('Element is not attached to the DOM'); @@ -938,12 +894,4 @@ function compensateHalfIntegerRoundingError(point: types.Point) { point.y -= 0.02; } -export type SchedulableTask<T> = (injectedScript: js.JSHandle<InjectedScript>) => Promise<js.JSHandle<InjectedScriptPoll<T>>>; - -function joinWithAnd(strings: string[]): string { - if (strings.length <= 1) - return strings.join(''); - return strings.slice(0, strings.length - 1).join(', ') + ' and ' + strings[strings.length - 1]; -} - export const kUnableToAdoptErrorMessage = 'Unable to adopt element handle from a different document'; diff --git a/playwright/packages/playwright-core/src/server/electron/electron.ts b/playwright/packages/playwright-core/src/server/electron/electron.ts index 1a52a913a1..9bc8c67842 100644 --- a/playwright/packages/playwright-core/src/server/electron/electron.ts +++ b/playwright/packages/playwright-core/src/server/electron/electron.ts @@ -30,7 +30,8 @@ import { TimeoutSettings } from '../../common/timeoutSettings'; import { ManualPromise, wrapInASCIIBox } from '../../utils'; import { WebSocketTransport } from '../transport'; import { launchProcess, envArrayToObject } from '../../utils/processLauncher'; -import { BrowserContext, validateBrowserContextOptions } from '../browserContext'; +import type { BrowserContext } from '../browserContext'; +import { validateBrowserContextOptions } from '../browserContext'; import type { BrowserWindow } from 'electron'; import type { Progress } from '../progress'; import { ProgressController } from '../progress'; @@ -40,15 +41,18 @@ import type { BrowserOptions, BrowserProcess } from '../browser'; import type { Playwright } from '../playwright'; import type * as childProcess from 'child_process'; import * as readline from 'readline'; -import { RecentLogsCollector } from '../../common/debugLogger'; +import { RecentLogsCollector } from '../../utils/debugLogger'; import { serverSideCallMetadata, SdkObject } from '../instrumentation'; import type * as channels from '@protocol/channels'; +import { toConsoleMessageLocation } from '../chromium/crProtocolHelper'; +import { ConsoleMessage } from '../console'; const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-'); export class ElectronApplication extends SdkObject { static Events = { Close: 'close', + Console: 'console', }; private _browserContext: CRBrowserContext; @@ -63,10 +67,6 @@ export class ElectronApplication extends SdkObject { super(parent, 'electron-app'); this._process = process; this._browserContext = browser._defaultContext as CRBrowserContext; - this._browserContext.on(BrowserContext.Events.Close, () => { - // Emit application closed after context closed. - Promise.resolve().then(() => this.emit(ElectronApplication.Events.Close)); - }); this._nodeConnection = nodeConnection; this._nodeSession = nodeConnection.rootSession; this._nodeSession.on('Runtime.executionContextCreated', async (event: Protocol.Runtime.executionContextCreatedPayload) => { @@ -82,13 +82,41 @@ export class ElectronApplication extends SdkObject { }); this._nodeElectronHandlePromise.resolve(new js.JSHandle(this._nodeExecutionContext!, 'object', 'ElectronModule', remoteObject.objectId!)); }); + this._nodeSession.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event)); + const appClosePromise = new Promise(f => this.once(ElectronApplication.Events.Close, f)); this._browserContext.setCustomCloseHandler(async () => { await this._browserContext.stopVideoRecording(); const electronHandle = await this._nodeElectronHandlePromise; await electronHandle.evaluate(({ app }) => app.quit()).catch(() => {}); + this._nodeConnection.close(); + await appClosePromise; }); } + async _onConsoleAPI(event: Protocol.Runtime.consoleAPICalledPayload) { + if (event.executionContextId === 0) { + // DevTools protocol stores the last 1000 console messages. These + // messages are always reported even for removed execution contexts. In + // this case, they are marked with executionContextId = 0 and are + // reported upon enabling Runtime agent. + // + // Ignore these messages since: + // - there's no execution context we can use to operate with message + // arguments + // - these messages are reported before Playwright clients can subscribe + // to the 'console' + // page event. + // + // @see https://github.com/GoogleChrome/puppeteer/issues/3865 + return; + } + if (!this._nodeExecutionContext) + return; + const args = event.args.map(arg => this._nodeExecutionContext!.createHandle(arg)); + const message = new ConsoleMessage(null, event.type, undefined, args, toConsoleMessageLocation(event.stackTrace)); + this.emit(ElectronApplication.Events.Console, message); + } + async initialize() { await this._nodeSession.send('Runtime.enable', {}); // Delay loading the app until browser is started and the browser targets are configured to auto-attach. @@ -104,11 +132,8 @@ export class ElectronApplication extends SdkObject { } async close() { - const progressController = new ProgressController(serverSideCallMetadata(), this); - const closed = progressController.run(progress => helper.waitForEvent(progress, this, ElectronApplication.Events.Close).promise); + // This will call BrowserContext.setCustomCloseHandler. await this._browserContext.close({ reason: 'Application exited' }); - this._nodeConnection.close(); - await closed; } async browserWindow(page: Page): Promise<js.JSHandle<BrowserWindow>> { @@ -135,12 +160,13 @@ export class Electron extends SdkObject { controller.setLogName('browser'); return controller.run(async progress => { let app: ElectronApplication | undefined = undefined; + // --remote-debugging-port=0 must be the last playwright's argument, loader.ts relies on it. const electronArguments = ['--inspect=0', '--remote-debugging-port=0', ...args]; if (os.platform() === 'linux') { const runningAsRoot = process.geteuid && process.geteuid() === 0; if (runningAsRoot && electronArguments.indexOf('--no-sandbox') === -1) - electronArguments.push('--no-sandbox'); + electronArguments.unshift('--no-sandbox'); } const artifactsDir = await fs.promises.mkdtemp(ARTIFACTS_FOLDER); @@ -189,7 +215,7 @@ export class Electron extends SdkObject { handleSIGINT: true, handleSIGTERM: true, handleSIGHUP: true, - onExit: () => {}, + onExit: () => app?.emit(ElectronApplication.Events.Close), }); const waitForXserverError = new Promise(async (resolve, reject) => { diff --git a/playwright/packages/playwright-core/src/server/electron/loader.ts b/playwright/packages/playwright-core/src/server/electron/loader.ts index 3180abc013..98bd92d457 100644 --- a/playwright/packages/playwright-core/src/server/electron/loader.ts +++ b/playwright/packages/playwright-core/src/server/electron/loader.ts @@ -17,9 +17,10 @@ const { app } = require('electron'); const { chromiumSwitches } = require('../chromium/chromiumSwitches'); -// [Electron, -r, loader.js, --inspect=0, --remote-debugging-port=0, ...args] -process.argv.splice(1, 4); - +// Always pass user arguments first, see https://github.com/microsoft/playwright/issues/16614 and +// https://github.com/microsoft/playwright/issues/29198. +// [Electron, -r, loader.js[, --no-sandbox>], --inspect=0, --remote-debugging-port=0, ...args] +process.argv.splice(1, process.argv.indexOf('--remote-debugging-port=0')); for (const arg of chromiumSwitches) { const match = arg.match(/--([^=]*)=?(.*)/)!; diff --git a/playwright/packages/playwright-core/src/server/firefox/ffAccessibility.ts b/playwright/packages/playwright-core/src/server/firefox/ffAccessibility.ts index 9bf519c3bb..d8d56b58de 100644 --- a/playwright/packages/playwright-core/src/server/firefox/ffAccessibility.ts +++ b/playwright/packages/playwright-core/src/server/firefox/ffAccessibility.ts @@ -245,7 +245,6 @@ class FFAXNode implements accessibility.AXNode { const tokenProperties: Array<keyof channels.AXNode & keyof Protocol.Accessibility.AXTree> = [ 'autocomplete', 'haspopup', - 'invalid', 'orientation', ]; for (const tokenProperty of tokenProperties) { @@ -261,6 +260,8 @@ class FFAXNode implements accessibility.AXNode { axNode.checked = this._payload.checked === true ? 'checked' : this._payload.checked === 'mixed' ? 'mixed' : 'unchecked'; if ('pressed' in this._payload) axNode.pressed = this._payload.pressed === true ? 'pressed' : 'released'; + if ('invalid' in this._payload) + axNode.invalid = this._payload.invalid === true ? 'true' : 'false'; return axNode; } } diff --git a/playwright/packages/playwright-core/src/server/firefox/ffConnection.ts b/playwright/packages/playwright-core/src/server/firefox/ffConnection.ts index 3d5070440b..f36dba4b23 100644 --- a/playwright/packages/playwright-core/src/server/firefox/ffConnection.ts +++ b/playwright/packages/playwright-core/src/server/firefox/ffConnection.ts @@ -18,8 +18,8 @@ import { EventEmitter } from 'events'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import type { Protocol } from './protocol'; -import type { RecentLogsCollector } from '../../common/debugLogger'; -import { debugLogger } from '../../common/debugLogger'; +import type { RecentLogsCollector } from '../../utils/debugLogger'; +import { debugLogger } from '../../utils/debugLogger'; import type { ProtocolLogger } from '../types'; import { helper } from '../helper'; import { ProtocolError } from '../protocolError'; diff --git a/playwright/packages/playwright-core/src/server/firefox/ffPage.ts b/playwright/packages/playwright-core/src/server/firefox/ffPage.ts index 8b3876036c..d2d2a03412 100644 --- a/playwright/packages/playwright-core/src/server/firefox/ffPage.ts +++ b/playwright/packages/playwright-core/src/server/firefox/ffPage.ts @@ -32,7 +32,7 @@ import { FFNetworkManager } from './ffNetworkManager'; import type { Protocol } from './protocol'; import type { Progress } from '../progress'; import { splitErrorMessage } from '../../utils/stackTrace'; -import { debugLogger } from '../../common/debugLogger'; +import { debugLogger } from '../../utils/debugLogger'; import { ManualPromise } from '../../utils/manualPromise'; import { BrowserContext } from '../browserContext'; import { TargetClosedError } from '../errors'; diff --git a/playwright/packages/playwright-core/src/server/frames.ts b/playwright/packages/playwright-core/src/server/frames.ts index 6631da6d69..dab9a1ebcc 100644 --- a/playwright/packages/playwright-core/src/server/frames.ts +++ b/playwright/packages/playwright-core/src/server/frames.ts @@ -31,10 +31,10 @@ import type { Progress } from './progress'; import { ProgressController } from './progress'; import { LongStandingScope, assert, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime } from '../utils'; import { ManualPromise } from '../utils/manualPromise'; -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import type { CallMetadata } from './instrumentation'; import { serverSideCallMetadata, SdkObject } from './instrumentation'; -import type { InjectedScript, ElementStateWithoutStable, FrameExpectParams, InjectedScriptPoll, InjectedScriptProgress } from './injected/injectedScript'; +import type { InjectedScript, ElementStateWithoutStable, FrameExpectParams } from './injected/injectedScript'; import { isSessionClosedError } from './protocolError'; import { type ParsedSelector, isInvalidSelectorError } from '../utils/isomorphic/selectorParser'; import type { ScreenshotOptions } from './screenshotter'; @@ -80,8 +80,6 @@ export type NavigationEvent = { isPublic?: boolean; }; -export type SchedulableTask<T> = (injectedScript: js.JSHandle<InjectedScript>) => Promise<js.JSHandle<InjectedScriptPoll<T>>>; -export type DomTaskBody<T, R, E> = (progress: InjectedScriptProgress, element: E, data: T, elements: Element[]) => R | symbol; type ElementCallback<T, R> = (injected: InjectedScript, element: Element, data: T) => R; export class NavigationAbortedError extends Error { @@ -482,7 +480,7 @@ export class Frame extends SdkObject { private _childFrames = new Set<Frame>(); _name = ''; _inflightRequests = new Set<network.Request>(); - private _networkIdleTimer: NodeJS.Timer | undefined; + private _networkIdleTimer: NodeJS.Timeout | undefined; private _setContentCounter = 0; readonly _detachedScope = new LongStandingScope(); private _raceAgainstEvaluationStallingEventsPromises = new Set<ManualPromise<any>>(); @@ -1092,9 +1090,13 @@ export class Frame extends SdkObject { progress: Progress, selector: string, strict: boolean | undefined, + performLocatorHandlersCheckpoint: boolean, action: (handle: dom.ElementHandle<Element>) => Promise<R | 'error:notconnected'>): Promise<R> { progress.log(`waiting for ${this._asLocator(selector)}`); return 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, { strict }); progress.throwIfAborted(); if (!resolved) @@ -1135,7 +1137,7 @@ export class Frame extends SdkObject { } async rafrafTimeoutScreenshotElementWithProgress(progress: Progress, selector: string, timeout: number, options: ScreenshotOptions): Promise<Buffer> { - return await this._retryWithProgressIfNotConnected(progress, selector, true /* strict */, async handle => { + return await this._retryWithProgressIfNotConnected(progress, selector, true /* strict */, true /* performLocatorHandlersCheckpoint */, async handle => { await handle._frame.rafrafTimeout(timeout); return await this._page._screenshotter.screenshotElement(progress, handle, options); }); @@ -1144,21 +1146,21 @@ export class Frame extends SdkObject { async click(metadata: CallMetadata, selector: string, options: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._click(progress, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performLocatorHandlersCheckpoint */, handle => handle._click(progress, options))); }, this._page._timeoutSettings.timeout(options)); } async dblclick(metadata: CallMetadata, selector: string, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._dblclick(progress, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performLocatorHandlersCheckpoint */, handle => handle._dblclick(progress, options))); }, this._page._timeoutSettings.timeout(options)); } async dragAndDrop(metadata: CallMetadata, source: string, target: string, options: types.DragActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); await controller.run(async progress => { - dom.assertDone(await this._retryWithProgressIfNotConnected(progress, source, options.strict, async handle => { + dom.assertDone(await this._retryWithProgressIfNotConnected(progress, source, options.strict, !options.force /* performLocatorHandlersCheckpoint */, async handle => { return handle._retryPointerAction(progress, 'move and down', false, async point => { await this._page.mouse.move(point.x, point.y); await this._page.mouse.down(); @@ -1168,7 +1170,8 @@ export class Frame extends SdkObject { timeout: progress.timeUntilDeadline(), }); })); - dom.assertDone(await this._retryWithProgressIfNotConnected(progress, target, options.strict, async handle => { + // Note: do not perform locator handlers checkpoint to avoid moving the mouse in the middle of a drag operation. + dom.assertDone(await this._retryWithProgressIfNotConnected(progress, target, options.strict, false /* performLocatorHandlersCheckpoint */, async handle => { return handle._retryPointerAction(progress, 'move and up', false, async point => { await this._page.mouse.move(point.x, point.y); await this._page.mouse.up(); @@ -1186,28 +1189,28 @@ export class Frame extends SdkObject { throw new Error('The page does not support tap. Use hasTouch context option to enable touch support.'); const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._tap(progress, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performLocatorHandlersCheckpoint */, handle => handle._tap(progress, options))); }, this._page._timeoutSettings.timeout(options)); } async fill(metadata: CallMetadata, selector: string, value: string, options: types.NavigatingActionWaitOptions & { force?: boolean }) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._fill(progress, value, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performLocatorHandlersCheckpoint */, handle => handle._fill(progress, value, options))); }, this._page._timeoutSettings.timeout(options)); } async focus(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions = {}) { const controller = new ProgressController(metadata, this); await controller.run(async progress => { - dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._focus(progress))); + dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performLocatorHandlersCheckpoint */, handle => handle._focus(progress))); }, this._page._timeoutSettings.timeout(options)); } async blur(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions = {}) { const controller = new ProgressController(metadata, this); await controller.run(async progress => { - dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._blur(progress))); + dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performLocatorHandlersCheckpoint */, handle => handle._blur(progress))); }, this._page._timeoutSettings.timeout(options)); } @@ -1270,6 +1273,12 @@ export class Frame extends SdkObject { const controller = new ProgressController(metadata, this); return controller.run(async progress => { progress.log(` checking visibility of ${this._asLocator(selector)}`); + return await this.isVisibleInternal(selector, options, scope); + }, this._page._timeoutSettings.timeout({})); + } + + async isVisibleInternal(selector: string, options: types.StrictOptions = {}, scope?: dom.ElementHandle): Promise<boolean> { + try { const resolved = await this.selectors.resolveInjectedForSelector(selector, options, scope); if (!resolved) return false; @@ -1278,11 +1287,11 @@ export class Frame extends SdkObject { const state = element ? injected.elementState(element, 'visible') : false; return state === 'error:notconnected' ? false : state; }, { info: resolved.info, root: resolved.frame === this ? scope : undefined }); - }, this._page._timeoutSettings.timeout({})).catch(e => { + } catch (e) { if (js.isJavaScriptErrorInEvaluate(e) || isInvalidSelectorError(e) || isSessionClosedError(e)) throw e; return false; - }); + } } async isHidden(metadata: CallMetadata, selector: string, options: types.StrictOptions = {}, scope?: dom.ElementHandle): Promise<boolean> { @@ -1308,14 +1317,14 @@ export class Frame extends SdkObject { async hover(metadata: CallMetadata, selector: string, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._hover(progress, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performLocatorHandlersCheckpoint */, handle => handle._hover(progress, options))); }, this._page._timeoutSettings.timeout(options)); } async selectOption(metadata: CallMetadata, selector: string, elements: dom.ElementHandle[], values: types.SelectOption[], options: types.NavigatingActionWaitOptions & types.ForceOptions = {}): Promise<string[]> { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._selectOption(progress, elements, values, options)); + return await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performLocatorHandlersCheckpoint */, handle => handle._selectOption(progress, elements, values, options)); }, this._page._timeoutSettings.timeout(options)); } @@ -1323,35 +1332,35 @@ export class Frame extends SdkObject { const inputFileItems = await prepareFilesForUpload(this, params); const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, params.strict, handle => handle._setInputFiles(progress, inputFileItems, params))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, params.strict, true /* performLocatorHandlersCheckpoint */, handle => handle._setInputFiles(progress, inputFileItems, params))); }, this._page._timeoutSettings.timeout(params)); } async type(metadata: CallMetadata, selector: string, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._type(progress, text, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performLocatorHandlersCheckpoint */, handle => handle._type(progress, text, options))); }, this._page._timeoutSettings.timeout(options)); } async press(metadata: CallMetadata, selector: string, key: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._press(progress, key, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true /* performLocatorHandlersCheckpoint */, handle => handle._press(progress, key, options))); }, this._page._timeoutSettings.timeout(options)); } async check(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._setChecked(progress, true, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performLocatorHandlersCheckpoint */, handle => handle._setChecked(progress, true, options))); }, this._page._timeoutSettings.timeout(options)); } async uncheck(metadata: CallMetadata, selector: string, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { - return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._setChecked(progress, false, options))); + return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, !options.force /* performLocatorHandlersCheckpoint */, handle => handle._setChecked(progress, false, options))); }, this._page._timeoutSettings.timeout(options)); } @@ -1363,6 +1372,14 @@ export class Frame extends SdkObject { } async expect(metadata: CallMetadata, selector: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { + const result = await this._expectImpl(metadata, selector, options); + // Library mode special case for the expect errors which are return values, not exceptions. + if (result.matches === options.isNot) + metadata.error = { error: { name: 'Expect', message: 'Expect failed' } }; + return result; + } + + private async _expectImpl(metadata: CallMetadata, selector: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { let timeout = this._page._timeoutSettings.timeout(options); const start = timeout > 0 ? monotonicTime() : 0; const lastIntermediateResult: { received?: any, isSet: boolean } = { isSet: false }; @@ -1386,6 +1403,8 @@ export class Frame extends SdkObject { progress.log(`waiting for ${this._asLocator(selector)}`); } return await this.retryWithProgressAndTimeouts(progress, [100, 250, 500, 1000], async continuePolling => { + await this._page.performLocatorHandlersCheckpoint(progress); + const selectorInFrame = await this.selectors.resolveFrameForSelector(selector, { strict: true }); progress.throwIfAborted(); diff --git a/playwright/packages/playwright-core/src/server/helper.ts b/playwright/packages/playwright-core/src/server/helper.ts index 38246c9fd6..84fd0174f6 100644 --- a/playwright/packages/playwright-core/src/server/helper.ts +++ b/playwright/packages/playwright-core/src/server/helper.ts @@ -18,7 +18,7 @@ import type { EventEmitter } from 'events'; import type * as types from './types'; import type { Progress } from './progress'; -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import type { RegisteredListener } from '../utils/eventsHelper'; import { eventsHelper } from '../utils/eventsHelper'; diff --git a/playwright/packages/playwright-core/src/server/injected/consoleApi.ts b/playwright/packages/playwright-core/src/server/injected/consoleApi.ts index cce789021f..f0231e9551 100644 --- a/playwright/packages/playwright-core/src/server/injected/consoleApi.ts +++ b/playwright/packages/playwright-core/src/server/injected/consoleApi.ts @@ -22,23 +22,22 @@ import type { Language } from '../../utils/isomorphic/locatorGenerators'; import type { InjectedScript } from './injectedScript'; const selectorSymbol = Symbol('selector'); -const injectedScriptSymbol = Symbol('injectedScript'); class Locator { + [selectorSymbol]: string; element: Element | undefined; elements: Element[] | undefined; constructor(injectedScript: InjectedScript, selector: string, options?: { hasText?: string | RegExp, hasNotText?: string | RegExp, has?: Locator, hasNot?: Locator }) { - (this as any)[selectorSymbol] = selector; - (this as any)[injectedScriptSymbol] = injectedScript; if (options?.hasText) selector += ` >> internal:has-text=${escapeForTextSelector(options.hasText, false)}`; if (options?.hasNotText) selector += ` >> internal:has-not-text=${escapeForTextSelector(options.hasNotText, false)}`; if (options?.has) - selector += ` >> internal:has=` + JSON.stringify((options.has as any)[selectorSymbol]); + selector += ` >> internal:has=` + JSON.stringify(options.has[selectorSymbol]); if (options?.hasNot) - selector += ` >> internal:has-not=` + JSON.stringify((options.hasNot as any)[selectorSymbol]); + selector += ` >> internal:has-not=` + JSON.stringify(options.hasNot[selectorSymbol]); + this[selectorSymbol] = selector; if (selector) { const parsed = injectedScript.parseSelector(selector); this.element = injectedScript.querySelector(parsed, injectedScript.document, false); @@ -60,8 +59,8 @@ class Locator { self.first = (): Locator => self.locator('nth=0'); self.last = (): Locator => self.locator('nth=-1'); self.nth = (index: number): Locator => self.locator(`nth=${index}`); - self.and = (locator: Locator): Locator => new Locator(injectedScript, selectorBase + ` >> internal:and=` + JSON.stringify((locator as any)[selectorSymbol])); - self.or = (locator: Locator): Locator => new Locator(injectedScript, selectorBase + ` >> internal:or=` + JSON.stringify((locator as any)[selectorSymbol])); + self.and = (locator: Locator): Locator => new Locator(injectedScript, selectorBase + ` >> internal:and=` + JSON.stringify(locator[selectorSymbol])); + self.or = (locator: Locator): Locator => new Locator(injectedScript, selectorBase + ` >> internal:or=` + JSON.stringify(locator[selectorSymbol])); } } @@ -120,13 +119,13 @@ class ConsoleAPI { private _selector(element: Element) { if (!(element instanceof Element)) throw new Error(`Usage: playwright.selector(element).`); - return this._injectedScript.generateSelector(element); + return this._injectedScript.generateSelectorSimple(element); } private _generateLocator(element: Element, language?: Language) { if (!(element instanceof Element)) throw new Error(`Usage: playwright.locator(element).`); - const selector = this._injectedScript.generateSelector(element); + const selector = this._injectedScript.generateSelectorSimple(element); return asLocator(language || 'javascript', selector); } diff --git a/playwright/packages/playwright-core/src/server/injected/highlight.css b/playwright/packages/playwright-core/src/server/injected/highlight.css index e45e45b55e..9ee0370720 100644 --- a/playwright/packages/playwright-core/src/server/injected/highlight.css +++ b/playwright/packages/playwright-core/src/server/injected/highlight.css @@ -33,7 +33,30 @@ x-pw-tooltip { max-width: 600px; position: absolute; top: 0; - padding: 4px; + padding: 0; + flex-direction: column; + overflow: hidden; +} + +x-pw-tooltip-line { + display: flex; + max-width: 600px; + padding: 6px; + user-select: none; + cursor: pointer; +} + +x-pw-tooltip-line.selectable:hover { + background-color: hsl(0, 0%, 95%); + overflow: hidden; +} + +x-pw-tooltip-footer { + display: flex; + max-width: 600px; + padding: 6px; + user-select: none; + color: #777; } x-pw-dialog { diff --git a/playwright/packages/playwright-core/src/server/injected/highlight.ts b/playwright/packages/playwright-core/src/server/injected/highlight.ts index 3052846104..0487bf04d7 100644 --- a/playwright/packages/playwright-core/src/server/injected/highlight.ts +++ b/playwright/packages/playwright-core/src/server/injected/highlight.ts @@ -33,6 +33,9 @@ type HighlightEntry = { export type HighlightOptions = { tooltipText?: string; + tooltipList?: string[]; + tooltipFooter?: string; + tooltipListItemSelected?: (index: number | undefined) => void; color?: string; }; @@ -40,6 +43,7 @@ export class Highlight { private _glassPaneElement: HTMLElement; private _glassPaneShadow: ShadowRoot; private _highlightEntries: HighlightEntry[] = []; + private _highlightOptions: HighlightOptions = {}; private _actionPointElement: HTMLElement; private _isUnderTest: boolean; private _injectedScript: InjectedScript; @@ -64,6 +68,8 @@ export class Highlight { this._glassPaneElement.addEventListener(eventName, e => { e.stopPropagation(); e.stopImmediatePropagation(); + if (e.type === 'click' && (e as MouseEvent).button === 0 && this._highlightOptions.tooltipListItemSelected) + this._highlightOptions.tooltipListItemSelected(undefined); }); } this._actionPointElement = document.createElement('x-pw-action-point'); @@ -112,6 +118,8 @@ export class Highlight { entry.tooltipElement?.remove(); } this._highlightEntries = []; + this._highlightOptions = {}; + this._glassPaneElement.style.pointerEvents = 'none'; } updateHighlight(elements: Element[], options: HighlightOptions) { @@ -130,27 +138,48 @@ export class Highlight { // Code below should trigger one layout and leave with the // destroyed layout. - if (this._highlightIsUpToDate(elements, options.tooltipText)) + if (this._highlightIsUpToDate(elements, options)) return; // 1. Destroy the layout this.clearHighlight(); + this._highlightOptions = options; + this._glassPaneElement.style.pointerEvents = options.tooltipListItemSelected ? 'initial' : 'none'; for (let i = 0; i < elements.length; ++i) { const highlightElement = this._createHighlightElement(); this._glassPaneShadow.appendChild(highlightElement); let tooltipElement; - if (options.tooltipText) { + if (options.tooltipList || options.tooltipText || options.tooltipFooter) { tooltipElement = this._injectedScript.document.createElement('x-pw-tooltip'); this._glassPaneShadow.appendChild(tooltipElement); - const suffix = elements.length > 1 ? ` [${i + 1} of ${elements.length}]` : ''; - tooltipElement.textContent = options.tooltipText + suffix; tooltipElement.style.top = '0'; tooltipElement.style.left = '0'; tooltipElement.style.display = 'flex'; + let lines: string[] = []; + if (options.tooltipList) { + lines = options.tooltipList; + } else if (options.tooltipText) { + const suffix = elements.length > 1 ? ` [${i + 1} of ${elements.length}]` : ''; + lines = [options.tooltipText + suffix]; + } + for (let index = 0; index < lines.length; index++) { + const element = this._injectedScript.document.createElement('x-pw-tooltip-line'); + element.textContent = lines[index]; + tooltipElement.appendChild(element); + if (options.tooltipListItemSelected) { + element.classList.add('selectable'); + element.addEventListener('click', () => options.tooltipListItemSelected?.(index)); + } + } + if (options.tooltipFooter) { + const footer = this._injectedScript.document.createElement('x-pw-tooltip-footer'); + footer.textContent = options.tooltipFooter; + tooltipElement.appendChild(footer); + } } - this._highlightEntries.push({ targetElement: elements[i], tooltipElement, highlightElement, tooltipText: options.tooltipText }); + this._highlightEntries.push({ targetElement: elements[i], tooltipElement, highlightElement }); } // 2. Trigger layout while positioning tooltips and computing bounding boxes. @@ -212,12 +241,26 @@ export class Highlight { return { anchorLeft, anchorTop }; } - private _highlightIsUpToDate(elements: Element[], tooltipText: string | undefined): boolean { + private _highlightIsUpToDate(elements: Element[], options: HighlightOptions): boolean { + if (options.tooltipText !== this._highlightOptions.tooltipText) + return false; + if (options.tooltipListItemSelected !== this._highlightOptions.tooltipListItemSelected) + return false; + if (options.tooltipFooter !== this._highlightOptions.tooltipFooter) + return false; + + if (options.tooltipList?.length !== this._highlightOptions.tooltipList?.length) + return false; + if (options.tooltipList && this._highlightOptions.tooltipList) { + for (let i = 0; i < options.tooltipList.length; i++) { + if (options.tooltipList[i] !== this._highlightOptions.tooltipList[i]) + return false; + } + } + if (elements.length !== this._highlightEntries.length) return false; for (let i = 0; i < this._highlightEntries.length; ++i) { - if (tooltipText !== this._highlightEntries[i].tooltipText) - return false; if (elements[i] !== this._highlightEntries[i].targetElement) return false; const oldBox = this._highlightEntries[i].box; @@ -227,6 +270,7 @@ export class Highlight { if (box.top !== oldBox.top || box.right !== oldBox.right || box.bottom !== oldBox.bottom || box.left !== oldBox.left) return false; } + return true; } diff --git a/playwright/packages/playwright-core/src/server/injected/injectedScript.ts b/playwright/packages/playwright-core/src/server/injected/injectedScript.ts index 67852c541f..1b8160194d 100644 --- a/playwright/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/playwright/packages/playwright-core/src/server/injected/injectedScript.ts @@ -24,7 +24,7 @@ import type { NestedSelectorBody, ParsedSelector, ParsedSelectorPart } from '../ import { visitAllSelectorParts, parseSelector, stringifySelector } from '../../utils/isomorphic/selectorParser'; import { type TextMatcher, elementMatchesText, elementText, type ElementText, getElementLabels } from './selectorUtils'; import { SelectorEvaluatorImpl, sortInDOMOrder } from './selectorEvaluator'; -import { enclosingShadowRootOrDocument, isElementVisible, parentElementOrShadowHost, setBrowserName } from './domUtils'; +import { enclosingShadowRootOrDocument, isElementVisible, isInsideScope, parentElementOrShadowHost, setBrowserName } from './domUtils'; import type { CSSComplexSelectorList } from '../../utils/isomorphic/cssParser'; import { generateSelector, type GenerateSelectorOptions } from './selectorGenerator'; import type * as channels from '@protocol/channels'; @@ -35,31 +35,8 @@ import { asLocator } from '../../utils/isomorphic/locatorGenerators'; import type { Language } from '../../utils/isomorphic/locatorGenerators'; import { normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils'; -type Predicate<T> = (progress: InjectedScriptProgress) => T | symbol; - -export type InjectedScriptProgress = { - injectedScript: InjectedScript; - continuePolling: symbol; - aborted: boolean; - log: (message: string) => void; - logRepeating: (message: string) => void; -}; - -export type LogEntry = { - message?: string; -}; - export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any }; -export type InjectedScriptPoll<T> = { - run: () => Promise<T>, - // Takes more logs, waiting until at least one message is available. - takeNextLogs: () => Promise<LogEntry[]>, - // Takes all current logs without waiting. - takeLastLogs: () => LogEntry[], - cancel: () => void, -}; - export type ElementStateWithoutStable = 'visible' | 'hidden' | 'enabled' | 'disabled' | 'editable' | 'checked' | 'unchecked'; export type ElementState = ElementStateWithoutStable | 'stable'; @@ -89,6 +66,7 @@ export class InjectedScript { // eslint-disable-next-line no-restricted-globals readonly window: Window & typeof globalThis; readonly document: Document; + readonly utils = { isInsideScope, elementText, asLocator, normalizeWhiteSpace }; // eslint-disable-next-line no-restricted-globals constructor(window: Window & typeof globalThis, isUnderTest: boolean, sdkLanguage: Language, testIdAttributeNameForStrictErrorAndConsoleCodegen: string, stableRafCount: number, browserName: string, customEngines: { name: string, engine: SelectorEngine }[]) { @@ -163,7 +141,11 @@ export class InjectedScript { return result; } - generateSelector(targetElement: Element, options?: GenerateSelectorOptions): string { + generateSelector(targetElement: Element, options: GenerateSelectorOptions) { + return generateSelector(this, targetElement, options); + } + + generateSelectorSimple(targetElement: Element, options?: GenerateSelectorOptions): string { return generateSelector(this, targetElement, { ...options, testIdAttributeName: this._testIdAttributeNameForStrictErrorAndConsoleCodegen }).selector; } @@ -449,92 +431,6 @@ export class InjectedScript { }); } - pollRaf<T>(predicate: Predicate<T>): InjectedScriptPoll<T> { - return this.poll(predicate, next => requestAnimationFrame(next)); - } - - private poll<T>(predicate: Predicate<T>, scheduleNext: (next: () => void) => void): InjectedScriptPoll<T> { - return this._runAbortableTask(progress => { - let fulfill: (result: T) => void; - let reject: (error: Error) => void; - const result = new Promise<T>((f, r) => { fulfill = f; reject = r; }); - - const next = () => { - if (progress.aborted) - return; - try { - const success = predicate(progress); - if (success !== progress.continuePolling) - fulfill(success as T); - else - scheduleNext(next); - } catch (e) { - progress.log(' ' + e.message); - reject(e); - } - }; - - next(); - return result; - }); - } - - private _runAbortableTask<T>(task: (progess: InjectedScriptProgress) => Promise<T>): InjectedScriptPoll<T> { - let unsentLog: LogEntry[] = []; - let takeNextLogsCallback: ((logs: LogEntry[]) => void) | undefined; - let taskFinished = false; - const logReady = () => { - if (!takeNextLogsCallback) - return; - takeNextLogsCallback(unsentLog); - unsentLog = []; - takeNextLogsCallback = undefined; - }; - - const takeNextLogs = () => new Promise<LogEntry[]>(fulfill => { - takeNextLogsCallback = fulfill; - if (unsentLog.length || taskFinished) - logReady(); - }); - - let lastMessage = ''; - const progress: InjectedScriptProgress = { - injectedScript: this, - aborted: false, - continuePolling: Symbol('continuePolling'), - log: (message: string) => { - lastMessage = message; - unsentLog.push({ message }); - logReady(); - }, - logRepeating: (message: string) => { - if (message !== lastMessage) - progress.log(message); - }, - }; - - const run = () => { - const result = task(progress); - - // After the task has finished, there should be no more logs. - // Release any pending `takeNextLogs` call, and do not block any future ones. - // This prevents non-finished protocol evaluation calls and memory leaks. - result.finally(() => { - taskFinished = true; - logReady(); - }); - - return result; - }; - - return { - takeNextLogs, - run, - cancel: () => { progress.aborted = true; }, - takeLastLogs: () => unsentLog, - }; - } - getElementBorderWidth(node: Node): { left: number; top: number; } { if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView) return { left: 0, top: 0 }; @@ -581,65 +477,73 @@ export class InjectedScript { return element; } - waitForElementStatesAndPerformAction<T>(node: Node, states: ElementState[], force: boolean | undefined, - callback: (node: Node, progress: InjectedScriptProgress) => T | symbol): InjectedScriptPoll<T | 'error:notconnected'> { + async checkElementStates(node: Node, states: ElementState[]): Promise<'error:notconnected' | { missingState: ElementState } | undefined> { + if (states.includes('stable')) { + const stableResult = await this._checkElementIsStable(node); + if (stableResult === false) + return { missingState: 'stable' }; + if (stableResult === 'error:notconnected') + return stableResult; + } + for (const state of states) { + if (state !== 'stable') { + const result = this.elementState(node, state); + if (result === false) + return { missingState: state }; + if (result === 'error:notconnected') + return result; + } + } + } + + private async _checkElementIsStable(node: Node): Promise<'error:notconnected' | boolean> { + const continuePolling = Symbol('continuePolling'); let lastRect: { x: number, y: number, width: number, height: number } | undefined; - let counter = 0; - let samePositionCounter = 0; + let stableRafCounter = 0; let lastTime = 0; - return this.pollRaf(progress => { - if (force) { - progress.log(` forcing action`); - return callback(node, progress); + const check = () => { + const element = this.retarget(node, 'no-follow-label'); + if (!element) + return 'error:notconnected'; + + // Drop frames that are shorter than 16ms - WebKit Win bug. + const time = performance.now(); + if (this._stableRafCount > 1 && time - lastTime < 15) + return continuePolling; + lastTime = time; + + const clientRect = element.getBoundingClientRect(); + const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height }; + if (lastRect) { + const samePosition = rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height; + if (!samePosition) + return false; + if (++stableRafCounter >= this._stableRafCount) + return true; } + lastRect = rect; + return continuePolling; + }; - for (const state of states) { - if (state !== 'stable') { - const result = this.elementState(node, state); - if (typeof result !== 'boolean') - return result; - if (!result) { - progress.logRepeating(` element is not ${state} - waiting...`); - return progress.continuePolling; - } - continue; - } + let fulfill: (result: 'error:notconnected' | boolean) => void; + let reject: (error: Error) => void; + const result = new Promise<'error:notconnected' | boolean>((f, r) => { fulfill = f; reject = r; }); - const element = this.retarget(node, 'no-follow-label'); - if (!element) - return 'error:notconnected'; - - // First raf happens in the same animation frame as evaluation, so it does not produce - // any client rect difference compared to synchronous call. We skip the synchronous call - // and only force layout during actual rafs as a small optimisation. - if (++counter === 1) - return progress.continuePolling; - - // Drop frames that are shorter than 16ms - WebKit Win bug. - const time = performance.now(); - if (this._stableRafCount > 1 && time - lastTime < 15) - return progress.continuePolling; - lastTime = time; - - const clientRect = element.getBoundingClientRect(); - const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height }; - const samePosition = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height; - if (samePosition) - ++samePositionCounter; + const raf = () => { + try { + const success = check(); + if (success !== continuePolling) + fulfill(success); else - samePositionCounter = 0; - const isStable = samePositionCounter >= this._stableRafCount; - const isStableForLogs = isStable || !lastRect; - lastRect = rect; - if (!isStableForLogs) - progress.logRepeating(` element is not stable - waiting...`); - if (!isStable) - return progress.continuePolling; + requestAnimationFrame(raf); + } catch (e) { + reject(e); } + }; + requestAnimationFrame(raf); - return callback(node, progress); - }); + return result; } elementState(node: Node, state: ElementStateWithoutStable): boolean | 'error:notconnected' { @@ -675,9 +579,7 @@ export class InjectedScript { throw this.createStacklessError(`Unexpected element state "${state}"`); } - selectOptions(optionsToSelect: (Node | { valueOrLabel?: string, value?: string, label?: string, index?: number })[], - node: Node, progress: InjectedScriptProgress): string[] | 'error:notconnected' | symbol { - + selectOptions(node: Node, optionsToSelect: (Node | { valueOrLabel?: string, value?: string, label?: string, index?: number })[]): string[] | 'error:notconnected' | 'error:optionsnotfound' { const element = this.retarget(node, 'follow-label'); if (!element) return 'error:notconnected'; @@ -713,19 +615,16 @@ export class InjectedScript { break; } } - if (remainingOptionsToSelect.length) { - progress.logRepeating(' did not find some options - waiting... '); - return progress.continuePolling; - } + if (remainingOptionsToSelect.length) + return 'error:optionsnotfound'; select.value = undefined as any; selectedOptions.forEach(option => option.selected = true); - progress.log(' selected specified option(s)'); select.dispatchEvent(new Event('input', { bubbles: true, composed: true })); select.dispatchEvent(new Event('change', { bubbles: true })); return selectedOptions.map(option => option.value); } - fill(value: string, node: Node, progress: InjectedScriptProgress): 'error:notconnected' | 'needsinput' | 'done' { + fill(node: Node, value: string): 'error:notconnected' | 'needsinput' | 'done' { const element = this.retarget(node, 'follow-label'); if (!element) return 'error:notconnected'; @@ -734,10 +633,8 @@ export class InjectedScript { const type = input.type.toLowerCase(); const kInputTypesToSetValue = new Set(['color', 'date', 'time', 'datetime-local', 'month', 'range', 'week']); const kInputTypesToTypeInto = new Set(['', 'email', 'number', 'password', 'search', 'tel', 'text', 'url']); - if (!kInputTypesToTypeInto.has(type) && !kInputTypesToSetValue.has(type)) { - progress.log(` input of type "${type}" cannot be filled`); + if (!kInputTypesToTypeInto.has(type) && !kInputTypesToSetValue.has(type)) throw this.createStacklessError(`Input of type "${type}" cannot be filled`); - } if (type === 'number') { value = value.trim(); if (isNaN(Number(value))) @@ -1104,7 +1001,7 @@ export class InjectedScript { strictModeViolationError(selector: ParsedSelector, matches: Element[]): Error { const infos = matches.slice(0, 10).map(m => ({ preview: this.previewNode(m), - selector: this.generateSelector(m), + selector: this.generateSelectorSimple(m), })); const lines = infos.map((info, i) => `\n ${i + 1}) ${info.preview} aka ${asLocator(this._sdkLanguage, info.selector)}`); if (infos.length < matches.length) @@ -1125,6 +1022,10 @@ export class InjectedScript { return error; } + createHighlight() { + return new Highlight(this); + } + maskSelectors(selectors: ParsedSelector[], color: string) { if (this._highlight) this.hideHighlight(); @@ -1493,7 +1394,7 @@ function createTextMatcher(selector: string, internal: boolean): { matcher: Text selector = normalizeWhiteSpace(selector); if (strict) { if (internal) - return { kind: 'strict', matcher: (elementText: ElementText) => normalizeWhiteSpace(elementText.full) === selector }; + return { kind: 'strict', matcher: (elementText: ElementText) => elementText.normalized === selector }; const strictTextNodeMatcher = (elementText: ElementText) => { if (!selector && !elementText.immediate.length) @@ -1503,7 +1404,7 @@ function createTextMatcher(selector: string, internal: boolean): { matcher: Text return { matcher: strictTextNodeMatcher, kind: 'strict' }; } selector = selector.toLowerCase(); - return { kind: 'lax', matcher: (elementText: ElementText) => normalizeWhiteSpace(elementText.full).toLowerCase().includes(selector) }; + return { kind: 'lax', matcher: (elementText: ElementText) => elementText.normalized.toLowerCase().includes(selector) }; } class ExpectedTextMatcher { diff --git a/playwright/packages/playwright-core/src/server/injected/recorder/DEPS.list b/playwright/packages/playwright-core/src/server/injected/recorder/DEPS.list new file mode 100644 index 0000000000..ee39467fea --- /dev/null +++ b/playwright/packages/playwright-core/src/server/injected/recorder/DEPS.list @@ -0,0 +1,4 @@ +# Recorder must use any external dependencies through InjectedScript. +# Otherwise it will end up with a copy of all modules it uses, and any +# module-level globals will be duplicated, which leads to subtle bugs. +[*] diff --git a/playwright/packages/playwright-core/src/server/injected/recorder.ts b/playwright/packages/playwright-core/src/server/injected/recorder/recorder.ts similarity index 80% rename from playwright/packages/playwright-core/src/server/injected/recorder.ts rename to playwright/packages/playwright-core/src/server/injected/recorder/recorder.ts index 0037e7406c..af704f0f07 100644 --- a/playwright/packages/playwright-core/src/server/injected/recorder.ts +++ b/playwright/packages/playwright-core/src/server/injected/recorder/recorder.ts @@ -14,17 +14,12 @@ * limitations under the License. */ -import type * as actions from '../recorder/recorderActions'; -import type { InjectedScript } from '../injected/injectedScript'; -import { generateSelector } from '../injected/selectorGenerator'; -import type { Point } from '../../common/types'; +import type * as actions from '../../recorder/recorderActions'; +import type { InjectedScript } from '../injectedScript'; +import type { Point } from '../../../common/types'; import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes'; -import { Highlight, type HighlightOptions } from '../injected/highlight'; -import { isInsideScope } from './domUtils'; -import { elementText } from './selectorUtils'; -import type { ElementText } from './selectorUtils'; -import { asLocator } from '../../utils/isomorphic/locatorGenerators'; -import { normalizeWhiteSpace } from '@isomorphic/stringUtils'; +import type { ElementText } from '../selectorUtils'; +import type { Highlight, HighlightOptions } from '../highlight'; interface RecorderDelegate { performAction?(action: actions.Action): Promise<void>; @@ -40,6 +35,7 @@ interface RecorderTool { cursor(): string; cleanup?(): void; onClick?(event: MouseEvent): void; + onContextMenu?(event: MouseEvent): void; onDragStart?(event: DragEvent): void; onInput?(event: Event): void; onKeyDown?(event: KeyboardEvent): void; @@ -65,6 +61,7 @@ class InspectTool implements RecorderTool { private _recorder: Recorder; private _hoveredModel: HighlightModel | null = null; private _hoveredElement: HTMLElement | null = null; + private _hoveredSelectors: string[] | null = null; private _assertVisibility: boolean; constructor(recorder: Recorder, assertVisibility: boolean) { @@ -79,22 +76,31 @@ class InspectTool implements RecorderTool { cleanup() { this._hoveredModel = null; this._hoveredElement = null; + this._hoveredSelectors = null; } onClick(event: MouseEvent) { consumeEvent(event); - if (this._assertVisibility) { - if (this._hoveredModel?.selector) { - this._recorder.delegate.recordAction?.({ - name: 'assertVisible', - selector: this._hoveredModel.selector, - signals: [], - }); - this._recorder.delegate.setMode?.('recording'); - this._recorder.overlay?.flashToolSucceeded('assertingVisibility'); - } - } else { - this._recorder.delegate.setSelector?.(this._hoveredModel ? this._hoveredModel.selector : ''); + if (event.button !== 0) + return; + if (this._hoveredModel?.selector) + this._commit(this._hoveredModel.selector); + } + + onContextMenu(event: MouseEvent) { + if (this._hoveredModel && !this._hoveredModel.tooltipListItemSelected + && this._hoveredSelectors && this._hoveredSelectors.length > 1) { + consumeEvent(event); + const selectors = this._hoveredSelectors; + this._hoveredModel.tooltipFooter = undefined; + this._hoveredModel.tooltipList = selectors.map(selector => this._recorder.injectedScript.utils.asLocator(this._recorder.state.language, selector)); + this._hoveredModel.tooltipListItemSelected = (index: number | undefined) => { + if (index === undefined) + this._reset(true); + else + this._commit(selectors[index]); + }; + this._recorder.updateHighlight(this._hoveredModel, true); } } @@ -122,11 +128,26 @@ class InspectTool implements RecorderTool { if (this._hoveredElement === target) return; this._hoveredElement = target; - const model = this._hoveredElement ? generateSelector(this._recorder.injectedScript, this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null; + + let model: HighlightModel | null = null; + let selectors: string[] = []; + if (this._hoveredElement) { + const generated = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName, multiple: false }); + selectors = generated.selectors; + model = { + selector: generated.selector, + elements: generated.elements, + tooltipText: this._recorder.injectedScript.utils.asLocator(this._recorder.state.language, generated.selector), + tooltipFooter: selectors.length > 1 ? `Click to select, right-click for more options` : undefined, + color: this._assertVisibility ? '#8acae480' : undefined, + }; + } + if (this._hoveredModel?.selector === model?.selector) return; this._hoveredModel = model; - this._recorder.updateHighlight(model, true, { color: this._assertVisibility ? '#8acae480' : undefined }); + this._hoveredSelectors = selectors; + this._recorder.updateHighlight(model, true); } onMouseEnter(event: MouseEvent) { @@ -137,17 +158,18 @@ class InspectTool implements RecorderTool { consumeEvent(event); const window = this._recorder.injectedScript.window; // Leaving iframe. - if (window.top !== window && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) { - this._hoveredElement = null; - this._hoveredModel = null; - this._recorder.updateHighlight(null, true); - } + if (window.top !== window && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) + this._reset(true); } onKeyDown(event: KeyboardEvent) { consumeEvent(event); - if (this._assertVisibility && event.key === 'Escape') - this._recorder.delegate.setMode?.('recording'); + if (event.key === 'Escape') { + if (this._hoveredModel?.tooltipListItemSelected) + this._reset(true); + else if (this._assertVisibility) + this._recorder.delegate.setMode?.('recording'); + } } onKeyUp(event: KeyboardEvent) { @@ -155,9 +177,28 @@ class InspectTool implements RecorderTool { } onScroll(event: Event) { + this._reset(false); + } + + private _commit(selector: string) { + if (this._assertVisibility) { + this._recorder.delegate.recordAction?.({ + name: 'assertVisible', + selector, + signals: [], + }); + this._recorder.delegate.setMode?.('recording'); + this._recorder.overlay?.flashToolSucceeded('assertingVisibility'); + } else { + this._recorder.delegate.setSelector?.(selector); + } + } + + private _reset(userGesture: boolean) { this._hoveredElement = null; this._hoveredModel = null; - this._recorder.updateHighlight(null, false); + this._hoveredSelectors = null; + this._recorder.updateHighlight(null, userGesture); } } @@ -185,6 +226,10 @@ class RecordActionTool implements RecorderTool { } onClick(event: MouseEvent) { + // in webkit, sliding a range element may trigger a click event with a different target if the mouse is released outside the element bounding box. + // So we check the hovered element instead, and if it is a range input, we skip click handling + if (isRangeInput(this._hoveredElement)) + return; if (this._shouldIgnoreMouseEvent(event)) return; if (this._actionInProgress(event)) @@ -277,6 +322,17 @@ class RecordActionTool implements RecorderTool { return; } + if (isRangeInput(target)) { + this._recorder.delegate.recordAction?.({ + name: 'fill', + // must use hoveredModel instead of activeModel for it to work in webkit + selector: this._hoveredModel!.selector, + signals: [], + text: target.value, + }); + return; + } + if (['INPUT', 'TEXTAREA'].includes(target.nodeName) || target.isContentEditable) { if (target.nodeName === 'INPUT' && ['checkbox', 'radio'].includes((target as HTMLInputElement).type.toLowerCase())) { // Checkbox is handled in click, we can't let input trigger on checkbox - that would mean we dispatched click events while recording. @@ -362,7 +418,7 @@ class RecordActionTool implements RecorderTool { // We'd like to ignore this stray event. if (userGesture && activeElement === this._recorder.document.body) return; - const result = activeElement ? generateSelector(this._recorder.injectedScript, activeElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null; + const result = activeElement ? this._recorder.injectedScript.generateSelector(activeElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null; this._activeModel = result && result.selector ? result : null; if (userGesture) this._hoveredElement = activeElement as HTMLElement | null; @@ -374,7 +430,7 @@ class RecordActionTool implements RecorderTool { const nodeName = target.nodeName; if (nodeName === 'SELECT' || nodeName === 'OPTION') return true; - if (nodeName === 'INPUT' && ['date'].includes((target as HTMLInputElement).type)) + if (nodeName === 'INPUT' && ['date', 'range'].includes((target as HTMLInputElement).type)) return true; return false; } @@ -459,11 +515,11 @@ class RecordActionTool implements RecorderTool { this._recorder.updateHighlight(null, true); return; } - const { selector, elements } = generateSelector(this._recorder.injectedScript, this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }); + const { selector, elements } = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }); if (this._hoveredModel && this._hoveredModel.selector === selector) return; - this._hoveredModel = selector ? { selector, elements } : null; - this._recorder.updateHighlight(this._hoveredModel, true, { color: '#dc6f6f7f' }); + this._hoveredModel = selector ? { selector, elements, color: '#dc6f6f7f' } : null; + this._recorder.updateHighlight(this._hoveredModel, true); } } @@ -507,12 +563,7 @@ class TextAssertionTool implements RecorderTool { onClick(event: MouseEvent) { consumeEvent(event); if (this._kind === 'value') { - const action = this._generateAction(); - if (action) { - this._recorder.delegate.recordAction?.(action); - this._recorder.delegate.setMode?.('recording'); - this._recorder.overlay?.flashToolSucceeded('assertingValue'); - } + this._commitAssertValue(); } else { if (!this._dialogElement) this._showDialog(); @@ -525,6 +576,15 @@ class TextAssertionTool implements RecorderTool { event.preventDefault(); } + onPointerUp(event: PointerEvent) { + const target = this._hoverHighlight?.elements[0]; + if (this._kind === 'value' && target && target.nodeName === 'INPUT' && (target as HTMLInputElement).disabled) { + // Click on a disabled input does not produce a "click" event, but we still want + // to assert the value. + this._commitAssertValue(); + } + } + onMouseMove(event: MouseEvent) { if (this._dialogElement) return; @@ -532,10 +592,12 @@ class TextAssertionTool implements RecorderTool { if (this._hoverHighlight?.elements[0] === target) return; if (this._kind === 'text') - this._hoverHighlight = elementText(this._textCache, target).full ? { elements: [target], selector: '' } : null; + this._hoverHighlight = this._recorder.injectedScript.utils.elementText(this._textCache, target).full ? { elements: [target], selector: '' } : null; else - this._hoverHighlight = this._elementHasValue(target) ? generateSelector(this._recorder.injectedScript, target, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null; - this._recorder.updateHighlight(this._hoverHighlight, true, { color: '#8acae480' }); + this._hoverHighlight = this._elementHasValue(target) ? this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null; + if (this._hoverHighlight) + this._hoverHighlight.color = '#8acae480'; + this._recorder.updateHighlight(this._hoverHighlight, true); } onKeyDown(event: KeyboardEvent) { @@ -545,7 +607,7 @@ class TextAssertionTool implements RecorderTool { } onScroll(event: Event) { - this._recorder.updateHighlight(this._hoverHighlight, false, { color: '#8acae480' }); + this._recorder.updateHighlight(this._hoverHighlight, false); } private _elementHasValue(element: Element) { @@ -560,7 +622,7 @@ class TextAssertionTool implements RecorderTool { if (this._kind === 'value') { if (!this._elementHasValue(target)) return null; - const { selector } = generateSelector(this._recorder.injectedScript, target, { testIdAttributeName: this._recorder.state.testIdAttributeName }); + const { selector } = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName }); if (target.nodeName === 'INPUT' && ['checkbox', 'radio'].includes((target as HTMLInputElement).type.toLowerCase())) { return { name: 'assertChecked', @@ -578,15 +640,16 @@ class TextAssertionTool implements RecorderTool { }; } } else { - this._hoverHighlight = generateSelector(this._recorder.injectedScript, target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true }); + this._hoverHighlight = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true }); + this._hoverHighlight.color = '#8acae480'; // forTextExpect can update the target, re-highlight it. - this._recorder.updateHighlight(this._hoverHighlight, true, { color: '#8acae480' }); + this._recorder.updateHighlight(this._hoverHighlight, true); return { name: 'assertText', selector: this._hoverHighlight.selector, signals: [], - text: normalizeWhiteSpace(elementText(this._textCache, target).full), + text: this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized, substring: true, }; } @@ -594,7 +657,7 @@ class TextAssertionTool implements RecorderTool { private _renderValue(action: actions.Action) { if (action?.name === 'assertText') - return normalizeWhiteSpace(action.text); + return this._recorder.injectedScript.utils.normalizeWhiteSpace(action.text); if (action?.name === 'assertChecked') return String(action.checked); if (action?.name === 'assertValue') @@ -649,12 +712,12 @@ class TextAssertionTool implements RecorderTool { textElement.classList.add('text-editor'); const updateAndValidate = () => { - const newValue = normalizeWhiteSpace(textElement.value); + const newValue = this._recorder.injectedScript.utils.normalizeWhiteSpace(textElement.value); const target = this._hoverHighlight?.elements[0]; if (!target) return; action.text = newValue; - const targetText = normalizeWhiteSpace(elementText(this._textCache, target).full); + const targetText = this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized; const matches = newValue && targetText.includes(newValue); textElement.classList.toggle('does-not-match', !matches); }; @@ -676,11 +739,24 @@ class TextAssertionTool implements RecorderTool { this._recorder.document.removeEventListener('keydown', this._keyboardListener!); this._dialogElement = null; } + + private _commitAssertValue() { + if (this._kind !== 'value') + return; + const action = this._generateAction(); + if (!action) + return; + this._recorder.delegate.recordAction?.(action); + this._recorder.delegate.setMode?.('recording'); + this._recorder.overlay?.flashToolSucceeded('assertingValue'); + } } class Overlay { private _recorder: Recorder; + private _listeners: (() => void)[] = []; private _overlayElement: HTMLElement; + private _dragHandle: HTMLElement; private _recordToggle: HTMLElement; private _pickLocatorToggle: HTMLElement; private _assertVisibilityToggle: HTMLElement; @@ -698,81 +774,89 @@ class Overlay { const toolsListElement = document.createElement('x-pw-tools-list'); this._overlayElement.appendChild(toolsListElement); - const dragHandle = document.createElement('x-pw-tool-gripper'); - dragHandle.addEventListener('mousedown', event => { - this._dragState = { offsetX: this._offsetX, dragStart: { x: event.clientX, y: 0 } }; - }); - dragHandle.appendChild(document.createElement('x-div')); - toolsListElement.appendChild(dragHandle); + this._dragHandle = document.createElement('x-pw-tool-gripper'); + this._dragHandle.appendChild(document.createElement('x-div')); + toolsListElement.appendChild(this._dragHandle); this._recordToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item'); this._recordToggle.title = 'Record'; this._recordToggle.classList.add('record'); this._recordToggle.appendChild(this._recorder.injectedScript.document.createElement('x-div')); - this._recordToggle.addEventListener('click', () => { - this._recorder.delegate.setMode?.(this._recorder.state.mode === 'none' || this._recorder.state.mode === 'standby' || this._recorder.state.mode === 'inspecting' ? 'recording' : 'standby'); - }); toolsListElement.appendChild(this._recordToggle); this._pickLocatorToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item'); this._pickLocatorToggle.title = 'Pick locator'; this._pickLocatorToggle.classList.add('pick-locator'); this._pickLocatorToggle.appendChild(this._recorder.injectedScript.document.createElement('x-div')); - this._pickLocatorToggle.addEventListener('click', () => { - const newMode: Record<Mode, Mode> = { - 'inspecting': 'standby', - 'none': 'inspecting', - 'standby': 'inspecting', - 'recording': 'recording-inspecting', - 'recording-inspecting': 'recording', - 'assertingText': 'recording-inspecting', - 'assertingVisibility': 'recording-inspecting', - 'assertingValue': 'recording-inspecting', - }; - this._recorder.delegate.setMode?.(newMode[this._recorder.state.mode]); - }); toolsListElement.appendChild(this._pickLocatorToggle); this._assertVisibilityToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item'); this._assertVisibilityToggle.title = 'Assert visibility'; this._assertVisibilityToggle.classList.add('visibility'); this._assertVisibilityToggle.appendChild(this._recorder.injectedScript.document.createElement('x-div')); - this._assertVisibilityToggle.addEventListener('click', () => { - if (!this._assertVisibilityToggle.classList.contains('disabled')) - this._recorder.delegate.setMode?.(this._recorder.state.mode === 'assertingVisibility' ? 'recording' : 'assertingVisibility'); - }); toolsListElement.appendChild(this._assertVisibilityToggle); this._assertTextToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item'); this._assertTextToggle.title = 'Assert text'; this._assertTextToggle.classList.add('text'); this._assertTextToggle.appendChild(this._recorder.injectedScript.document.createElement('x-div')); - this._assertTextToggle.addEventListener('click', () => { - if (!this._assertTextToggle.classList.contains('disabled')) - this._recorder.delegate.setMode?.(this._recorder.state.mode === 'assertingText' ? 'recording' : 'assertingText'); - }); toolsListElement.appendChild(this._assertTextToggle); this._assertValuesToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item'); this._assertValuesToggle.title = 'Assert value'; this._assertValuesToggle.classList.add('value'); this._assertValuesToggle.appendChild(this._recorder.injectedScript.document.createElement('x-div')); - this._assertValuesToggle.addEventListener('click', () => { - if (!this._assertValuesToggle.classList.contains('disabled')) - this._recorder.delegate.setMode?.(this._recorder.state.mode === 'assertingValue' ? 'recording' : 'assertingValue'); - }); toolsListElement.appendChild(this._assertValuesToggle); this._updateVisualPosition(); + this._refreshListeners(); + } + + private _refreshListeners() { + removeEventListeners(this._listeners); + this._listeners = [ + addEventListener(this._dragHandle, 'mousedown', event => { + this._dragState = { offsetX: this._offsetX, dragStart: { x: (event as MouseEvent).clientX, y: 0 } }; + }), + addEventListener(this._recordToggle, 'click', () => { + this._recorder.delegate.setMode?.(this._recorder.state.mode === 'none' || this._recorder.state.mode === 'standby' || this._recorder.state.mode === 'inspecting' ? 'recording' : 'standby'); + }), + addEventListener(this._pickLocatorToggle, 'click', () => { + const newMode: Record<Mode, Mode> = { + 'inspecting': 'standby', + 'none': 'inspecting', + 'standby': 'inspecting', + 'recording': 'recording-inspecting', + 'recording-inspecting': 'recording', + 'assertingText': 'recording-inspecting', + 'assertingVisibility': 'recording-inspecting', + 'assertingValue': 'recording-inspecting', + }; + this._recorder.delegate.setMode?.(newMode[this._recorder.state.mode]); + }), + addEventListener(this._assertVisibilityToggle, 'click', () => { + if (!this._assertVisibilityToggle.classList.contains('disabled')) + this._recorder.delegate.setMode?.(this._recorder.state.mode === 'assertingVisibility' ? 'recording' : 'assertingVisibility'); + }), + addEventListener(this._assertTextToggle, 'click', () => { + if (!this._assertTextToggle.classList.contains('disabled')) + this._recorder.delegate.setMode?.(this._recorder.state.mode === 'assertingText' ? 'recording' : 'assertingText'); + }), + addEventListener(this._assertValuesToggle, 'click', () => { + if (!this._assertValuesToggle.classList.contains('disabled')) + this._recorder.delegate.setMode?.(this._recorder.state.mode === 'assertingValue' ? 'recording' : 'assertingValue'); + }), + ]; } install() { this._recorder.highlight.appendChild(this._overlayElement); + this._refreshListeners(); this._updateVisualPosition(); } contains(element: Element) { - return isInsideScope(this._overlayElement, element); + return this._recorder.injectedScript.utils.isInsideScope(this._overlayElement, element); } setUIState(state: UIState) { @@ -867,7 +951,7 @@ export class Recorder { constructor(injectedScript: InjectedScript) { this.document = injectedScript.document; this.injectedScript = injectedScript; - this.highlight = new Highlight(injectedScript); + this.highlight = injectedScript.createHighlight(); this._tools = { 'none': new NoneTool(), 'standby': new NoneTool(), @@ -899,6 +983,7 @@ export class Recorder { this._listeners = [ addEventListener(this.document, 'click', event => this._onClick(event as MouseEvent), true), addEventListener(this.document, 'auxclick', event => this._onClick(event as MouseEvent), true), + addEventListener(this.document, 'contextmenu', event => this._onContextMenu(event as MouseEvent), true), addEventListener(this.document, 'dragstart', event => this._onDragStart(event as DragEvent), true), addEventListener(this.document, 'input', event => this._onInput(event), true), addEventListener(this.document, 'keydown', event => this._onKeyDown(event as KeyboardEvent), true), @@ -978,6 +1063,14 @@ export class Recorder { this._currentTool.onClick?.(event); } + private _onContextMenu(event: MouseEvent) { + if (!event.isTrusted) + return; + if (this._ignoreOverlayEvent(event)) + return; + this._currentTool.onContextMenu?.(event); + } + private _onDragStart(event: DragEvent) { if (!event.isTrusted) return; @@ -1083,17 +1176,20 @@ export class Recorder { this._currentTool.onKeyUp?.(event); } - updateHighlight(model: HighlightModel | null, userGesture: boolean, options: HighlightOptions = {}) { - if (options.tooltipText === undefined && model?.selector) - options.tooltipText = asLocator(this.state.language, model.selector); - this.highlight.updateHighlight(model?.elements || [], options); + updateHighlight(model: HighlightModel | null, userGesture: boolean) { + let tooltipText = model?.tooltipText; + if (tooltipText === undefined && !model?.tooltipList && model?.selector) + tooltipText = this.injectedScript.utils.asLocator(this.state.language, model.selector); + this.highlight.updateHighlight(model?.elements || [], { ...model, tooltipText }); if (userGesture) this.delegate.highlightUpdated?.(); } private _ignoreOverlayEvent(event: Event) { - const target = event.composedPath()[0] as Element; - return target.nodeName.toLowerCase() === 'x-pw-glass'; + return event.composedPath().some(e => { + const nodeName = (e as Element).nodeName || ''; + return nodeName.toLowerCase() === 'x-pw-glass'; + }); } deepEventTarget(event: Event): HTMLElement { @@ -1141,7 +1237,7 @@ function consumeEvent(e: Event) { e.stopImmediatePropagation(); } -type HighlightModel = { +type HighlightModel = HighlightOptions & { selector: string; elements: Element[]; }; @@ -1153,6 +1249,13 @@ function asCheckbox(node: Node | null): HTMLInputElement | null { return ['checkbox', 'radio'].includes(inputElement.type) ? inputElement : null; } +function isRangeInput(node: Node | null): node is HTMLInputElement { + if (!node || node.nodeName !== 'INPUT') + return false; + const inputElement = node as HTMLInputElement; + return inputElement.type.toLowerCase() === 'range'; +} + function addEventListener(target: EventTarget, eventName: string, listener: EventListener, useCapture?: boolean): () => void { target.addEventListener(eventName, listener, useCapture); const remove = () => { diff --git a/playwright/packages/playwright-core/src/server/injected/roleSelectorEngine.ts b/playwright/packages/playwright-core/src/server/injected/roleSelectorEngine.ts index b647f81b8e..56643199e5 100644 --- a/playwright/packages/playwright-core/src/server/injected/roleSelectorEngine.ts +++ b/playwright/packages/playwright-core/src/server/injected/roleSelectorEngine.ts @@ -16,9 +16,10 @@ import type { SelectorEngine, SelectorRoot } from './selectorEngine'; import { matchesAttributePart } from './selectorUtils'; -import { beginAriaCaches, endAriaCaches, getAriaChecked, getAriaDisabled, getAriaExpanded, getAriaLevel, getAriaPressed, getAriaRole, getAriaSelected, getElementAccessibleName, isElementHiddenForAria, kAriaCheckedRoles, kAriaExpandedRoles, kAriaLevelRoles, kAriaPressedRoles, kAriaSelectedRoles } from './roleUtils'; +import { beginAriaCaches, endAriaCaches, getAriaChecked, getAriaDisabled, getAriaExpanded, getAriaLevel, getAriaPressed, getAriaSelected, getElementAccessibleName, getElementsByRole, isElementHiddenForAria, kAriaCheckedRoles, kAriaExpandedRoles, kAriaLevelRoles, kAriaPressedRoles, kAriaSelectedRoles } from './roleUtils'; import { parseAttributeSelector, type AttributeSelectorPart, type AttributeSelectorOperator } from '../../utils/isomorphic/selectorParser'; import { normalizeWhiteSpace } from '../../utils/isomorphic/stringUtils'; +import { isInsideScope } from './domUtils'; type RoleEngineOptions = { role: string; @@ -125,26 +126,27 @@ function validateAttributes(attrs: AttributeSelectorPart[], role: string): RoleE } function queryRole(scope: SelectorRoot, options: RoleEngineOptions, internal: boolean): Element[] { - const result: Element[] = []; - const match = (element: Element) => { - if (getAriaRole(element) !== options.role) - return; + const doc = scope.nodeType === 9 /* Node.DOCUMENT_NODE */ ? scope as Document : scope.ownerDocument; + const elements = doc ? getElementsByRole(doc, options.role) : []; + return elements.filter(element => { + if (!isInsideScope(scope, element)) + return false; if (options.selected !== undefined && getAriaSelected(element) !== options.selected) - return; + return false; if (options.checked !== undefined && getAriaChecked(element) !== options.checked) - return; + return false; if (options.pressed !== undefined && getAriaPressed(element) !== options.pressed) - return; + return false; if (options.expanded !== undefined && getAriaExpanded(element) !== options.expanded) - return; + return false; if (options.level !== undefined && getAriaLevel(element) !== options.level) - return; + return false; if (options.disabled !== undefined && getAriaDisabled(element) !== options.disabled) - return; + return false; if (!options.includeHidden) { const isHidden = isElementHiddenForAria(element); if (isHidden) - return; + return false; } if (options.name !== undefined) { // Always normalize whitespace in the accessible name. @@ -155,25 +157,10 @@ function queryRole(scope: SelectorRoot, options: RoleEngineOptions, internal: bo if (internal && !options.exact && options.nameOp === '=') options.nameOp = '*='; if (!matchesAttributePart(accessibleName, { name: '', jsonPath: [], op: options.nameOp || '=', value: options.name, caseSensitive: !!options.exact })) - return; + return false; } - result.push(element); - }; - - const query = (root: Element | ShadowRoot | Document) => { - const shadows: ShadowRoot[] = []; - if ((root as Element).shadowRoot) - shadows.push((root as Element).shadowRoot!); - for (const element of root.querySelectorAll('*')) { - match(element); - if (element.shadowRoot) - shadows.push(element.shadowRoot); - } - shadows.forEach(query); - }; - - query(scope); - return result; + return true; + }); } export function createRoleEngine(internal: boolean): SelectorEngine { diff --git a/playwright/packages/playwright-core/src/server/injected/roleUtils.ts b/playwright/packages/playwright-core/src/server/injected/roleUtils.ts index 05ba3fda2d..a42b60233e 100644 --- a/playwright/packages/playwright-core/src/server/injected/roleUtils.ts +++ b/playwright/packages/playwright-core/src/server/injected/roleUtils.ts @@ -323,7 +323,18 @@ function queryInAriaOwned(element: Element, selector: string): Element[] { return result; } -function getPseudoContent(pseudoStyle: CSSStyleDeclaration | undefined) { +function getPseudoContent(element: Element, pseudo: '::before' | '::after') { + const cache = pseudo === '::before' ? cachePseudoContentBefore : cachePseudoContentAfter; + if (cache?.has(element)) + return cache?.get(element) || ''; + const pseudoStyle = getElementComputedStyle(element, pseudo); + const content = getPseudoContentImpl(pseudoStyle); + if (cache) + cache.set(element, content); + return content; +} + +function getPseudoContentImpl(pseudoStyle: CSSStyleDeclaration | undefined) { if (!pseudoStyle) return ''; const content = pseudoStyle.content; @@ -677,7 +688,7 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN tokens.push(node.textContent || ''); } }; - tokens.push(getPseudoContent(getElementComputedStyle(element, '::before'))); + tokens.push(getPseudoContent(element, '::before')); const assignedNodes = element.nodeName === 'SLOT' ? (element as HTMLSlotElement).assignedNodes() : []; if (assignedNodes.length) { for (const child of assignedNodes) @@ -692,7 +703,7 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN for (const owned of getIdRefs(element, element.getAttribute('aria-owns'))) visit(owned, true); } - tokens.push(getPseudoContent(getElementComputedStyle(element, '::after'))); + tokens.push(getPseudoContent(element, '::after')); const accessibleName = tokens.join(''); if (accessibleName.trim()) return accessibleName; @@ -834,9 +845,51 @@ function getAccessibleNameFromAssociatedLabels(labels: Iterable<HTMLLabelElement })).filter(accessibleName => !!accessibleName).join(' '); } +export function getElementsByRole(document: Document, role: string): Element[] { + if (document === cacheElementsByRoleDocument) + return cacheElementsByRole!.get(role) || []; + const map = calculateElementsByRoleMap(document); + if (cachesCounter) { + cacheElementsByRoleDocument = document; + cacheElementsByRole = map; + } + return map.get(role) || []; +} + +function calculateElementsByRoleMap(document: Document) { + const result = new Map<string, Element[]>(); + + const visit = (root: Element | ShadowRoot | Document) => { + const shadows: ShadowRoot[] = []; + if ((root as Element).shadowRoot) + shadows.push((root as Element).shadowRoot!); + for (const element of root.querySelectorAll('*')) { + const role = getAriaRole(element); + if (role) { + let list = result.get(role); + if (!list) { + list = []; + result.set(role, list); + } + list.push(element); + } + if (element.shadowRoot) + shadows.push(element.shadowRoot); + } + shadows.forEach(visit); + }; + visit(document); + + return result; +} + let cacheAccessibleName: Map<Element, string> | undefined; let cacheAccessibleNameHidden: Map<Element, string> | undefined; let cacheIsHidden: Map<Element, boolean> | undefined; +let cachePseudoContentBefore: Map<Element, string> | undefined; +let cachePseudoContentAfter: Map<Element, string> | undefined; +let cacheElementsByRole: Map<string, Element[]> | undefined; +let cacheElementsByRoleDocument: Document | undefined; let cachesCounter = 0; export function beginAriaCaches() { @@ -844,6 +897,8 @@ export function beginAriaCaches() { cacheAccessibleName ??= new Map(); cacheAccessibleNameHidden ??= new Map(); cacheIsHidden ??= new Map(); + cachePseudoContentBefore ??= new Map(); + cachePseudoContentAfter ??= new Map(); } export function endAriaCaches() { @@ -851,5 +906,9 @@ export function endAriaCaches() { cacheAccessibleName = undefined; cacheAccessibleNameHidden = undefined; cacheIsHidden = undefined; + cachePseudoContentBefore = undefined; + cachePseudoContentAfter = undefined; + cacheElementsByRole = undefined; + cacheElementsByRoleDocument = undefined; } } diff --git a/playwright/packages/playwright-core/src/server/injected/selectorEvaluator.ts b/playwright/packages/playwright-core/src/server/injected/selectorEvaluator.ts index 43abbcd8b4..d2f481526f 100644 --- a/playwright/packages/playwright-core/src/server/injected/selectorEvaluator.ts +++ b/playwright/packages/playwright-core/src/server/injected/selectorEvaluator.ts @@ -450,7 +450,7 @@ const textEngine: SelectorEngine = { if (args.length !== 1 || typeof args[0] !== 'string') throw new Error(`"text" engine expects a single string`); const text = normalizeWhiteSpace(args[0]).toLowerCase(); - const matcher = (elementText: ElementText) => normalizeWhiteSpace(elementText.full).toLowerCase().includes(text); + const matcher = (elementText: ElementText) => elementText.normalized.toLowerCase().includes(text); return elementMatchesText((evaluator as SelectorEvaluatorImpl)._cacheText, element, matcher) === 'self'; }, }; @@ -486,7 +486,7 @@ const hasTextEngine: SelectorEngine = { if (shouldSkipForTextMatching(element)) return false; const text = normalizeWhiteSpace(args[0]).toLowerCase(); - const matcher = (elementText: ElementText) => normalizeWhiteSpace(elementText.full).toLowerCase().includes(text); + const matcher = (elementText: ElementText) => elementText.normalized.toLowerCase().includes(text); return matcher(elementText((evaluator as SelectorEvaluatorImpl)._cacheText, element)); }, }; diff --git a/playwright/packages/playwright-core/src/server/injected/selectorGenerator.ts b/playwright/packages/playwright-core/src/server/injected/selectorGenerator.ts index edeb2b5b8e..b88b25acf4 100644 --- a/playwright/packages/playwright-core/src/server/injected/selectorGenerator.ts +++ b/playwright/packages/playwright-core/src/server/injected/selectorGenerator.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { cssEscape, escapeForAttributeSelector, escapeForTextSelector, normalizeWhiteSpace, quoteCSSAttributeValue } from '../../utils/isomorphic/stringUtils'; +import { cssEscape, escapeForAttributeSelector, escapeForTextSelector, escapeRegExp, quoteCSSAttributeValue } from '../../utils/isomorphic/stringUtils'; import { closestCrossShadow, isInsideScope, parentElementOrShadowHost } from './domUtils'; import type { InjectedScript } from './injectedScript'; import { getAriaRole, getElementAccessibleName, beginAriaCaches, endAriaCaches } from './roleUtils'; @@ -67,17 +67,18 @@ export type GenerateSelectorOptions = { omitInternalEngines?: boolean; root?: Element | Document; forTextExpect?: boolean; + multiple?: boolean; }; -export function generateSelector(injectedScript: InjectedScript, targetElement: Element, options: GenerateSelectorOptions): { selector: string, elements: Element[] } { +export function generateSelector(injectedScript: InjectedScript, targetElement: Element, options: GenerateSelectorOptions): { selector: string, selectors: string[], elements: Element[] } { injectedScript._evaluator.begin(); beginAriaCaches(); try { - let targetTokens: SelectorToken[]; + let selectors: string[] = []; if (options.forTextExpect) { - targetTokens = cssFallback(injectedScript, targetElement.ownerDocument.documentElement, options); + let targetTokens = cssFallback(injectedScript, targetElement.ownerDocument.documentElement, options); for (let element: Element | undefined = targetElement; element; element = parentElementOrShadowHost(element)) { - const tokens = generateSelectorFor(injectedScript, element, options); + const tokens = generateSelectorFor(injectedScript, element, { ...options, noText: true }); if (!tokens) continue; const score = combineScores(tokens); @@ -86,14 +87,41 @@ export function generateSelector(injectedScript: InjectedScript, targetElement: break; } } + selectors = [joinTokens(targetTokens)]; } else { targetElement = closestCrossShadow(targetElement, 'button,select,input,[role=button],[role=checkbox],[role=radio],a,[role=link]', options.root) || targetElement; - targetTokens = generateSelectorFor(injectedScript, targetElement, options) || cssFallback(injectedScript, targetElement, options); + if (options.multiple) { + const withText = generateSelectorFor(injectedScript, targetElement, options); + const withoutText = generateSelectorFor(injectedScript, targetElement, { ...options, noText: true }); + let tokens = [withText, withoutText]; + + // Clear cache to re-generate without css id. + cacheAllowText.clear(); + cacheDisallowText.clear(); + + if (withText && hasCSSIdToken(withText)) + tokens.push(generateSelectorFor(injectedScript, targetElement, { ...options, noCSSId: true })); + if (withoutText && hasCSSIdToken(withoutText)) + tokens.push(generateSelectorFor(injectedScript, targetElement, { ...options, noText: true, noCSSId: true })); + + tokens = tokens.filter(Boolean); + if (!tokens.length) { + const css = cssFallback(injectedScript, targetElement, options); + tokens.push(css); + if (hasCSSIdToken(css)) + tokens.push(cssFallback(injectedScript, targetElement, { ...options, noCSSId: true })); + } + selectors = [...new Set(tokens.map(t => joinTokens(t!)))]; + } else { + const targetTokens = generateSelectorFor(injectedScript, targetElement, options) || cssFallback(injectedScript, targetElement, options); + selectors = [joinTokens(targetTokens)]; + } } - const selector = joinTokens(targetTokens); + const selector = selectors[0]; const parsedSelector = injectedScript.parseSelector(selector); return { selector, + selectors, elements: injectedScript.querySelectorAll(parsedSelector, options.root ?? targetElement.ownerDocument) }; } finally { @@ -109,7 +137,9 @@ function filterRegexTokens(textCandidates: SelectorToken[][]): SelectorToken[][] return textCandidates.filter(c => c[0].selector[0] !== '/'); } -function generateSelectorFor(injectedScript: InjectedScript, targetElement: Element, options: GenerateSelectorOptions): SelectorToken[] | null { +type InternalOptions = GenerateSelectorOptions & { noText?: boolean, noCSSId?: boolean }; + +function generateSelectorFor(injectedScript: InjectedScript, targetElement: Element, options: InternalOptions): SelectorToken[] | null { if (options.root && !isInsideScope(options.root, targetElement)) throw new Error(`Target element must belong to the root's subtree`); @@ -188,10 +218,10 @@ function generateSelectorFor(injectedScript: InjectedScript, targetElement: Elem return value; }; - return calculate(targetElement, !options.forTextExpect); + return calculate(targetElement, !options.noText); } -function buildNoTextCandidates(injectedScript: InjectedScript, element: Element, options: GenerateSelectorOptions): SelectorToken[] { +function buildNoTextCandidates(injectedScript: InjectedScript, element: Element, options: InternalOptions): SelectorToken[] { const candidates: SelectorToken[] = []; // CSS selectors are applicable to elements via locator() and iframes via frameLocator(). @@ -201,9 +231,11 @@ function buildNoTextCandidates(injectedScript: InjectedScript, element: Element, candidates.push({ engine: 'css', selector: `[${attr}=${quoteCSSAttributeValue(element.getAttribute(attr)!)}]`, score: kOtherTestIdScore }); } - const idAttr = element.getAttribute('id'); - if (idAttr && !isGuidLike(idAttr)) - candidates.push({ engine: 'css', selector: makeSelectorForId(idAttr), score: kCSSIdScore }); + if (!options.noCSSId) { + const idAttr = element.getAttribute('id'); + if (idAttr && !isGuidLike(idAttr)) + candidates.push({ engine: 'css', selector: makeSelectorForId(idAttr), score: kCSSIdScore }); + } candidates.push({ engine: 'css', selector: cssEscape(element.nodeName.toLowerCase()), score: kCSSTagNameScore }); } @@ -237,7 +269,7 @@ function buildNoTextCandidates(injectedScript: InjectedScript, element: Element, const labels = getElementLabels(injectedScript._evaluator._cacheText, element); for (const label of labels) { - const labelText = label.full.trim(); + const labelText = label.normalized; candidates.push({ engine: 'internal:label', selector: escapeForTextSelector(labelText, true), score: kLabelScoreExact }); for (const alternative of suitableTextAlternatives(labelText)) candidates.push({ engine: 'internal:label', selector: escapeForTextSelector(alternative.text, false), score: kLabelScore - alternative.scoreBouns }); @@ -281,7 +313,7 @@ function buildTextCandidates(injectedScript: InjectedScript, element: Element, i candidates.push([{ engine: 'internal:attr', selector: `[alt=${escapeForAttributeSelector(alternative.text, false)}]`, score: kAltTextScore - alternative.scoreBouns }]); } - const text = normalizeWhiteSpace(elementText(injectedScript._evaluator._cacheText, element).full); + const text = elementText(injectedScript._evaluator._cacheText, element).normalized; if (text) { const alternatives = suitableTextAlternatives(text); if (isTargetNode) { @@ -315,7 +347,11 @@ function makeSelectorForId(id: string) { return /^[a-zA-Z][a-zA-Z0-9\-\_]+$/.test(id) ? '#' + id : `[id="${cssEscape(id)}"]`; } -function cssFallback(injectedScript: InjectedScript, targetElement: Element, options: GenerateSelectorOptions): SelectorToken[] { +function hasCSSIdToken(tokens: SelectorToken[]) { + return tokens.some(token => token.engine === 'css' && (token.selector.startsWith('#') || token.selector.startsWith('[id="'))); +} + +function cssFallback(injectedScript: InjectedScript, targetElement: Element, options: InternalOptions): SelectorToken[] { const root: Node = options.root ?? targetElement.ownerDocument; const tokens: string[] = []; @@ -342,9 +378,10 @@ function cssFallback(injectedScript: InjectedScript, targetElement: Element, opt for (let element: Element | undefined = targetElement; element && element !== root; element = parentElementOrShadowHost(element)) { const nodeName = element.nodeName.toLowerCase(); - // Element ID is the strongest signal, use it. let bestTokenForLevel: string = ''; - if (element.id) { + + // Element ID is the strongest signal, use it. + if (element.id && !options.noCSSId) { const token = makeSelectorForId(element.id); const selector = uniqueCSSSelector(token); if (selector) @@ -471,11 +508,6 @@ function isGuidLike(id: string): boolean { return transitionCount >= id.length / 4; } -function escapeRegExp(s: string) { - // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - function trimWordBoundary(text: string, maxLength: number) { if (text.length <= maxLength) return text; diff --git a/playwright/packages/playwright-core/src/server/injected/selectorUtils.ts b/playwright/packages/playwright-core/src/server/injected/selectorUtils.ts index 45e27451ef..746e12af9d 100644 --- a/playwright/packages/playwright-core/src/server/injected/selectorUtils.ts +++ b/playwright/packages/playwright-core/src/server/injected/selectorUtils.ts @@ -15,6 +15,7 @@ */ import type { AttributeSelectorPart } from '../../utils/isomorphic/selectorParser'; +import { normalizeWhiteSpace } from '../../utils/isomorphic/stringUtils'; import { getAriaLabelledByElements } from './roleUtils'; export function matchesComponentAttribute(obj: any, attr: AttributeSelectorPart) { @@ -56,17 +57,17 @@ export function shouldSkipForTextMatching(element: Element | ShadowRoot) { return element.nodeName === 'SCRIPT' || element.nodeName === 'NOSCRIPT' || element.nodeName === 'STYLE' || document.head && document.head.contains(element); } -export type ElementText = { full: string, immediate: string[] }; +export type ElementText = { full: string, normalized: string, immediate: string[] }; export type TextMatcher = (text: ElementText) => boolean; export function elementText(cache: Map<Element | ShadowRoot, ElementText>, root: Element | ShadowRoot): ElementText { let value = cache.get(root); if (value === undefined) { - value = { full: '', immediate: [] }; + value = { full: '', normalized: '', immediate: [] }; if (!shouldSkipForTextMatching(root)) { let currentImmediate = ''; if ((root instanceof HTMLInputElement) && (root.type === 'submit' || root.type === 'button')) { - value = { full: root.value, immediate: [root.value] }; + value = { full: root.value, normalized: normalizeWhiteSpace(root.value), immediate: [root.value] }; } else { for (let child = root.firstChild; child; child = child.nextSibling) { if (child.nodeType === Node.TEXT_NODE) { @@ -84,6 +85,8 @@ export function elementText(cache: Map<Element | ShadowRoot, ElementText>, root: value.immediate.push(currentImmediate); if ((root as Element).shadowRoot) value.full += elementText(cache, (root as Element).shadowRoot!).full; + if (value.full) + value.normalized = normalizeWhiteSpace(value.full); } } cache.set(root, value); @@ -111,7 +114,7 @@ export function getElementLabels(textCache: Map<Element | ShadowRoot, ElementTex return labels.map(label => elementText(textCache, label)); const ariaLabel = element.getAttribute('aria-label'); if (ariaLabel !== null && !!ariaLabel.trim()) - return [{ full: ariaLabel, immediate: [ariaLabel] }]; + return [{ full: ariaLabel, normalized: normalizeWhiteSpace(ariaLabel), immediate: [ariaLabel] }]; // https://html.spec.whatwg.org/multipage/forms.html#category-label const isNonHiddenInput = element.nodeName === 'INPUT' && (element as HTMLInputElement).type !== 'hidden'; diff --git a/playwright/packages/playwright-core/src/server/injected/vueSelectorEngine.ts b/playwright/packages/playwright-core/src/server/injected/vueSelectorEngine.ts index 0ba552d558..154f36b983 100644 --- a/playwright/packages/playwright-core/src/server/injected/vueSelectorEngine.ts +++ b/playwright/packages/playwright-core/src/server/injected/vueSelectorEngine.ts @@ -86,12 +86,18 @@ function buildComponentsTreeVue3(instance: VueVNode): ComponentNode { // @see https://github.com/vuejs/devtools/blob/e7132f3392b975e39e1d9a23cf30456c270099c2/packages/app-backend-vue3/src/components/util.ts#L29 function getInstanceName(instance: VueVNode): string { const name = getComponentTypeName(instance.type || {}); - if (name) return name; - if (instance.root === instance) return 'Root'; - for (const key in instance.parent?.type?.components) - if (instance.parent?.type.components[key] === instance.type) return saveComponentName(instance, key); - for (const key in instance.appContext?.components) - if (instance.appContext.components[key] === instance.type) return saveComponentName(instance, key); + if (name) + return name; + if (instance.root === instance) + return 'Root'; + for (const key in instance.parent?.type?.components) { + if (instance.parent?.type.components[key] === instance.type) + return saveComponentName(instance, key); + } + for (const key in instance.appContext?.components) { + if (instance.appContext.components[key] === instance.type) + return saveComponentName(instance, key); + } return 'Anonymous Component'; } @@ -132,7 +138,8 @@ function buildComponentsTreeVue3(instance: VueVNode): ComponentNode { // @see https://github.com/vuejs/devtools/blob/e7132f3392b975e39e1d9a23cf30456c270099c2/packages/app-backend-vue3/src/components/el.ts#L15 function getFragmentRootElements(vnode: any): Element[] { - if (!vnode.children) return []; + if (!vnode.children) + return []; const list = []; diff --git a/playwright/packages/playwright-core/src/server/network.ts b/playwright/packages/playwright-core/src/server/network.ts index dc2fa2ea3c..83b0093a38 100644 --- a/playwright/packages/playwright-core/src/server/network.ts +++ b/playwright/packages/playwright-core/src/server/network.ts @@ -134,18 +134,6 @@ export class Request extends SdkObject { this._waitForResponsePromise.resolve(null); } - async _waitForRequestFailure() { - const response = await this._waitForResponsePromise; - // If response is null it was a failure an we are done. - if (!response) - return; - await response._finishedPromise; - if (this.failure()) - return; - // If request finished without errors, we stall. - await new Promise(() => {}); - } - _setOverrides(overrides: types.NormalizedContinueOverrides) { this._overrides = overrides; this._updateHeadersMap(); @@ -270,13 +258,7 @@ export class Route extends SdkObject { async abort(errorCode: string = 'failed') { this._startHandling(); this._request._context.emit(BrowserContext.Events.RequestAborted, this._request); - await Promise.race([ - this._delegate.abort(errorCode), - // If the request is already cancelled by the page before we handle the route, - // we'll receive loading failed event and will ignore route handling error. - this._request._waitForRequestFailure() - ]); - + await this._delegate.abort(errorCode); this._endHandling(); } @@ -304,17 +286,12 @@ export class Route extends SdkObject { const headers = [...(overrides.headers || [])]; this._maybeAddCorsHeaders(headers); this._request._context.emit(BrowserContext.Events.RequestFulfilled, this._request); - await Promise.race([ - this._delegate.fulfill({ - status: overrides.status || 200, - headers, - body, - isBase64, - }), - // If the request is already cancelled by the page before we handle the route, - // we'll receive loading failed event and will ignore route handling error. - this._request._waitForRequestFailure() - ]); + await this._delegate.fulfill({ + status: overrides.status || 200, + headers, + body: body!, + isBase64, + }); this._endHandling(); } @@ -347,13 +324,7 @@ export class Route extends SdkObject { this._request._setOverrides(overrides); if (!overrides.isFallback) this._request._context.emit(BrowserContext.Events.RequestContinued, this._request); - await Promise.race([ - this._delegate.continue(this._request, overrides), - // If the request is already cancelled by the page before we handle the route, - // we'll receive loading failed event and will ignore route handling error. - this._request._waitForRequestFailure() - ]); - + await this._delegate.continue(this._request, overrides); this._endHandling(); } diff --git a/playwright/packages/playwright-core/src/server/page.ts b/playwright/packages/playwright-core/src/server/page.ts index a00d09cfe3..359fed8360 100644 --- a/playwright/packages/playwright-core/src/server/page.ts +++ b/playwright/packages/playwright-core/src/server/page.ts @@ -33,7 +33,7 @@ import type { Progress } from './progress'; import { ProgressController } from './progress'; import { LongStandingScope, assert, isError } from '../utils'; import { ManualPromise } from '../utils/manualPromise'; -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import type { ImageComparatorOptions } from '../utils/comparators'; import { getComparator } from '../utils/comparators'; import type { CallMetadata } from './instrumentation'; @@ -44,6 +44,7 @@ import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser'; import { parseEvaluationResultValue, source } from './isomorphic/utilityScriptSerializers'; import type { SerializedValue } from './isomorphic/utilityScriptSerializers'; import { TargetClosedError } from './errors'; +import { asLocator } from '../utils/isomorphic/locatorGenerators'; export interface PageDelegate { readonly rawMouse: input.RawMouse; @@ -110,7 +111,7 @@ type EmulatedMedia = { forcedColors: types.ForcedColors; }; -type ExpectScreenshotOptions = { +type ExpectScreenshotOptions = ImageComparatorOptions & ScreenshotOptions & { timeout?: number, expected?: Buffer, isNot?: boolean, @@ -118,8 +119,6 @@ type ExpectScreenshotOptions = { frame: frames.Frame, selector: string, }, - comparatorOptions?: ImageComparatorOptions, - screenshotOptions?: ScreenshotOptions, }; export class Page extends SdkObject { @@ -131,6 +130,7 @@ export class Page extends SdkObject { FrameAttached: 'frameattached', FrameDetached: 'framedetached', InternalFrameNavigatedToNewDocument: 'internalframenavigatedtonewdocument', + LocatorHandlerTriggered: 'locatorhandlertriggered', ScreencastFrame: 'screencastframe', Video: 'video', WebSocket: 'websocket', @@ -168,6 +168,9 @@ export class Page extends SdkObject { _video: Artifact | null = null; _opener: Page | undefined; private _isServerSideOnly = false; + private _locatorHandlers = new Map<number, { selector: string, resolved?: ManualPromise<void> }>(); + private _lastLocatorHandlerUid = 0; + private _locatorHandlerRunningCounter = 0; // Aiming at 25 fps by default - each frame is 40ms, but we give some slack with 35ms. // When throttling for tracing, 200ms between frames, except for 10 frames around the action. @@ -249,6 +252,7 @@ export class Page extends SdkObject { async resetForReuse(metadata: CallMetadata) { this.setDefaultNavigationTimeout(undefined); this.setDefaultTimeout(undefined); + this._locatorHandlers.clear(); await this._removeExposedBindings(); await this._removeInitScripts(); @@ -428,6 +432,42 @@ export class Page extends SdkObject { }), this._timeoutSettings.navigationTimeout(options)); } + registerLocatorHandler(selector: string) { + const uid = ++this._lastLocatorHandlerUid; + this._locatorHandlers.set(uid, { selector }); + return uid; + } + + resolveLocatorHandler(uid: number) { + const handler = this._locatorHandlers.get(uid); + if (handler) { + handler.resolved?.resolve(); + handler.resolved = undefined; + } + } + + async performLocatorHandlersCheckpoint(progress: Progress) { + // Do not run locator handlers from inside locator handler callbacks to avoid deadlocks. + if (this._locatorHandlerRunningCounter) + return; + for (const [uid, handler] of this._locatorHandlers) { + if (!handler.resolved) { + if (await this.mainFrame().isVisibleInternal(handler.selector, { strict: true })) { + handler.resolved = new ManualPromise(); + this.emit(Page.Events.LocatorHandlerTriggered, uid); + } + } + if (handler.resolved) { + ++this._locatorHandlerRunningCounter; + progress.log(` found ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)}, intercepting action to run the handler`); + await this.openScope.race(handler.resolved).finally(() => --this._locatorHandlerRunningCounter); + // Avoid side-effects after long-running operation. + progress.throwIfAborted(); + progress.log(` interception handler has finished, continuing`); + } + } + } + async emulateMedia(options: Partial<EmulatedMedia>) { if (options.media !== undefined) this._emulatedMedia.media = options.media; @@ -498,10 +538,11 @@ export class Page extends SdkObject { async expectScreenshot(metadata: CallMetadata, options: ExpectScreenshotOptions = {}): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[] }> { const locator = options.locator; const rafrafScreenshot = locator ? async (progress: Progress, timeout: number) => { - return await locator.frame.rafrafTimeoutScreenshotElementWithProgress(progress, locator.selector, timeout, options.screenshotOptions || {}); + return await locator.frame.rafrafTimeoutScreenshotElementWithProgress(progress, locator.selector, timeout, options || {}); } : async (progress: Progress, timeout: number) => { + await this.performLocatorHandlersCheckpoint(progress); await this.mainFrame().rafrafTimeout(timeout); - return await this._screenshotter.screenshotPage(progress, options.screenshotOptions || {}); + return await this._screenshotter.screenshotPage(progress, options || {}); }; const comparator = getComparator('image/png'); @@ -509,7 +550,7 @@ export class Page extends SdkObject { if (!options.expected && options.isNot) return { errorMessage: '"not" matcher requires expected result' }; try { - const format = validateScreenshotOptions(options.screenshotOptions || {}); + const format = validateScreenshotOptions(options || {}); if (format !== 'png') throw new Error('Only PNG screenshots are supported'); } catch (error) { @@ -522,7 +563,7 @@ export class Page extends SdkObject { diff?: Buffer, } | undefined = undefined; const areEqualScreenshots = (actual: Buffer | undefined, expected: Buffer | undefined, previous: Buffer | undefined) => { - const comparatorResult = actual && expected ? comparator(actual, expected, options.comparatorOptions) : undefined; + const comparatorResult = actual && expected ? comparator(actual, expected, options) : undefined; if (comparatorResult !== undefined && !!comparatorResult === !!options.isNot) return true; if (comparatorResult) diff --git a/playwright/packages/playwright-core/src/server/pipeTransport.ts b/playwright/packages/playwright-core/src/server/pipeTransport.ts index cd8e31cb94..a84ca67023 100644 --- a/playwright/packages/playwright-core/src/server/pipeTransport.ts +++ b/playwright/packages/playwright-core/src/server/pipeTransport.ts @@ -17,7 +17,7 @@ import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from './transport'; import { makeWaitForNextTask } from '../utils'; -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; export class PipeTransport implements ConnectionTransport { private _pipeRead: NodeJS.ReadableStream; diff --git a/playwright/packages/playwright-core/src/server/playwright.ts b/playwright/packages/playwright-core/src/server/playwright.ts index cfa3e699ac..c4ffd518ce 100644 --- a/playwright/packages/playwright-core/src/server/playwright.ts +++ b/playwright/packages/playwright-core/src/server/playwright.ts @@ -24,7 +24,7 @@ import { Selectors } from './selectors'; import { WebKit } from './webkit/webkit'; import type { CallMetadata } from './instrumentation'; import { createInstrumentation, SdkObject } from './instrumentation'; -import { debugLogger } from '../common/debugLogger'; +import { debugLogger } from '../utils/debugLogger'; import type { Page } from './page'; import { DebugController } from './debugController'; import type { Language } from '../utils/isomorphic/locatorGenerators'; diff --git a/playwright/packages/playwright-core/src/server/progress.ts b/playwright/packages/playwright-core/src/server/progress.ts index 094230a7c0..70de43b944 100644 --- a/playwright/packages/playwright-core/src/server/progress.ts +++ b/playwright/packages/playwright-core/src/server/progress.ts @@ -16,15 +16,13 @@ import { TimeoutError } from './errors'; import { assert, monotonicTime } from '../utils'; -import type { LogName } from '../common/debugLogger'; +import type { LogName } from '../utils/debugLogger'; import type { CallMetadata, Instrumentation, SdkObject } from './instrumentation'; import type { ElementHandle } from './dom'; import { ManualPromise } from '../utils/manualPromise'; -import type { LogEntry } from './injected/injectedScript'; export interface Progress { log(message: string): void; - logEntry(entry: LogEntry): void; timeUntilDeadline(): number; isRunning(): boolean; cleanupWhenAborted(cleanup: () => any): void; @@ -74,16 +72,10 @@ export class ProgressController { const progress: Progress = { log: message => { - progress.logEntry({ message }); - }, - logEntry: entry => { - if ('message' in entry) { - const message = entry.message!; - if (this._state === 'running') - this.metadata.log.push(message); - // Note: we might be sending logs after progress has finished, for example browser logs. - this.instrumentation.onCallLog(this.sdkObject, this.metadata, this._logName, message); - } + if (this._state === 'running') + this.metadata.log.push(message); + // Note: we might be sending logs after progress has finished, for example browser logs. + this.instrumentation.onCallLog(this.sdkObject, this.metadata, this._logName, message); }, timeUntilDeadline: () => this._deadline ? this._deadline - monotonicTime() : 2147483647, // 2^31-1 safe setTimeout in Node. isRunning: () => this._state === 'running', diff --git a/playwright/packages/playwright-core/src/server/recorder.ts b/playwright/packages/playwright-core/src/server/recorder.ts index 9f22234b32..fb113adbd6 100644 --- a/playwright/packages/playwright-core/src/server/recorder.ts +++ b/playwright/packages/playwright-core/src/server/recorder.ts @@ -445,7 +445,8 @@ class ContextRecorder extends EventEmitter { setOutput(codegenId: string, outputFile?: string) { const languages = new Set([ - new JavaLanguageGenerator(), + new JavaLanguageGenerator('junit'), + new JavaLanguageGenerator('library'), new JavaScriptLanguageGenerator(/* isPlaywrightTest */false), new JavaScriptLanguageGenerator(/* isPlaywrightTest */true), new PythonLanguageGenerator(/* isAsync */false, /* isPytest */true), @@ -751,7 +752,7 @@ async function findFrameSelector(frame: Frame): Promise<string | undefined> { const utility = await parent._utilityContext(); const injected = await utility.injectedScript(); const selector = await injected.evaluate((injected, element) => { - return injected.generateSelector(element as Element, { testIdAttributeName: '', omitInternalEngines: true }); + return injected.generateSelectorSimple(element as Element, { testIdAttributeName: '', omitInternalEngines: true }); }, frameElement); return selector; } catch (e) { diff --git a/playwright/packages/playwright-core/src/server/recorder/csharp.ts b/playwright/packages/playwright-core/src/server/recorder/csharp.ts index 1dcc6a331a..46fadc244a 100644 --- a/playwright/packages/playwright-core/src/server/recorder/csharp.ts +++ b/playwright/packages/playwright-core/src/server/recorder/csharp.ts @@ -161,7 +161,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator { case 'assertVisible': return `await Expect(${subject}.${this._asLocator(action.selector)}).ToBeVisibleAsync();`; case 'assertValue': { - const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmpty()`; + const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmptyAsync()`; return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`; } } diff --git a/playwright/packages/playwright-core/src/server/recorder/java.ts b/playwright/packages/playwright-core/src/server/recorder/java.ts index 742b4db93a..d4ebfdeea4 100644 --- a/playwright/packages/playwright-core/src/server/recorder/java.ts +++ b/playwright/packages/playwright-core/src/server/recorder/java.ts @@ -26,16 +26,36 @@ import { JavaScriptFormatter } from './javascript'; import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils'; import { asLocator } from '../../utils/isomorphic/locatorGenerators'; +type JavaLanguageMode = 'library' | 'junit'; + export class JavaLanguageGenerator implements LanguageGenerator { - id = 'java'; + id: string; groupName = 'Java'; - name = 'Library'; + name: string; highlighter = 'java' as Language; + _mode: JavaLanguageMode; + + constructor(mode: JavaLanguageMode) { + if (mode === 'library') { + this.name = 'Library'; + this.id = 'java'; + } else if (mode === 'junit') { + this.name = 'JUnit'; + this.id = 'java-junit'; + } else { + throw new Error(`Unknown Java language mode: ${mode}`); + } + this._mode = mode; + } generateAction(actionInContext: ActionInContext): string { const action = actionInContext.action; const pageAlias = actionInContext.frame.pageAlias; - const formatter = new JavaScriptFormatter(6); + const offset = this._mode === 'junit' ? 4 : 6; + const formatter = new JavaScriptFormatter(offset); + + if (this._mode !== 'library' && (action.name === 'openPage' || action.name === 'closePage')) + return ''; if (action.name === 'openPage') { formatter.add(`Page ${pageAlias} = context.newPage();`); @@ -141,6 +161,21 @@ export class JavaLanguageGenerator implements LanguageGenerator { generateHeader(options: LanguageGeneratorOptions): string { const formatter = new JavaScriptFormatter(); + if (this._mode === 'junit') { + formatter.add(` + import com.microsoft.playwright.junit.UsePlaywright; + import com.microsoft.playwright.Page; + import com.microsoft.playwright.options.*; + + import org.junit.jupiter.api.*; + import static com.microsoft.playwright.assertions.PlaywrightAssertions.*; + + @UsePlaywright + public class TestExample { + @Test + void test(Page page) {`); + return formatter.format(); + } formatter.add(` import com.microsoft.playwright.*; import com.microsoft.playwright.options.*; @@ -157,6 +192,10 @@ export class JavaLanguageGenerator implements LanguageGenerator { generateFooter(saveStorage: string | undefined): string { const storageStateLine = saveStorage ? `\n context.storageState(new BrowserContext.StorageStateOptions().setPath(${quote(saveStorage)}));\n` : ''; + if (this._mode === 'junit') { + return `${storageStateLine} } +}`; + } return `${storageStateLine} } } }`; diff --git a/playwright/packages/playwright-core/src/server/registry/browserFetcher.ts b/playwright/packages/playwright-core/src/server/registry/browserFetcher.ts index 154edf1114..6f8da6e825 100644 --- a/playwright/packages/playwright-core/src/server/registry/browserFetcher.ts +++ b/playwright/packages/playwright-core/src/server/registry/browserFetcher.ts @@ -20,7 +20,7 @@ import os from 'os'; import path from 'path'; import childProcess from 'child_process'; import { existsAsync } from '../../utils/fileUtils'; -import { debugLogger } from '../../common/debugLogger'; +import { debugLogger } from '../../utils/debugLogger'; import { ManualPromise } from '../../utils/manualPromise'; import { colors, progress as ProgressBar } from '../../utilsBundle'; import { browserDirectoryToMarkerFilePath } from '.'; diff --git a/playwright/packages/playwright-core/src/server/registry/dependencies.ts b/playwright/packages/playwright-core/src/server/registry/dependencies.ts index 70458277f9..44f56f3d2f 100644 --- a/playwright/packages/playwright-core/src/server/registry/dependencies.ts +++ b/playwright/packages/playwright-core/src/server/registry/dependencies.ts @@ -217,7 +217,7 @@ export async function validateDependenciesLinux(sdkLanguage: string, linuxLddDir } } - const maybeSudo = (process.getuid() !== 0) && os.platform() !== 'win32' ? 'sudo ' : ''; + const maybeSudo = process.getuid?.() && os.platform() !== 'win32' ? 'sudo ' : ''; const dockerInfo = readDockerVersionSync(); const errorLines = [ `Host system is missing dependencies to run browsers.`, @@ -230,17 +230,17 @@ export async function validateDependenciesLinux(sdkLanguage: string, linuxLddDir const pwVersion = getPlaywrightVersion(); const requiredDockerImage = dockerInfo.dockerImageName.replace(dockerInfo.driverVersion, pwVersion); errorLines.push(...[ - `This is most likely due to docker image version not matching Playwright version:`, - `- Playwright: ${pwVersion}`, - `- Docker: ${dockerInfo.driverVersion}`, + `This is most likely due to Docker image version not matching Playwright version:`, + `- Playwright : ${pwVersion}`, + `- Docker image: ${dockerInfo.driverVersion}`, ``, `Either:`, - `- (recommended) use docker image "${requiredDockerImage}"`, - `- (alternative 1) run the following command inside docker to install missing dependencies:`, + `- (recommended) use Docker image "${requiredDockerImage}"`, + `- (alternative 1) run the following command inside Docker to install missing dependencies:`, ``, ` ${maybeSudo}${buildPlaywrightCLICommand(sdkLanguage, 'install-deps')}`, ``, - `- (alternative 2) use apt inside docker:`, + `- (alternative 2) use apt inside Docker:`, ``, ` ${maybeSudo}apt-get install ${[...missingPackages].join('\\\n ')}`, ``, @@ -366,7 +366,7 @@ function quoteProcessArgs(args: string[]): string[] { } export async function transformCommandsForRoot(commands: string[]): Promise<{ command: string, args: string[], elevatedPermissions: boolean}> { - const isRoot = process.getuid() === 0; + const isRoot = process.getuid?.() === 0; if (isRoot) return { command: 'sh', args: ['-c', `${commands.join('&& ')}`], elevatedPermissions: false }; const sudoExists = await spawnAsync('which', ['sudo']); diff --git a/playwright/packages/playwright-core/src/server/registry/index.ts b/playwright/packages/playwright-core/src/server/registry/index.ts index 707507ac09..fa3a1bbf78 100644 --- a/playwright/packages/playwright-core/src/server/registry/index.ts +++ b/playwright/packages/playwright-core/src/server/registry/index.ts @@ -20,7 +20,6 @@ import path from 'path'; import * as util from 'util'; import * as fs from 'fs'; import { lockfile } from '../../utilsBundle'; -import { getLinuxDistributionInfo } from '../../utils/linuxUtils'; import { fetchData } from '../../utils/network'; import { getEmbedderName } from '../../utils/userAgent'; import { getFromENV, getAsBooleanFromENV, calculateSha1, wrapInASCIIBox, getPackageManagerExecCommand } from '../../utils'; @@ -32,6 +31,7 @@ import { transformCommandsForRoot, dockerVersion, readDockerVersionSync } from ' import { installDependenciesLinux, installDependenciesWindows, validateDependenciesLinux, validateDependenciesWindows } from './dependencies'; import { downloadBrowserWithProgressBar, logPolitely } from './browserFetcher'; export { writeDockerVersion } from './dependencies'; +import { debugLogger } from '../../utils/debugLogger'; const PACKAGE_PATH = path.join(__dirname, '..', '..', '..'); const BIN_PATH = path.join(__dirname, '..', '..', '..', 'bin'); @@ -77,10 +77,10 @@ type DownloadPaths = Record<HostPlatform, string | undefined>; const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { 'chromium': { '<unknown>': undefined, - 'ubuntu18.04-x64': 'builds/chromium/%s/chromium-linux.zip', + 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/chromium/%s/chromium-linux.zip', 'ubuntu22.04-x64': 'builds/chromium/%s/chromium-linux.zip', - 'ubuntu18.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', + 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip', 'debian11-x64': 'builds/chromium/%s/chromium-linux.zip', @@ -100,10 +100,10 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { }, 'chromium-tip-of-tree': { '<unknown>': undefined, - 'ubuntu18.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', + 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', 'ubuntu22.04-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', - 'ubuntu18.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', + 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip', 'debian11-x64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip', @@ -123,10 +123,10 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { }, 'chromium-with-symbols': { '<unknown>': undefined, - 'ubuntu18.04-x64': 'builds/chromium/%s/chromium-with-symbols-linux.zip', + 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/chromium/%s/chromium-with-symbols-linux.zip', 'ubuntu22.04-x64': 'builds/chromium/%s/chromium-with-symbols-linux.zip', - 'ubuntu18.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip', + 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip', 'debian11-x64': 'builds/chromium/%s/chromium-with-symbols-linux.zip', @@ -146,7 +146,7 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { }, 'firefox': { '<unknown>': undefined, - 'ubuntu18.04-x64': 'builds/firefox/%s/firefox-ubuntu-18.04.zip', + 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/firefox/%s/firefox-ubuntu-20.04.zip', 'ubuntu22.04-x64': 'builds/firefox/%s/firefox-ubuntu-22.04.zip', 'ubuntu18.04-arm64': undefined, @@ -169,7 +169,7 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { }, 'firefox-beta': { '<unknown>': undefined, - 'ubuntu18.04-x64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-18.04.zip', + 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip', 'ubuntu22.04-x64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04.zip', 'ubuntu18.04-arm64': undefined, @@ -215,7 +215,7 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { }, 'webkit': { '<unknown>': undefined, - 'ubuntu18.04-x64': 'builds/deprecated-webkit-ubuntu-18.04/%s/deprecated-webkit-ubuntu-18.04.zip', + 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/webkit/%s/webkit-ubuntu-20.04.zip', 'ubuntu22.04-x64': 'builds/webkit/%s/webkit-ubuntu-22.04.zip', 'ubuntu18.04-arm64': undefined, @@ -238,10 +238,10 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { }, 'ffmpeg': { '<unknown>': undefined, - 'ubuntu18.04-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip', + 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip', 'ubuntu22.04-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip', - 'ubuntu18.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip', + 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip', 'ubuntu22.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip', 'debian11-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip', @@ -261,10 +261,10 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = { }, 'android': { '<unknown>': 'builds/android/%s/android.zip', - 'ubuntu18.04-x64': 'builds/android/%s/android.zip', + 'ubuntu18.04-x64': undefined, 'ubuntu20.04-x64': 'builds/android/%s/android.zip', 'ubuntu22.04-x64': 'builds/android/%s/android.zip', - 'ubuntu18.04-arm64': 'builds/android/%s/android.zip', + 'ubuntu18.04-arm64': undefined, 'ubuntu20.04-arm64': 'builds/android/%s/android.zip', 'ubuntu22.04-arm64': 'builds/android/%s/android.zip', 'debian11-x64': 'builds/android/%s/android.zip', @@ -382,7 +382,7 @@ export interface Executable { browserVersion?: string, executablePathOrDie(sdkLanguage: string): string; executablePath(sdkLanguage: string): string | undefined; - validateHostRequirements(sdkLanguage: string): Promise<void>; + _validateHostRequirements(sdkLanguage: string): Promise<void>; } interface ExecutableImpl extends Executable { @@ -445,7 +445,7 @@ export class Registry { executablePath: () => chromiumExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium', chromiumExecutable, chromium.installByDefault, sdkLanguage), installType: chromium.installByDefault ? 'download-by-default' : 'download-on-demand', - validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromium.dir, ['chrome-linux'], [], ['chrome-win']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromium.dir, ['chrome-linux'], [], ['chrome-win']), downloadURLs: this._downloadURLs(chromium), browserVersion: chromium.browserVersion, _install: () => this._downloadExecutable(chromium, chromiumExecutable), @@ -463,7 +463,7 @@ export class Registry { executablePath: () => chromiumWithSymbolsExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium-with-symbols', chromiumWithSymbolsExecutable, chromiumWithSymbols.installByDefault, sdkLanguage), installType: chromiumWithSymbols.installByDefault ? 'download-by-default' : 'download-on-demand', - validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumWithSymbols.dir, ['chrome-linux'], [], ['chrome-win']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumWithSymbols.dir, ['chrome-linux'], [], ['chrome-win']), downloadURLs: this._downloadURLs(chromiumWithSymbols), browserVersion: chromiumWithSymbols.browserVersion, _install: () => this._downloadExecutable(chromiumWithSymbols, chromiumWithSymbolsExecutable), @@ -481,7 +481,7 @@ export class Registry { executablePath: () => chromiumTipOfTreeExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium-tip-of-tree', chromiumTipOfTreeExecutable, chromiumTipOfTree.installByDefault, sdkLanguage), installType: chromiumTipOfTree.installByDefault ? 'download-by-default' : 'download-on-demand', - validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumTipOfTree.dir, ['chrome-linux'], [], ['chrome-win']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'chromium', chromiumTipOfTree.dir, ['chrome-linux'], [], ['chrome-win']), downloadURLs: this._downloadURLs(chromiumTipOfTree), browserVersion: chromiumTipOfTree.browserVersion, _install: () => this._downloadExecutable(chromiumTipOfTree, chromiumTipOfTreeExecutable), @@ -567,7 +567,7 @@ export class Registry { executablePath: () => firefoxExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox', firefoxExecutable, firefox.installByDefault, sdkLanguage), installType: firefox.installByDefault ? 'download-by-default' : 'download-on-demand', - validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefox.dir, ['firefox'], [], ['firefox']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefox.dir, ['firefox'], [], ['firefox']), downloadURLs: this._downloadURLs(firefox), browserVersion: firefox.browserVersion, _install: () => this._downloadExecutable(firefox, firefoxExecutable), @@ -585,7 +585,7 @@ export class Registry { executablePath: () => firefoxAsanExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox-asan', firefoxAsanExecutable, firefoxAsan.installByDefault, sdkLanguage), installType: firefoxAsan.installByDefault ? 'download-by-default' : 'download-on-demand', - validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefoxAsan.dir, ['firefox'], [], ['firefox']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefoxAsan.dir, ['firefox'], [], ['firefox']), downloadURLs: this._downloadURLs(firefoxAsan), browserVersion: firefoxAsan.browserVersion, _install: () => this._downloadExecutable(firefoxAsan, firefoxAsanExecutable), @@ -603,7 +603,7 @@ export class Registry { executablePath: () => firefoxBetaExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('firefox-beta', firefoxBetaExecutable, firefoxBeta.installByDefault, sdkLanguage), installType: firefoxBeta.installByDefault ? 'download-by-default' : 'download-on-demand', - validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefoxBeta.dir, ['firefox'], [], ['firefox']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'firefox', firefoxBeta.dir, ['firefox'], [], ['firefox']), downloadURLs: this._downloadURLs(firefoxBeta), browserVersion: firefoxBeta.browserVersion, _install: () => this._downloadExecutable(firefoxBeta, firefoxBetaExecutable), @@ -631,7 +631,7 @@ export class Registry { executablePath: () => webkitExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('webkit', webkitExecutable, webkit.installByDefault, sdkLanguage), installType: webkit.installByDefault ? 'download-by-default' : 'download-on-demand', - validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'webkit', webkit.dir, webkitLinuxLddDirectories, ['libGLESv2.so.2', 'libx264.so'], ['']), + _validateHostRequirements: (sdkLanguage: string) => this._validateHostRequirements(sdkLanguage, 'webkit', webkit.dir, webkitLinuxLddDirectories, ['libGLESv2.so.2', 'libx264.so'], ['']), downloadURLs: this._downloadURLs(webkit), browserVersion: webkit.browserVersion, _install: () => this._downloadExecutable(webkit, webkitExecutable), @@ -649,7 +649,7 @@ export class Registry { executablePath: () => ffmpegExecutable, executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('ffmpeg', ffmpegExecutable, ffmpeg.installByDefault, sdkLanguage), installType: ffmpeg.installByDefault ? 'download-by-default' : 'download-on-demand', - validateHostRequirements: () => Promise.resolve(), + _validateHostRequirements: () => Promise.resolve(), downloadURLs: this._downloadURLs(ffmpeg), _install: () => this._downloadExecutable(ffmpeg, ffmpegExecutable), _dependencyGroup: 'tools', @@ -664,7 +664,7 @@ export class Registry { executablePath: () => undefined, executablePathOrDie: () => '', installType: 'download-on-demand', - validateHostRequirements: () => Promise.resolve(), + _validateHostRequirements: () => Promise.resolve(), downloadURLs: this._downloadURLs(android), _install: () => this._downloadExecutable(android), _dependencyGroup: 'tools', @@ -704,7 +704,7 @@ export class Registry { executablePath: (sdkLanguage: string) => executablePath(sdkLanguage, false), executablePathOrDie: (sdkLanguage: string) => executablePath(sdkLanguage, true)!, installType: install ? 'install-script' : 'none', - validateHostRequirements: () => Promise.resolve(), + _validateHostRequirements: () => Promise.resolve(), _isHermeticInstallation: false, _install: install, }; @@ -735,14 +735,6 @@ export class Registry { } private async _validateHostRequirements(sdkLanguage: string, browserName: BrowserName, browserDirectory: string, linuxLddDirectories: string[], dlOpenLibraries: string[], windowsExeAndDllDirectories: string[]) { - if (getAsBooleanFromENV('PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS')) { - process.stderr.write('Skipping host requirements validation logic because `PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS` env variable is set.\n'); - return; - } - const distributionInfo = await getLinuxDistributionInfo(); - if (browserName === 'firefox' && distributionInfo?.id === 'ubuntu' && distributionInfo?.version === '16.04') - throw new Error(`Cannot launch Firefox on Ubuntu 16.04! Minimum required Ubuntu version for Firefox browser is 20.04`); - if (os.platform() === 'linux') return await validateDependenciesLinux(sdkLanguage, linuxLddDirectories.map(d => path.join(browserDirectory, d)), dlOpenLibraries); if (os.platform() === 'win32' && os.arch() === 'x64') @@ -859,6 +851,37 @@ export class Registry { }; } + async validateHostRequirementsForExecutablesIfNeeded(executables: Executable[], sdkLanguage: string) { + if (getAsBooleanFromENV('PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS')) { + process.stderr.write('Skipping host requirements validation logic because `PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS` env variable is set.\n'); + return; + } + for (const executable of executables) + await this._validateHostRequirementsForExecutableIfNeeded(executable, sdkLanguage); + } + + private async _validateHostRequirementsForExecutableIfNeeded(executable: Executable, sdkLanguage: string) { + const kMaximumReValidationPeriod = 30 * 24 * 60 * 60 * 1000; // 30 days + // Executable does not require validation. + if (!executable.directory) + return; + const markerFile = path.join(executable.directory, 'DEPENDENCIES_VALIDATED'); + // Executable is already validated. + if (await fs.promises.stat(markerFile).then(stat => (Date.now() - stat.mtime.getTime()) < kMaximumReValidationPeriod).catch(() => false)) + return; + + debugLogger.log('install', `validating host requirements for "${executable.name}"`); + try { + await executable._validateHostRequirements(sdkLanguage); + debugLogger.log('install', `validation passed for ${executable.name}`); + } catch (error) { + debugLogger.log('install', `validation failed for ${executable.name}`); + throw error; + } + + await fs.promises.writeFile(markerFile, '').catch(() => {}); + } + private _downloadURLs(descriptor: BrowsersJSONDescriptor): string[] { const paths = (DOWNLOAD_PATHS as any)[descriptor.name]; const downloadPathTemplate: string|undefined = paths[hostPlatform] || paths['<unknown>']; diff --git a/playwright/packages/playwright-core/src/server/registry/nativeDeps.ts b/playwright/packages/playwright-core/src/server/registry/nativeDeps.ts index 7970156225..0f5f1c499e 100644 --- a/playwright/packages/playwright-core/src/server/registry/nativeDeps.ts +++ b/playwright/packages/playwright-core/src/server/registry/nativeDeps.ts @@ -19,168 +19,6 @@ // ./utils/linux-browser-dependencies/run.sh ubuntu:20.04 export const deps: any = { - 'ubuntu18.04-x64': { - tools: [ - 'xvfb', - 'fonts-noto-color-emoji', - 'ttf-unifont', - 'libfontconfig', - 'libfreetype6', - 'xfonts-cyrillic', - 'xfonts-scalable', - 'fonts-liberation', - 'fonts-ipafont-gothic', - 'fonts-wqy-zenhei', - 'fonts-tlwg-loma-otf', - 'ttf-ubuntu-font-family', - ], - chromium: [ - 'fonts-liberation', - 'libasound2', - 'libatk-bridge2.0-0', - 'libatk1.0-0', - 'libatspi2.0-0', - 'libcairo2', - 'libcups2', - 'libdbus-1-3', - 'libdrm2', - 'libegl1', - 'libgbm1', - 'libglib2.0-0', - 'libgtk-3-0', - 'libnspr4', - 'libnss3', - 'libpango-1.0-0', - 'libx11-6', - 'libx11-xcb1', - 'libxcb1', - 'libxcomposite1', - 'libxdamage1', - 'libxext6', - 'libxfixes3', - 'libxrandr2', - 'libxshmfence1', - ], - firefox: [ - 'ffmpeg', - 'libatk1.0-0', - 'libcairo-gobject2', - 'libcairo2', - 'libdbus-1-3', - 'libdbus-glib-1-2', - 'libfontconfig1', - 'libfreetype6', - 'libgdk-pixbuf2.0-0', - 'libglib2.0-0', - 'libgtk-3-0', - 'libpango-1.0-0', - 'libpangocairo-1.0-0', - 'libpangoft2-1.0-0', - 'libx11-6', - 'libx11-xcb1', - 'libxcb-shm0', - 'libxcb1', - 'libxcomposite1', - 'libxcursor1', - 'libxdamage1', - 'libxext6', - 'libxfixes3', - 'libxi6', - 'libxrender1', - 'libxt6', - 'libxtst6', - ], - webkit: [], - lib2package: { - 'libasound.so.2': 'libasound2', - 'libatk-1.0.so.0': 'libatk1.0-0', - 'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0', - 'libatspi.so.0': 'libatspi2.0-0', - 'libbrotlidec.so.1': 'libbrotli1', - 'libcairo-gobject.so.2': 'libcairo-gobject2', - 'libcairo.so.2': 'libcairo2', - 'libcups.so.2': 'libcups2', - 'libdbus-1.so.3': 'libdbus-1-3', - 'libdbus-glib-1.so.2': 'libdbus-glib-1-2', - 'libdrm.so.2': 'libdrm2', - 'libEGL.so.1': 'libegl1', - 'libenchant.so.1': 'libenchant1c2a', - 'libepoxy.so.0': 'libepoxy0', - 'libevent-2.1.so.6': 'libevent-2.1-6', - 'libevdev.so.2': 'libevdev2', - 'libfontconfig.so.1': 'libfontconfig1', - 'libfreetype.so.6': 'libfreetype6', - 'libgbm.so.1': 'libgbm1', - 'libgdk_pixbuf-2.0.so.0': 'libgdk-pixbuf2.0-0', - 'libgdk-3.so.0': 'libgtk-3-0', - 'libgdk-x11-2.0.so.0': 'libgtk2.0-0', - 'libgio-2.0.so.0': 'libglib2.0-0', - 'libGL.so.1': 'libgl1', - 'libGLESv2.so.2': 'libgles2', - 'libglib-2.0.so.0': 'libglib2.0-0', - 'libgmodule-2.0.so.0': 'libglib2.0-0', - 'libgobject-2.0.so.0': 'libglib2.0-0', - 'libgstapp-1.0.so.0': 'gstreamer1.0-plugins-base', - 'libgstaudio-1.0.so.0': 'gstreamer1.0-plugins-base', - 'libgstbase-1.0.so.0': 'libgstreamer1.0-0', - 'libgstcodecparsers-1.0.so.0': 'gstreamer1.0-plugins-bad', - 'libgstfft-1.0.so.0': 'gstreamer1.0-plugins-base', - 'libgstgl-1.0.so.0': 'libgstreamer-gl1.0-0', - 'libgstpbutils-1.0.so.0': 'gstreamer1.0-plugins-base', - 'libgstreamer-1.0.so.0': 'libgstreamer1.0-0', - 'libgsttag-1.0.so.0': 'gstreamer1.0-plugins-base', - 'libgstvideo-1.0.so.0': 'gstreamer1.0-plugins-base', - 'libgthread-2.0.so.0': 'libglib2.0-0', - 'libgtk-3.so.0': 'libgtk-3-0', - 'libgtk-x11-2.0.so.0': 'libgtk2.0-0', - 'libharfbuzz-icu.so.0': 'libharfbuzz-icu0', - 'libharfbuzz.so.0': 'libharfbuzz0b', - 'libhyphen.so.0': 'libhyphen0', - 'libicudata.so.60': 'libicu60', - 'libicui18n.so.60': 'libicu60', - 'libicuuc.so.60': 'libicu60', - 'libjpeg.so.8': 'libjpeg-turbo8', - 'libnotify.so.4': 'libnotify4', - 'libnspr4.so': 'libnspr4', - 'libnss3.so': 'libnss3', - 'libnssutil3.so': 'libnss3', - 'libopenjp2.so.7': 'libopenjp2-7', - 'libopus.so.0': 'libopus0', - 'libpango-1.0.so.0': 'libpango-1.0-0', - 'libpangocairo-1.0.so.0': 'libpangocairo-1.0-0', - 'libpangoft2-1.0.so.0': 'libpangoft2-1.0-0', - 'libpng16.so.16': 'libpng16-16', - 'libsecret-1.so.0': 'libsecret-1-0', - 'libsmime3.so': 'libnss3', - 'libvpx.so.5': 'libvpx5', - 'libwayland-client.so.0': 'libwayland-client0', - 'libwayland-egl.so.1': 'libwayland-egl1', - 'libwayland-server.so.0': 'libwayland-server0', - 'libwebp.so.6': 'libwebp6', - 'libwebpdemux.so.2': 'libwebpdemux2', - 'libwoff2dec.so.1.0.2': 'libwoff1', - 'libX11-xcb.so.1': 'libx11-xcb1', - 'libX11.so.6': 'libx11-6', - 'libxcb-dri3.so.0': 'libxcb-dri3-0', - 'libxcb-shm.so.0': 'libxcb-shm0', - 'libxcb.so.1': 'libxcb1', - 'libXcomposite.so.1': 'libxcomposite1', - 'libXcursor.so.1': 'libxcursor1', - 'libXdamage.so.1': 'libxdamage1', - 'libXext.so.6': 'libxext6', - 'libXfixes.so.3': 'libxfixes3', - 'libXi.so.6': 'libxi6', - 'libxkbcommon.so.0': 'libxkbcommon0', - 'libxml2.so.2': 'libxml2', - 'libXrandr.so.2': 'libxrandr2', - 'libXrender.so.1': 'libxrender1', - 'libxslt.so.1': 'libxslt1.1', - 'libXt.so.6': 'libxt6', - 'libXtst.so.6': 'libxtst6', - 'libevent-2.1-6': 'libevent-2.1-6', - }, - }, - 'ubuntu20.04-x64': { tools: [ 'xvfb', diff --git a/playwright/packages/playwright-core/src/server/trace/recorder/DEPS.list b/playwright/packages/playwright-core/src/server/trace/recorder/DEPS.list index 8f5e716a87..201a4b001d 100644 --- a/playwright/packages/playwright-core/src/server/trace/recorder/DEPS.list +++ b/playwright/packages/playwright-core/src/server/trace/recorder/DEPS.list @@ -6,4 +6,5 @@ ../../../utils/ ../../../utilsBundle.ts ../../../zipBundle.ts -../common/ +../../dispatchers/dispatcher.ts +../common/ \ No newline at end of file diff --git a/playwright/packages/playwright-core/src/server/trace/recorder/snapshotter.ts b/playwright/packages/playwright-core/src/server/trace/recorder/snapshotter.ts index 30bb86c8bf..cb09ea5652 100644 --- a/playwright/packages/playwright-core/src/server/trace/recorder/snapshotter.ts +++ b/playwright/packages/playwright-core/src/server/trace/recorder/snapshotter.ts @@ -18,7 +18,7 @@ import { BrowserContext } from '../../browserContext'; import { Page } from '../../page'; import type { RegisteredListener } from '../../../utils/eventsHelper'; import { eventsHelper } from '../../../utils/eventsHelper'; -import { debugLogger } from '../../../common/debugLogger'; +import { debugLogger } from '../../../utils/debugLogger'; import type { Frame } from '../../frames'; import type { SnapshotData } from './snapshotterInjected'; import { frameSnapshotStreamer } from './snapshotterInjected'; diff --git a/playwright/packages/playwright-core/src/server/trace/recorder/tracing.ts b/playwright/packages/playwright-core/src/server/trace/recorder/tracing.ts index df41894c05..52fe777134 100644 --- a/playwright/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/playwright/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -42,6 +42,7 @@ import type { SnapshotterBlob, SnapshotterDelegate } from './snapshotter'; import { Snapshotter } from './snapshotter'; import { yazl } from '../../../zipBundle'; import type { ConsoleMessage } from '../../console'; +import { Dispatcher } from '../../dispatchers/dispatcher'; const version: trace.VERSION = 6; @@ -450,7 +451,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps args: message.args().map(a => ({ preview: a.toString(), value: a.rawValue() })), location: message.location(), time: monotonicTime(), - pageId: message.page().guid, + pageId: message.page()?.guid, }; this._appendTraceEvent(event); } @@ -496,8 +497,10 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps function visitTraceEvent(object: any, sha1s: Set<string>): any { if (Array.isArray(object)) return object.map(o => visitTraceEvent(o, sha1s)); + if (object instanceof Dispatcher) + return `<${(object as Dispatcher<any, any, any>)._type}>`; if (object instanceof Buffer) - return undefined; + return `<Buffer>`; if (object instanceof Date) return object; if (typeof object === 'object') { diff --git a/playwright/packages/playwright-core/src/server/trace/viewer/traceViewer.ts b/playwright/packages/playwright-core/src/server/trace/viewer/traceViewer.ts index fa37a53be6..f328ec6f05 100644 --- a/playwright/packages/playwright-core/src/server/trace/viewer/traceViewer.ts +++ b/playwright/packages/playwright-core/src/server/trace/viewer/traceViewer.ts @@ -168,7 +168,7 @@ export async function openTraceInBrowser(traceUrls: string[], options?: OpenTrac // eslint-disable-next-line no-console console.log('\nListening on ' + url); if (!isUnderTest()) - await open(url).catch(() => {}); + await open(url.replace('0.0.0.0', 'localhost')).catch(() => {}); } class StdinServer implements Transport { diff --git a/playwright/packages/playwright-core/src/server/transport.ts b/playwright/packages/playwright-core/src/server/transport.ts index 7fcdb92c12..6cefa270f8 100644 --- a/playwright/packages/playwright-core/src/server/transport.ts +++ b/playwright/packages/playwright-core/src/server/transport.ts @@ -114,8 +114,10 @@ export class WebSocketTransport implements ConnectionTransport { }); if (result.redirect) { - // Strip access key headers from the redirected request. - const newHeaders = Object.fromEntries(Object.entries(headers || {}).filter(([name]) => !name.includes('access-key'))); + // Strip authorization headers from the redirected request. + const newHeaders = Object.fromEntries(Object.entries(headers || {}).filter(([name]) => { + return !name.includes('access-key') && name.toLowerCase() !== 'authorization'; + })); return WebSocketTransport._connect(progress, result.redirect.headers.location!, newHeaders, { follow: true, hadRedirects: true }, debugLogHeader); } diff --git a/playwright/packages/playwright-core/src/server/webkit/protocol.d.ts b/playwright/packages/playwright-core/src/server/webkit/protocol.d.ts index 734063c461..b0e7151d19 100644 --- a/playwright/packages/playwright-core/src/server/webkit/protocol.d.ts +++ b/playwright/packages/playwright-core/src/server/webkit/protocol.d.ts @@ -2210,6 +2210,123 @@ export module Protocol { nodeId: NodeId; pseudoId?: CSS.PseudoId; } + /** + * A structure holding media element statistics and configurations. + */ + export interface MediaStats { + audio?: AudioMediaStats; + video?: VideoMediaStats; + /** + * The ratio between physical screen pixels and CSS pixels. + */ + devicePixelRatio?: number; + /** + * The viewport size occupied by the media element. + */ + viewport?: ViewportSize; + quality?: VideoPlaybackQuality; + /** + * The source type of the media element. + */ + source?: string; + } + /** + * A structure holding media element's audio-specific statistics and configurations. + */ + export interface AudioMediaStats { + /** + * The data rate of the primary audio track in bits/s. + */ + bitrate: number; + /** + * The codec string of the primary audio track. (E.g., "hvc1.1.6.L123.B0") + */ + codec: string; + /** + * A human readable version of the `codec` parameter. + */ + humanReadableCodecString: string; + /** + * The number of audio channels in the primary audio track. + */ + numberOfChannels: number; + /** + * The sample rate of the primary audio track in hertz. + */ + sampleRate: number; + } + /** + * A structure holding media element's audio-specific statistics and configurations. + */ + export interface VideoMediaStats { + /** + * The data rate of the video track in bits/s. + */ + bitrate: number; + /** + * The codec string of the video track. (E.g., "hvc1.1.6.L123.B0") + */ + codec: string; + /** + * A human readable version of the `codec` parameter. + */ + humanReadableCodecString: string; + colorSpace: VideoColorSpace; + /** + * The nominal frame rate of video track in frames per second. + */ + framerate: number; + /** + * The native height of the video track in CSS pixels + */ + height: number; + /** + * The native width of the video track in CSS pixels + */ + width: number; + } + /** + * WebCodecs VideoColorSpace + */ + export interface VideoColorSpace { + /** + * A flag indicating whether the colorspace is Full range (true) or Video range (false) + */ + fullRange?: boolean; + /** + * The matrix specification of the colorspace + */ + matrix?: string; + /** + * The color primaries specification of the colorspace + */ + primaries?: string; + /** + * The transfer function specification of the colorspace + */ + transfer?: string; + } + /** + * A count of frames enqueued for display by the media element, and a subset count of dropped and display composited frames. + */ + export interface VideoPlaybackQuality { + /** + * The number of frames of the total which were composited by the display. + */ + displayCompositedVideoFrames: number; + /** + * The number of frames of the total which were dropped without being displayed. + */ + droppedVideoFrames: number; + /** + * The total number of frames enqueued for display by the media element. + */ + totalVideoFrames: number; + } + export interface ViewportSize { + width: number; + height: number; + } /** * Data to construct File object. */ @@ -3216,6 +3333,21 @@ export module Protocol { } export type setAllowEditingUserAgentShadowTreesReturnValue = { } + /** + * Returns media stats for the selected node. + */ + export type getMediaStatsParameters = { + /** + * Id of the node to retrieve mediastats for. + */ + nodeId: NodeId; + } + export type getMediaStatsReturnValue = { + /** + * An interleaved array of node attribute names and values. + */ + mediaStats: MediaStats; + } /** * Returns node description. */ @@ -9305,6 +9437,7 @@ the top of the viewport and Y increases as it proceeds towards the bottom of the "DOM.focus": DOM.focusParameters; "DOM.setInspectedNode": DOM.setInspectedNodeParameters; "DOM.setAllowEditingUserAgentShadowTrees": DOM.setAllowEditingUserAgentShadowTreesParameters; + "DOM.getMediaStats": DOM.getMediaStatsParameters; "DOM.describeNode": DOM.describeNodeParameters; "DOM.scrollIntoViewIfNeeded": DOM.scrollIntoViewIfNeededParameters; "DOM.getContentQuads": DOM.getContentQuadsParameters; @@ -9614,6 +9747,7 @@ the top of the viewport and Y increases as it proceeds towards the bottom of the "DOM.focus": DOM.focusReturnValue; "DOM.setInspectedNode": DOM.setInspectedNodeReturnValue; "DOM.setAllowEditingUserAgentShadowTrees": DOM.setAllowEditingUserAgentShadowTreesReturnValue; + "DOM.getMediaStats": DOM.getMediaStatsReturnValue; "DOM.describeNode": DOM.describeNodeReturnValue; "DOM.scrollIntoViewIfNeeded": DOM.scrollIntoViewIfNeededReturnValue; "DOM.getContentQuads": DOM.getContentQuadsReturnValue; diff --git a/playwright/packages/playwright-core/src/server/webkit/wkConnection.ts b/playwright/packages/playwright-core/src/server/webkit/wkConnection.ts index ff92cf5770..3eb777fd73 100644 --- a/playwright/packages/playwright-core/src/server/webkit/wkConnection.ts +++ b/playwright/packages/playwright-core/src/server/webkit/wkConnection.ts @@ -19,8 +19,8 @@ import { EventEmitter } from 'events'; import { assert } from '../../utils'; import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import type { Protocol } from './protocol'; -import type { RecentLogsCollector } from '../../common/debugLogger'; -import { debugLogger } from '../../common/debugLogger'; +import type { RecentLogsCollector } from '../../utils/debugLogger'; +import { debugLogger } from '../../utils/debugLogger'; import type { ProtocolLogger } from '../types'; import { helper } from '../helper'; import { ProtocolError } from '../protocolError'; diff --git a/playwright/packages/playwright-core/src/server/webkit/wkPage.ts b/playwright/packages/playwright-core/src/server/webkit/wkPage.ts index 24d6ef63f6..038010f456 100644 --- a/playwright/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/playwright/packages/playwright-core/src/server/webkit/wkPage.ts @@ -42,7 +42,7 @@ import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './wkInput'; import { WKInterceptableRequest, WKRouteImpl } from './wkInterceptableRequest'; import { WKProvisionalPage } from './wkProvisionalPage'; import { WKWorkers } from './wkWorkers'; -import { debugLogger } from '../../common/debugLogger'; +import { debugLogger } from '../../utils/debugLogger'; import { ManualPromise } from '../../utils/manualPromise'; import { BrowserContext } from '../browserContext'; import { TargetClosedError } from '../errors'; @@ -786,7 +786,7 @@ export class WKPage implements PageDelegate { scripts.push('delete window.ondevicemotion'); scripts.push('delete window.ondeviceorientation'); } - scripts.push('if (!window.safari) window.safari = {};'); + scripts.push('if (!window.safari) window.safari = { pushNotification: { toString() { return "[object SafariRemoteNotification]"; } } };'); scripts.push('if (!window.GestureEvent) window.GestureEvent = function GestureEvent() {};'); for (const binding of this._page.allBindings()) diff --git a/playwright/packages/playwright-core/src/utils/comparators.ts b/playwright/packages/playwright-core/src/utils/comparators.ts index 89b5cb2976..ef63b2d376 100644 --- a/playwright/packages/playwright-core/src/utils/comparators.ts +++ b/playwright/packages/playwright-core/src/utils/comparators.ts @@ -21,7 +21,7 @@ import { compare } from '../image_tools/compare'; const { diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL } = require('../third_party/diff_match_patch'); import { PNG } from '../utilsBundle'; -export type ImageComparatorOptions = { threshold?: number, maxDiffPixels?: number, maxDiffPixelRatio?: number, _comparator?: string }; +export type ImageComparatorOptions = { threshold?: number, maxDiffPixels?: number, maxDiffPixelRatio?: number, comparator?: string }; export type ComparatorResult = { diff?: Buffer; errorMessage: string; } | null; export type Comparator = (actualBuffer: Buffer | string, expectedBuffer: Buffer, options?: any) => ComparatorResult; @@ -65,18 +65,18 @@ function compareImages(mimeType: string, actualBuffer: Buffer | string, expected } const diff = new PNG({ width: size.width, height: size.height }); let count; - if (options._comparator === 'ssim-cie94') { + if (options.comparator === 'ssim-cie94') { count = compare(expected.data, actual.data, diff.data, size.width, size.height, { // All ΔE* formulae are originally designed to have the difference of 1.0 stand for a "just noticeable difference" (JND). // See https://en.wikipedia.org/wiki/Color_difference#CIELAB_%CE%94E* maxColorDeltaE94: 1.0, }); - } else if ((options._comparator ?? 'pixelmatch') === 'pixelmatch') { + } else if ((options.comparator ?? 'pixelmatch') === 'pixelmatch') { count = pixelmatch(expected.data, actual.data, diff.data, size.width, size.height, { threshold: options.threshold ?? 0.2, }); } else { - throw new Error(`Configuration specifies unknown comparator "${options._comparator}"`); + throw new Error(`Configuration specifies unknown comparator "${options.comparator}"`); } const maxDiffPixels1 = options.maxDiffPixels; diff --git a/playwright/packages/playwright-core/src/common/debugLogger.ts b/playwright/packages/playwright-core/src/utils/debugLogger.ts similarity index 100% rename from playwright/packages/playwright-core/src/common/debugLogger.ts rename to playwright/packages/playwright-core/src/utils/debugLogger.ts diff --git a/playwright/packages/playwright-core/src/utils/fileUtils.ts b/playwright/packages/playwright-core/src/utils/fileUtils.ts index 751878a869..0b2f28143b 100644 --- a/playwright/packages/playwright-core/src/utils/fileUtils.ts +++ b/playwright/packages/playwright-core/src/utils/fileUtils.ts @@ -52,3 +52,7 @@ export async function copyFileAndMakeWritable(from: string, to: string) { export function sanitizeForFilePath(s: string) { return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-'); } + +export function toPosixPath(aPath: string): string { + return aPath.split(path.sep).join(path.posix.sep); +} diff --git a/playwright/packages/playwright-core/src/utils/hostPlatform.ts b/playwright/packages/playwright-core/src/utils/hostPlatform.ts index 1cf354d574..89b03f0065 100644 --- a/playwright/packages/playwright-core/src/utils/hostPlatform.ts +++ b/playwright/packages/playwright-core/src/utils/hostPlatform.ts @@ -67,7 +67,7 @@ function calculatePlatform(): { hostPlatform: HostPlatform, isOfficiallySupporte if (distroInfo?.id === 'ubuntu' || distroInfo?.id === 'pop' || distroInfo?.id === 'neon' || distroInfo?.id === 'tuxedo') { const isOfficiallySupportedPlatform = distroInfo?.id === 'ubuntu'; if (parseInt(distroInfo.version, 10) <= 19) - return { hostPlatform: ('ubuntu18.04' + archSuffix) as HostPlatform, isOfficiallySupportedPlatform }; + return { hostPlatform: ('ubuntu18.04' + archSuffix) as HostPlatform, isOfficiallySupportedPlatform: false }; if (parseInt(distroInfo.version, 10) <= 21) return { hostPlatform: ('ubuntu20.04' + archSuffix) as HostPlatform, isOfficiallySupportedPlatform }; return { hostPlatform: ('ubuntu22.04' + archSuffix) as HostPlatform, isOfficiallySupportedPlatform }; diff --git a/playwright/packages/playwright-core/src/utils/index.ts b/playwright/packages/playwright-core/src/utils/index.ts index a33e73ba01..a5219cda6c 100644 --- a/playwright/packages/playwright-core/src/utils/index.ts +++ b/playwright/packages/playwright-core/src/utils/index.ts @@ -32,6 +32,7 @@ export * from './network'; export * from './processLauncher'; export * from './profiler'; export * from './rtti'; +export * from './semaphore'; export * from './spawnAsync'; export * from './stackTrace'; export * from './task'; @@ -39,6 +40,8 @@ export * from './time'; export * from './timeoutRunner'; export * from './traceUtils'; export * from './userAgent'; +export * from './wsServer'; export * from './zipFile'; export * from './zones'; export * from './isomorphic/locatorGenerators'; +export * from './isomorphic/stringUtils'; diff --git a/playwright/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts b/playwright/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts index 12fa08e80d..f72ef27eb4 100644 --- a/playwright/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts +++ b/playwright/packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts @@ -48,8 +48,10 @@ function preprocess(str: string): number[] { if (code === 0xd && str.charCodeAt(i + 1) === 0xa) { code = 0xa; i++; } - if (code === 0xd || code === 0xc) code = 0xa; - if (code === 0x0) code = 0xfffd; + if (code === 0xd || code === 0xc) + code = 0xa; + if (code === 0x0) + code = 0xfffd; if (between(code, 0xd800, 0xdbff) && between(str.charCodeAt(i + 1), 0xdc00, 0xdfff)) { // Decode a surrogate pair into an astral codepoint. const lead = code - 0xd800; @@ -63,7 +65,8 @@ function preprocess(str: string): number[] { } function stringFromCode(code: number) { - if (code <= 0xffff) return String.fromCharCode(code); + if (code <= 0xffff) + return String.fromCharCode(code); // Otherwise, encode astral char as surrogate pair. code -= Math.pow(2, 16); const lead = Math.floor(code / Math.pow(2, 10)) + 0xd800; @@ -107,8 +110,10 @@ export function tokenize(str1: string): CSSTokenInterface[] { num = 1; i += num; code = codepoint(i); - if (newline(code)) incrLineno(); - else column += num; + if (newline(code)) + incrLineno(); + else + column += num; // console.log('Consume '+i+' '+String.fromCharCode(code) + ' 0x' + code.toString(16)); return true; }; @@ -125,7 +130,8 @@ export function tokenize(str1: string): CSSTokenInterface[] { return true; }; const eof = function(codepoint?: number): boolean { - if (codepoint === undefined) codepoint = code; + if (codepoint === undefined) + codepoint = code; return codepoint === -1; }; const donothing = function() { }; @@ -138,12 +144,14 @@ export function tokenize(str1: string): CSSTokenInterface[] { consumeComments(); consume(); if (whitespace(code)) { - while (whitespace(next())) consume(); + while (whitespace(next())) + consume(); return new WhitespaceToken(); } else if (code === 0x22) {return consumeAStringToken();} else if (code === 0x23) { if (namechar(next()) || areAValidEscape(next(1), next(2))) { const token = new HashToken(''); - if (wouldStartAnIdentifier(next(1), next(2), next(3))) token.type = 'id'; + if (wouldStartAnIdentifier(next(1), next(2), next(3))) + token.type = 'id'; token.value = consumeAName(); return token; } else { @@ -288,7 +296,8 @@ export function tokenize(str1: string): CSSTokenInterface[] { const str = consumeAName(); if (str.toLowerCase() === 'url' && next() === 0x28) { consume(); - while (whitespace(next(1)) && whitespace(next(2))) consume(); + while (whitespace(next(1)) && whitespace(next(2))) + consume(); if (next() === 0x22 || next() === 0x27) return new FunctionToken(str); else if (whitespace(next()) && (next(2) === 0x22 || next(2) === 0x27)) @@ -305,7 +314,8 @@ export function tokenize(str1: string): CSSTokenInterface[] { }; const consumeAStringToken = function(endingCodePoint?: number): CSSParserToken { - if (endingCodePoint === undefined) endingCodePoint = code; + if (endingCodePoint === undefined) + endingCodePoint = code; let string = ''; while (consume()) { if (code === endingCodePoint || eof()) { @@ -331,13 +341,16 @@ export function tokenize(str1: string): CSSTokenInterface[] { const consumeAURLToken = function(): CSSTokenInterface { const token = new URLToken(''); - while (whitespace(next())) consume(); - if (eof(next())) return token; + while (whitespace(next())) + consume(); + if (eof(next())) + return token; while (consume()) { if (code === 0x29 || eof()) { return token; } else if (whitespace(code)) { - while (whitespace(next())) consume(); + while (whitespace(next())) + consume(); if (next() === 0x29 || eof(next())) { consume(); return token; @@ -379,9 +392,11 @@ export function tokenize(str1: string): CSSTokenInterface[] { break; } } - if (whitespace(next())) consume(); + if (whitespace(next())) + consume(); let value = parseInt(digits.map(function(x) { return String.fromCharCode(x); }).join(''), 16); - if (value > maximumallowedcodepoint) value = 0xfffd; + if (value > maximumallowedcodepoint) + value = 0xfffd; return value; } else if (eof()) { return 0xfffd; @@ -391,8 +406,10 @@ export function tokenize(str1: string): CSSTokenInterface[] { }; const areAValidEscape = function(c1: number, c2: number) { - if (c1 !== 0x5c) return false; - if (newline(c2)) return false; + if (c1 !== 0x5c) + return false; + if (newline(c2)) + return false; return true; }; const startsWithAValidEscape = function() { @@ -416,11 +433,14 @@ export function tokenize(str1: string): CSSTokenInterface[] { const wouldStartANumber = function(c1: number, c2: number, c3: number) { if (c1 === 0x2b || c1 === 0x2d) { - if (digit(c2)) return true; - if (c2 === 0x2e && digit(c3)) return true; + if (digit(c2)) + return true; + if (c2 === 0x2e && digit(c3)) + return true; return false; } else if (c1 === 0x2e) { - if (digit(c2)) return true; + if (digit(c2)) + return true; return false; } else if (digit(c1)) { return true; @@ -519,7 +539,8 @@ export function tokenize(str1: string): CSSTokenInterface[] { while (!eof(next())) { tokens.push(consumeAToken()); iterationCount++; - if (iterationCount > str.length * 2) throw new Error("I'm infinite-looping!"); + if (iterationCount > str.length * 2) + throw new Error("I'm infinite-looping!"); } return tokens; } diff --git a/playwright/packages/playwright-core/src/utils/isomorphic/locatorParser.ts b/playwright/packages/playwright-core/src/utils/isomorphic/locatorParser.ts index 886574e580..d9bbca2f7e 100644 --- a/playwright/packages/playwright-core/src/utils/isomorphic/locatorParser.ts +++ b/playwright/packages/playwright-core/src/utils/isomorphic/locatorParser.ts @@ -220,14 +220,17 @@ export function locatorOrSelectorAsSelector(language: Language, locator: string, try { const { selector, preferredQuote } = parseLocator(locator, testIdAttributeName); const locators = asLocators(language, selector, undefined, undefined, preferredQuote); - const digest = digestForComparison(locator); - if (locators.some(candidate => digestForComparison(candidate) === digest)) + const digest = digestForComparison(language, locator); + if (locators.some(candidate => digestForComparison(language, candidate) === digest)) return selector; } catch (e) { } return ''; } -function digestForComparison(locator: string) { - return locator.replace(/\s/g, '').replace(/["`]/g, '\''); +function digestForComparison(language: Language, locator: string) { + locator = locator.replace(/\s/g, ''); + if (language === 'javascript') + locator = locator.replace(/\\?["`]/g, '\''); + return locator; } diff --git a/playwright/packages/playwright-core/src/utils/isomorphic/stringUtils.ts b/playwright/packages/playwright-core/src/utils/isomorphic/stringUtils.ts index d51ecf8d41..19953ef5bb 100644 --- a/playwright/packages/playwright-core/src/utils/isomorphic/stringUtils.ts +++ b/playwright/packages/playwright-core/src/utils/isomorphic/stringUtils.ts @@ -115,4 +115,9 @@ export function trimString(input: string, cap: number, suffix: string = ''): str export function trimStringWithEllipsis(input: string, cap: number): string { return trimString(input, cap, '\u2026'); -} \ No newline at end of file +} + +export function escapeRegExp(s: string) { + // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} diff --git a/playwright/packages/playwright-core/src/utils/network.ts b/playwright/packages/playwright-core/src/utils/network.ts index deb12d2c59..bf834abaa0 100644 --- a/playwright/packages/playwright-core/src/utils/network.ts +++ b/playwright/packages/playwright-core/src/utils/network.ts @@ -169,6 +169,38 @@ export function createHttpsServer(...args: any[]): https.Server { return server; } +export async function isURLAvailable(url: URL, ignoreHTTPSErrors: boolean, onLog?: (data: string) => void, onStdErr?: (data: string) => void) { + let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr); + if (statusCode === 404 && url.pathname === '/') { + const indexUrl = new URL(url); + indexUrl.pathname = '/index.html'; + statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onLog, onStdErr); + } + return statusCode >= 200 && statusCode < 404; +} + +async function httpStatusCode(url: URL, ignoreHTTPSErrors: boolean, onLog?: (data: string) => void, onStdErr?: (data: string) => void): Promise<number> { + return new Promise(resolve => { + onLog?.(`HTTP HEAD: ${url}`); + httpRequest({ + method: 'HEAD', + url: url.toString(), + headers: { Accept: '*/*' }, + rejectUnauthorized: !ignoreHTTPSErrors + }, res => { + res.resume(); + const statusCode = res.statusCode ?? 0; + onLog?.(`HTTP Status: ${statusCode}`); + resolve(statusCode); + }, error => { + if ((error as NodeJS.ErrnoException).code === 'DEPTH_ZERO_SELF_SIGNED_CERT') + onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`); + onLog?.(`Error while checking if ${url} is available: ${error.message}`); + resolve(0); + }); + }); +} + function decorateServer(server: http.Server | http.Server) { const sockets = new Set<net.Socket>(); server.on('connection', socket => { diff --git a/playwright/packages/playwright-core/src/utils/processLauncher.ts b/playwright/packages/playwright-core/src/utils/processLauncher.ts index 62219e286c..4e6c1030b2 100644 --- a/playwright/packages/playwright-core/src/utils/processLauncher.ts +++ b/playwright/packages/playwright-core/src/utils/processLauncher.ts @@ -121,6 +121,13 @@ function addProcessHandlerIfNeeded(name: 'exit' | 'SIGINT' | 'SIGTERM' | 'SIGHUP process.on(name, processHandlers[name]); } } +function removeProcessHandlersIfNeeded() { + if (killSet.size) + return; + for (const handler of installedHandlers) + process.off(handler, processHandlers[handler]); + installedHandlers.clear(); +} export async function launchProcess(options: LaunchProcessOptions): Promise<LaunchResult> { const stdio: ('ignore' | 'pipe')[] = options.stdio === 'pipe' ? ['ignore', 'pipe', 'pipe', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe']; @@ -178,6 +185,7 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun processClosed = true; gracefullyCloseSet.delete(gracefullyClose); killSet.delete(killProcessAndCleanup); + removeProcessHandlersIfNeeded(); options.onExit(exitCode, signal); // Cleanup as process exits. cleanup().then(fulfillCleanup); @@ -216,6 +224,7 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun function killProcess() { gracefullyCloseSet.delete(gracefullyClose); killSet.delete(killProcessAndCleanup); + removeProcessHandlersIfNeeded(); options.log(`[pid=${spawnedProcess.pid}] <kill>`); if (spawnedProcess.pid && !spawnedProcess.killed && !processClosed) { options.log(`[pid=${spawnedProcess.pid}] <will force kill>`); diff --git a/playwright/packages/playwright-core/src/utils/rtti.ts b/playwright/packages/playwright-core/src/utils/rtti.ts index f096ae8ee4..a18d3a450a 100644 --- a/playwright/packages/playwright-core/src/utils/rtti.ts +++ b/playwright/packages/playwright-core/src/utils/rtti.ts @@ -14,9 +14,7 @@ * limitations under the License. */ -export function isString(obj: any): obj is string { - return typeof obj === 'string' || obj instanceof String; -} +export { isString } from './isomorphic/stringUtils'; export function isRegExp(obj: any): obj is RegExp { return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]'; diff --git a/playwright/packages/playwright-core/src/utils/semaphore.ts b/playwright/packages/playwright-core/src/utils/semaphore.ts new file mode 100644 index 0000000000..538e44fd9c --- /dev/null +++ b/playwright/packages/playwright-core/src/utils/semaphore.ts @@ -0,0 +1,50 @@ +/** + * 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 { ManualPromise } from './manualPromise'; + +export class Semaphore { + private _max: number; + private _acquired = 0; + private _queue: ManualPromise[] = []; + + constructor(max: number) { + this._max = max; + } + + setMax(max: number) { + this._max = max; + } + + acquire(): Promise<void> { + const lock = new ManualPromise(); + this._queue.push(lock); + this._flush(); + return lock; + } + + release() { + --this._acquired; + this._flush(); + } + + private _flush() { + while (this._acquired < this._max && this._queue.length) { + ++this._acquired; + this._queue.shift()!.resolve(); + } + } +} diff --git a/playwright/packages/playwright-core/src/utils/timeoutRunner.ts b/playwright/packages/playwright-core/src/utils/timeoutRunner.ts index fb57ffc703..fc4db8aed9 100644 --- a/playwright/packages/playwright-core/src/utils/timeoutRunner.ts +++ b/playwright/packages/playwright-core/src/utils/timeoutRunner.ts @@ -21,7 +21,7 @@ export class TimeoutRunnerError extends Error {} type TimeoutRunnerData = { lastElapsedSync: number, - timer: NodeJS.Timer | undefined, + timer: NodeJS.Timeout | undefined, timeoutPromise: ManualPromise<any>, }; diff --git a/playwright/packages/playwright-core/src/utils/wsServer.ts b/playwright/packages/playwright-core/src/utils/wsServer.ts new file mode 100644 index 0000000000..63c0ed1fed --- /dev/null +++ b/playwright/packages/playwright-core/src/utils/wsServer.ts @@ -0,0 +1,149 @@ +/** + * 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 http from 'http'; +import type stream from 'stream'; +import { createHttpServer } from '../utils'; +import type { WebSocketServer, WebSocket } from '../utilsBundle'; +import { wsServer } from '../utilsBundle'; +import { debugLogger } from './debugLogger'; + +let lastConnectionId = 0; +const kConnectionSymbol = Symbol('kConnection'); + +export const perMessageDeflate = { + zlibDeflateOptions: { + level: 3, + }, + zlibInflateOptions: { + chunkSize: 10 * 1024 + }, + threshold: 10 * 1024, +}; + +export type WSConnection = { + close: () => Promise<void>; +}; + +export type WSServerDelegate = { + onHeaders?: (headers: string[]) => void; + onUpgrade?: (request: http.IncomingMessage, socket: stream.Duplex) => { error: string } | undefined; + onConnection: (request: http.IncomingMessage, url: URL, ws: WebSocket, id: string) => WSConnection; + onClose?(): Promise<void>; +}; + +export class WSServer { + private _wsServer: WebSocketServer | undefined; + server: http.Server | undefined; + private _delegate: WSServerDelegate; + + constructor(delegate: WSServerDelegate) { + this._delegate = delegate; + } + + async listen(port: number = 0, hostname: string | undefined, path: string): Promise<string> { + debugLogger.log('server', `Server started at ${new Date()}`); + + const server = createHttpServer((request: http.IncomingMessage, response: http.ServerResponse) => { + if (request.method === 'GET' && request.url === '/json') { + response.setHeader('Content-Type', 'application/json'); + response.end(JSON.stringify({ + wsEndpointPath: path, + })); + return; + } + response.end('Running'); + }); + server.on('error', error => debugLogger.log('server', String(error))); + this.server = server; + + const wsEndpoint = await new Promise<string>((resolve, reject) => { + server.listen(port, hostname, () => { + const address = server.address(); + if (!address) { + reject(new Error('Could not bind server socket')); + return; + } + const wsEndpoint = typeof address === 'string' ? `${address}${path}` : `ws://${hostname || 'localhost'}:${address.port}${path}`; + resolve(wsEndpoint); + }).on('error', reject); + }); + + debugLogger.log('server', 'Listening at ' + wsEndpoint); + + this._wsServer = new wsServer({ + noServer: true, + perMessageDeflate, + }); + + if (this._delegate.onHeaders) + this._wsServer.on('headers', headers => this._delegate.onHeaders!(headers)); + + server.on('upgrade', (request, socket, head) => { + const pathname = new URL('http://localhost' + request.url!).pathname; + if (pathname !== path) { + socket.write(`HTTP/${request.httpVersion} 400 Bad Request\r\n\r\n`); + socket.destroy(); + return; + } + const upgradeResult = this._delegate.onUpgrade?.(request, socket); + if (upgradeResult) { + socket.write(upgradeResult.error); + socket.destroy(); + return; + } + this._wsServer?.handleUpgrade(request, socket, head, ws => this._wsServer?.emit('connection', ws, request)); + }); + + this._wsServer.on('connection', (ws, request) => { + debugLogger.log('server', 'Connected client ws.extension=' + ws.extensions); + const url = new URL('http://localhost' + (request.url || '')); + const id = String(++lastConnectionId); + debugLogger.log('server', `[${id}] serving connection: ${request.url}`); + const connection = this._delegate.onConnection(request, url, ws, id); + (ws as any)[kConnectionSymbol] = connection; + }); + + return wsEndpoint; + } + + async close() { + const server = this._wsServer; + if (!server) + return; + debugLogger.log('server', 'closing websocket server'); + const waitForClose = new Promise(f => server.close(f)); + // First disconnect all remaining clients. + await Promise.all(Array.from(server.clients).map(async ws => { + const connection = (ws as any)[kConnectionSymbol] as WSConnection | undefined; + if (connection) + await connection.close(); + try { + ws.terminate(); + } catch (e) { + } + })); + await waitForClose; + debugLogger.log('server', 'closing http server'); + if (this.server) + await new Promise(f => this.server!.close(f)); + this._wsServer = undefined; + this.server = undefined; + debugLogger.log('server', 'closed server'); + + await this._delegate.onClose?.(); + } +} diff --git a/playwright/packages/playwright-core/types/protocol.d.ts b/playwright/packages/playwright-core/types/protocol.d.ts index 49adee0f8b..83b8d235ce 100644 --- a/playwright/packages/playwright-core/types/protocol.d.ts +++ b/playwright/packages/playwright-core/types/protocol.d.ts @@ -245,7 +245,7 @@ If omitted, the full tree is returned. depth?: number; /** * The frame for whose document the AX tree should be retrieved. -If omited, the root frame is used. +If omitted, the root frame is used. */ frameId?: Page.FrameId; } @@ -305,7 +305,7 @@ If omitted, the root frame is used. /** * Query a DOM node's accessibility subtree for accessible name and role. This command computes the name and role for all nodes in the subtree, including those that are -ignored for accessibility, and returns those that mactch the specified name and role. If no DOM +ignored for accessibility, and returns those that match the specified name and role. If no DOM node is specified, or the DOM node does not exist, the command returns an error. If neither `accessibleName` or `role` is specified, it returns all the accessibility nodes in the subtree. */ @@ -367,6 +367,9 @@ including nodes that are ignored for accessibility. playbackRate: number; /** * `Animation`'s start time. +Milliseconds for time based animations and +percentage [0 - 100] for scroll driven animations +(i.e. when viewOrScrollTimeline exists). */ startTime: number; /** @@ -386,6 +389,39 @@ including nodes that are ignored for accessibility. animation/transition. */ cssId?: string; + /** + * View or scroll timeline + */ + viewOrScrollTimeline?: ViewOrScrollTimeline; + } + /** + * Timeline instance + */ + export interface ViewOrScrollTimeline { + /** + * Scroll container node + */ + sourceNodeId?: DOM.BackendNodeId; + /** + * Represents the starting scroll position of the timeline +as a length offset in pixels from scroll origin. + */ + startOffset?: number; + /** + * Represents the ending scroll position of the timeline +as a length offset in pixels from scroll origin. + */ + endOffset?: number; + /** + * The element whose principal box's visibility in the +scrollport defined the progress of the timeline. +Does not exist for animations with ScrollTimeline + */ + subjectNodeId?: DOM.BackendNodeId; + /** + * Orientation of the scroll + */ + axis: DOM.ScrollOrientation; } /** * AnimationEffect instance @@ -409,6 +445,9 @@ animation/transition. iterations: number; /** * `AnimationEffect`'s iteration duration. +Milliseconds for time based animations and +percentage [0 - 100] for scroll driven animations +(i.e. when viewOrScrollTimeline exists). */ duration: number; /** @@ -675,7 +714,7 @@ may be used by the front-end as additional context. request?: AffectedRequest; } export type MixedContentResolutionStatus = "MixedContentBlocked"|"MixedContentAutomaticallyUpgraded"|"MixedContentWarning"; - export type MixedContentResourceType = "AttributionSrc"|"Audio"|"Beacon"|"CSPReport"|"Download"|"EventSource"|"Favicon"|"Font"|"Form"|"Frame"|"Image"|"Import"|"Manifest"|"Ping"|"PluginData"|"PluginResource"|"Prefetch"|"Resource"|"Script"|"ServiceWorker"|"SharedWorker"|"SpeculationRules"|"Stylesheet"|"Track"|"Video"|"Worker"|"XMLHttpRequest"|"XSLT"; + export type MixedContentResourceType = "AttributionSrc"|"Audio"|"Beacon"|"CSPReport"|"Download"|"EventSource"|"Favicon"|"Font"|"Form"|"Frame"|"Image"|"Import"|"JSON"|"Manifest"|"Ping"|"PluginData"|"PluginResource"|"Prefetch"|"Resource"|"Script"|"ServiceWorker"|"SharedWorker"|"SpeculationRules"|"Stylesheet"|"Track"|"Video"|"Worker"|"XMLHttpRequest"|"XSLT"; export interface MixedContentIssueDetails { /** * The type of resource causing the mixed content issue (css, js, iframe, @@ -1132,7 +1171,7 @@ Munich 81456 */ export interface AddressUI { /** - * A two dimension array containing the repesentation of values from an address profile. + * A two dimension array containing the representation of values from an address profile. */ addressFields: AddressFields[]; } @@ -1165,6 +1204,14 @@ Munich 81456 * The filling strategy */ fillingStrategy: FillingStrategy; + /** + * The frame the field belongs to + */ + frameId: Page.FrameId; + /** + * The form field's DOM node + */ + fieldId: DOM.BackendNodeId; } /** @@ -1363,7 +1410,7 @@ events afterwards if enabled and recording. */ windowState?: WindowState; } - export type PermissionType = "accessibilityEvents"|"audioCapture"|"backgroundSync"|"backgroundFetch"|"clipboardReadWrite"|"clipboardSanitizedWrite"|"displayCapture"|"durableStorage"|"flash"|"geolocation"|"idleDetection"|"localFonts"|"midi"|"midiSysex"|"nfc"|"notifications"|"paymentHandler"|"periodicBackgroundSync"|"protectedMediaIdentifier"|"sensors"|"storageAccess"|"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"|"windowManagement"; export type PermissionSetting = "granted"|"denied"|"prompt"; /** * Definition of PermissionDescriptor defined in the Permissions API: @@ -1536,7 +1583,7 @@ Note that userVisibleOnly = true is the only currently supported type. /** * Whether to allow all or deny all download requests, or use default Chrome behavior if available (otherwise deny). |allowAndName| allows download and names files according to -their dowmload guids. +their download guids. */ behavior: "deny"|"allow"|"allowAndName"|"default"; /** @@ -1884,7 +1931,7 @@ pseudo-classes. frameId: Page.FrameId; /** * Stylesheet resource URL. Empty if this is a constructed stylesheet created using -new CSSStyleSheet() (but non-empty if this is a constructed sylesheet imported +new CSSStyleSheet() (but non-empty if this is a constructed stylesheet imported as a CSS module script). */ sourceURL: string; @@ -2609,6 +2656,12 @@ position specified by `location`. * Text position of a new rule in the target style sheet. */ location: SourceRange; + /** + * NodeId for the DOM node in whose context custom property declarations for registered properties should be +validated. If omitted, declarations in the new rule text can only be validated statically, which may produce +incorrect results if the declaration contains a var() for example. + */ + nodeForPropertySyntaxValidation?: DOM.NodeId; } export type addRuleReturnValue = { /** @@ -2983,6 +3036,12 @@ property */ export type setStyleTextsParameters = { edits: StyleDeclarationEdit[]; + /** + * NodeId for the DOM node in whose context custom property declarations for registered properties should be +validated. If omitted, declarations in the new rule text can only be validated statically, which may produce +incorrect results if the declaration contains a var() for example. + */ + nodeForPropertySyntaxValidation?: DOM.NodeId; } export type setStyleTextsReturnValue = { /** @@ -3362,6 +3421,10 @@ front-end. * ContainerSelector logical axes */ export type LogicalAxes = "Inline"|"Block"|"Both"; + /** + * Physical scroll orientation + */ + export type ScrollOrientation = "horizontal"|"vertical"; /** * DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type. @@ -3935,7 +3998,7 @@ be called for that search. */ export type getAttributesParameters = { /** - * Id of the node to retrieve attibutes for. + * Id of the node to retrieve attributes for. */ nodeId: NodeId; } @@ -5663,14 +5726,14 @@ resource fetches. */ export type VirtualTimePolicy = "advance"|"pause"|"pauseIfNetworkFetchesPending"; /** - * Used to specify User Agent Cient Hints to emulate. See https://wicg.github.io/ua-client-hints + * Used to specify User Agent Client Hints to emulate. See https://wicg.github.io/ua-client-hints */ export interface UserAgentBrandVersion { brand: string; version: string; } /** - * Used to specify User Agent Cient Hints to emulate. See https://wicg.github.io/ua-client-hints + * Used to specify User Agent Client Hints to emulate. See https://wicg.github.io/ua-client-hints Missing optional values will be filled in by the target with what it would normally use. */ export interface UserAgentMetadata { @@ -5974,7 +6037,7 @@ Sensor.start() will attempt to use a real sensor instead. export type setSensorOverrideEnabledReturnValue = { } /** - * Updates the sensor readings reported by a sensor type previously overriden + * Updates the sensor readings reported by a sensor type previously overridden by setSensorOverrideEnabled. */ export type setSensorOverrideReadingsParameters = { @@ -6097,8 +6160,9 @@ restores default host system locale. */ export type setTimezoneOverrideParameters = { /** - * The timezone identifier. If empty, disables the override and -restores default host system timezone. + * The timezone identifier. List of supported timezones: +https://source.chromium.org/chromium/chromium/deps/icu.git/+/faee8bc70570192d82d2978a71e2a615788597d1:source/data/misc/metaZones.txt +If empty, disables the override and restores default host system timezone. */ timezoneId: string; } @@ -6139,6 +6203,7 @@ on Android. } /** * Allows overriding user agent with the given string. +`userAgentMetadata` must be set for Client Hint headers to be sent. */ export type setUserAgentOverrideParameters = { /** @@ -6284,7 +6349,7 @@ display. Reported for diagnostic uses, may be removed in the future. */ handle: StreamHandle; /** - * Seek to the specified offset before reading (if not specificed, proceed with offset + * Seek to the specified offset before reading (if not specified, proceed with offset following the last read). Some types of streams may only support sequential reads. */ offset?: number; @@ -6910,7 +6975,7 @@ for example an emoji keyboard or an IME. export type insertTextReturnValue = { } /** - * This method sets the current candidate text for ime. + * This method sets the current candidate text for IME. Use imeCommitComposition to commit the final text. Use imeSetComposition with empty string as text to cancel composition. */ @@ -7409,7 +7474,7 @@ transform/scrolling purposes only. } export type layerTreeDidChangePayload = { /** - * Layer tree, absent if not in the comspositing mode. + * Layer tree, absent if not in the compositing mode. */ layers?: Layer[]; } @@ -8020,7 +8085,7 @@ passed by the developer (e.g. via "fetch") as understood by the backend. trustTokenParams?: TrustTokenParams; /** * True if this resource request is considered to be the 'same site' as the -request correspondinfg to the main frame. +request corresponding to the main frame. */ isSameSite?: boolean; } @@ -8172,8 +8237,13 @@ records. * The reason why Chrome uses a specific transport protocol for HTTP semantics. */ export type AlternateProtocolUsage = "alternativeJobWonWithoutRace"|"alternativeJobWonRace"|"mainJobWonRace"|"mappingMissing"|"broken"|"dnsAlpnH3JobWonWithoutRace"|"dnsAlpnH3JobWonRace"|"unspecifiedReason"; + /** + * Source of service worker router. + */ + export type ServiceWorkerRouterSource = "network"|"cache"|"fetch-event"|"race-network-and-fetch-handler"; export interface ServiceWorkerRouterInfo { ruleIdMatched: number; + matchedSourceType: ServiceWorkerRouterSource; } /** * HTTP response data. @@ -8203,6 +8273,10 @@ records. * Resource mimeType as determined by the browser. */ mimeType: string; + /** + * Resource charset as determined by the browser (if applicable). + */ + charset: string; /** * Refined HTTP request headers that were actually transmitted over the network. */ @@ -8240,7 +8314,7 @@ records. */ fromPrefetchCache?: boolean; /** - * Infomation about how Service Worker Static Router was used. + * Information about how Service Worker Static Router was used. */ serviceWorkerRouterInfo?: ServiceWorkerRouterInfo; /** @@ -8469,6 +8543,10 @@ of the request to the endpoint that set the cookie. * Types of reasons why a cookie may not be sent with a request. */ export type CookieBlockedReason = "SecureOnly"|"NotOnPath"|"DomainMismatch"|"SameSiteStrict"|"SameSiteLax"|"SameSiteUnspecifiedTreatedAsLax"|"SameSiteNoneInsecure"|"UserPreferences"|"ThirdPartyPhaseout"|"ThirdPartyBlockedInFirstPartySet"|"UnknownError"|"SchemefulSameSiteStrict"|"SchemefulSameSiteLax"|"SchemefulSameSiteUnspecifiedTreatedAsLax"|"SamePartyFromCrossPartyContext"|"NameValuePairExceedsMaxSize"; + /** + * 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"; /** * A cookie which was not stored from a response with the corresponding reason. */ @@ -8490,17 +8568,37 @@ errors. cookie?: Cookie; } /** - * A cookie with was not sent with a request with the corresponding reason. + * A cookie should have been blocked by 3PCD but is exempted and stored from a response with the +corresponding reason. A cookie could only have at most one exemption reason. */ - export interface BlockedCookieWithReason { + export interface ExemptedSetCookieWithReason { /** - * The reason(s) the cookie was blocked. + * The reason the cookie was exempted. */ - blockedReasons: CookieBlockedReason[]; + exemptionReason: CookieExemptionReason; + /** + * The cookie object representing the cookie. + */ + cookie: Cookie; + } + /** + * A cookie associated with the request which may or may not be sent with it. +Includes the cookies itself and reasons for blocking or exemption. + */ + export interface AssociatedCookie { /** * The cookie object representing the cookie which was not sent. */ cookie: Cookie; + /** + * The reason(s) the cookie was blocked. If empty means the cookie is included. + */ + blockedReasons: CookieBlockedReason[]; + /** + * The reason the cookie should have been blocked by 3PCD but is exempted. A cookie could +only have at most one exemption reason. + */ + exemptionReason?: CookieExemptionReason; } /** * Cookie parameter object @@ -8739,7 +8837,7 @@ https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges- */ securityDetails?: SecurityDetails; /** - * Errors occurred while handling the signed exchagne. + * Errors occurred while handling the signed exchange. */ errors?: SignedExchangeError[]; } @@ -8883,6 +8981,10 @@ CORB and streaming. * Actual bytes received (might be less than dataLength for compressed encodings). */ encodedDataLength: number; + /** + * Data that was received. + */ + data?: binary; } /** * Fired when EventSource message is received. @@ -8926,7 +9028,7 @@ CORB and streaming. */ type: ResourceType; /** - * User friendly error message. + * Error message. List of network errors: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ errorText: string; /** @@ -9326,9 +9428,9 @@ or requestWillBeSentExtraInfo will be fired first for the same request. requestId: RequestId; /** * A list of cookies potentially associated to the requested URL. This includes both cookies sent with -the request and the ones not sent; the latter are distinguished by having blockedReason field set. +the request and the ones not sent; the latter are distinguished by having blockedReasons field set. */ - associatedCookies: BlockedCookieWithReason[]; + associatedCookies: AssociatedCookie[]; /** * Raw request headers as they will be sent over the wire. */ @@ -9388,9 +9490,14 @@ Only sent when partitioned cookies are enabled. */ cookiePartitionKey?: string; /** - * True if partitioned cookies are enabled, but the partition key is not serializeable to string. + * True if partitioned cookies are enabled, but the partition key is not serializable to string. */ cookiePartitionKeyOpaque?: boolean; + /** + * A list of cookies which should have been blocked by 3PCD but are exempted and stored from +the response with the corresponding reason. + */ + exemptedCookies?: ExemptedSetCookieWithReason[]; } /** * Fired exactly once for each Trust Token operation. Depending on @@ -9621,7 +9728,7 @@ authChallenge. export type continueInterceptedRequestReturnValue = { } /** - * Deletes browser cookies with matching name and url or domain/path pair. + * Deletes browser cookies with matching name and url or domain/path/partitionKey pair. */ export type deleteCookiesParameters = { /** @@ -9641,6 +9748,11 @@ provided URL. * If specified, deletes only cookies with the exact path. */ path?: string; + /** + * If specified, deletes only cookies with the the given name and partitionKey where domain +matches provided URL. + */ + partitionKey?: string; } export type deleteCookiesReturnValue = { } @@ -10019,6 +10131,22 @@ continueInterceptedRequest call. } export type setUserAgentOverrideReturnValue = { } + /** + * Enables streaming of the response for the given requestId. +If enabled, the dataReceived event contains the data that was received during streaming. + */ + export type streamResourceContentParameters = { + /** + * Identifier of the request to stream. + */ + requestId: RequestId; + } + export type streamResourceContentReturnValue = { + /** + * Data that has been buffered until streaming is enabled. + */ + bufferedData: binary; + } /** * Returns information about the COEP/COOP isolation status. */ @@ -10075,7 +10203,7 @@ should be omitted for worker targets. */ export interface SourceOrderConfig { /** - * the color to outline the givent element in. + * the color to outline the given element in. */ parentOutlineColor: DOM.RGBA; /** @@ -10408,7 +10536,7 @@ should be omitted for worker targets. */ showCSS: boolean; /** - * Seleted platforms to show the overlay. + * Selected platforms to show the overlay. */ selectedPlatform: string; /** @@ -10576,8 +10704,8 @@ user manually inspects an element. } /** * Highlights owner element of the frame with given id. -Deprecated: Doesn't work reliablity and cannot be fixed due to process -separatation (the owner node might be in a different process). Determine +Deprecated: Doesn't work reliably and cannot be fixed due to process +separation (the owner node might be in a different process). Determine the owner node in the client and use highlightNode. */ export type highlightFrameParameters = { @@ -10937,7 +11065,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"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"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-factor"|"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"|"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"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"storage-access"|"sync-xhr"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-printing"|"web-share"|"window-management"|"window-placement"|"xr-spatial-tracking"; + export type PermissionsPolicyFeature = "accelerometer"|"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-factor"|"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"|"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"|"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"|"window-placement"|"xr-spatial-tracking"; /** * Reason for a permissions policy feature to be disabled. */ @@ -11189,7 +11317,7 @@ Example URLs: http://www.google.com/file.html -> "google.com" */ message: string; /** - * If criticial, this is a non-recoverable parse error. + * If critical, this is a non-recoverable parse error. */ critical: number; /** @@ -11396,7 +11524,7 @@ Example URLs: http://www.google.com/file.html -> "google.com" eager?: boolean; } /** - * Enum of possible auto-reponse for permisison / prompt dialogs. + * Enum of possible auto-response for permission / prompt dialogs. */ export type AutoResponseMode = "none"|"autoAccept"|"autoReject"|"autoOptOut"; /** @@ -11406,7 +11534,7 @@ Example URLs: http://www.google.com/file.html -> "google.com" /** * 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"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"DedicatedWorkerOrWorklet"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"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"; + 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"|"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"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"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"; /** * Types of not restored reasons for back-forward cache. */ @@ -11683,7 +11811,7 @@ open. */ type: DialogType; /** - * True iff browser is capable showing or acting on the given dialog. When browser has no + * True if browser is capable showing or acting on the given dialog. When browser has no dialog handler for given target, calling alert while Page domain is engaged will stall the page execution. Execution can be resumed via calling Page.handleJavaScriptDialog. */ @@ -11716,7 +11844,7 @@ when bfcache navigation fails. */ export type backForwardCacheNotUsedPayload = { /** - * The loader id for the associated navgation. + * The loader id for the associated navigation. */ loaderId: Network.LoaderId; /** @@ -12664,7 +12792,7 @@ https://github.com/WICG/web-lifecycle/ } /** * Requests backend to produce compilation cache for the specified scripts. -`scripts` are appeneded to the list of scripts for which the cache +`scripts` are appended to the list of scripts for which the cache would be produced. The list may be reset during page navigation. When script with a matching URL is encountered, the cache is optionally produced upon backend discretion, based on internal heuristics. @@ -12883,7 +13011,7 @@ https://w3c.github.io/performance-timeline/#dom-performanceobserver. frameId: Page.FrameId; /** * The event type, as specified in https://w3c.github.io/performance-timeline/#dom-performanceentry-entrytype -This determines which of the optional "details" fiedls is present. +This determines which of the optional "details" fields is present. */ type: string; /** @@ -12993,7 +13121,7 @@ https://www.w3.org/TR/mixed-content/#categories */ certificateNetworkError?: string; /** - * True if the certificate uses a weak signature aglorithm. + * True if the certificate uses a weak signature algorithm. */ certificateHasWeakSignature: boolean; /** @@ -13389,10 +13517,22 @@ Tokens from that issuer. issuerOrigin: string; count: number; } + /** + * Protected audience interest group auction identifier. + */ + export type InterestGroupAuctionId = string; /** * Enum of interest group access types. */ - export type InterestGroupAccessType = "join"|"leave"|"update"|"loaded"|"bid"|"win"|"additionalBid"|"additionalBidWin"|"clear"; + export type InterestGroupAccessType = "join"|"leave"|"update"|"loaded"|"bid"|"win"|"additionalBid"|"additionalBidWin"|"topLevelBid"|"topLevelAdditionalBid"|"clear"; + /** + * Enum of auction events. + */ + export type InterestGroupAuctionEventType = "started"|"configResolved"; + /** + * Enum of network fetches auctions can do. + */ + export type InterestGroupAuctionFetchType = "bidderJs"|"bidderWasm"|"sellerJs"|"bidderTrustedSignals"|"sellerTrustedSignals"; /** * Ad advertising element inside an interest group. */ @@ -13432,9 +13572,23 @@ Tokens from that issuer. * Details for an origin's shared storage. */ export interface SharedStorageMetadata { + /** + * Time when the origin's shared storage was last created. + */ creationTime: Network.TimeSinceEpoch; + /** + * Number of key-value pairs stored in origin's shared storage. + */ length: number; + /** + * Current amount of bits of entropy remaining in the navigation budget. + */ remainingBudget: number; + /** + * Total number of bytes stored as key-value pairs in origin's shared +storage. + */ + bytesUsed: number; } /** * Pair of reporting metadata details for a candidate URL for `selectURL()`. @@ -13536,6 +13690,17 @@ SharedStorageAccessType.workletSet. key: string; values: string[]; } + export interface AttributionReportingFilterConfig { + filterValues: AttributionReportingFilterDataEntry[]; + /** + * duration in seconds + */ + lookbackWindow?: number; + } + export interface AttributionReportingFilterPair { + filters: AttributionReportingFilterConfig[]; + notFilters: AttributionReportingFilterConfig[]; + } export interface AttributionReportingAggregationKeysEntry { key: string; value: UnsignedInt128AsBase16; @@ -13582,6 +13747,48 @@ int triggerDataMatching: AttributionReportingTriggerDataMatching; } export type AttributionReportingSourceRegistrationResult = "success"|"internalError"|"insufficientSourceCapacity"|"insufficientUniqueDestinationCapacity"|"excessiveReportingOrigins"|"prohibitedByBrowserPolicy"|"successNoised"|"destinationReportingLimitReached"|"destinationGlobalLimitReached"|"destinationBothLimitsReached"|"reportingOriginsPerSiteLimitReached"|"exceedsMaxChannelCapacity"; + export type AttributionReportingSourceRegistrationTimeConfig = "include"|"exclude"; + export interface AttributionReportingAggregatableValueDictEntry { + key: string; + /** + * number instead of integer because not all uint32 can be represented by +int + */ + value: number; + } + export interface AttributionReportingAggregatableValueEntry { + values: AttributionReportingAggregatableValueDictEntry[]; + filters: AttributionReportingFilterPair; + } + export interface AttributionReportingEventTriggerData { + data: UnsignedInt64AsBase10; + priority: SignedInt64AsBase10; + dedupKey?: UnsignedInt64AsBase10; + filters: AttributionReportingFilterPair; + } + export interface AttributionReportingAggregatableTriggerData { + keyPiece: UnsignedInt128AsBase16; + sourceKeys: string[]; + filters: AttributionReportingFilterPair; + } + export interface AttributionReportingAggregatableDedupKey { + dedupKey?: UnsignedInt64AsBase10; + filters: AttributionReportingFilterPair; + } + export interface AttributionReportingTriggerRegistration { + filters: AttributionReportingFilterPair; + debugKey?: UnsignedInt64AsBase10; + aggregatableDedupKeys: AttributionReportingAggregatableDedupKey[]; + eventTriggerData: AttributionReportingEventTriggerData[]; + aggregatableTriggerData: AttributionReportingAggregatableTriggerData[]; + aggregatableValues: AttributionReportingAggregatableValueEntry[]; + debugReporting: boolean; + aggregationCoordinatorOrigin?: string; + sourceRegistrationTimeConfig: AttributionReportingSourceRegistrationTimeConfig; + triggerContextId?: 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"; /** * A cache's contents have been modified. @@ -13664,13 +13871,61 @@ int bucketId: string; } /** - * One of the interest groups was accessed by the associated page. + * One of the interest groups was accessed. Note that these events are global +to all targets sharing an interest group store. */ export type interestGroupAccessedPayload = { accessTime: Network.TimeSinceEpoch; type: InterestGroupAccessType; ownerOrigin: string; name: string; + /** + * For topLevelBid/topLevelAdditionalBid, and when appropriate, +win and additionalBidWin + */ + componentSellerOrigin?: string; + /** + * For bid or somethingBid event, if done locally and not on a server. + */ + bid?: number; + bidCurrency?: string; + /** + * For non-global events --- links to interestGroupAuctionEvent + */ + uniqueAuctionId?: InterestGroupAuctionId; + } + /** + * An auction involving interest groups is taking place. These events are +target-specific. + */ + export type interestGroupAuctionEventOccurredPayload = { + eventTime: Network.TimeSinceEpoch; + type: InterestGroupAuctionEventType; + uniqueAuctionId: InterestGroupAuctionId; + /** + * Set for child auctions. + */ + parentAuctionId?: InterestGroupAuctionId; + /** + * Set for started and configResolved + */ + auctionConfig?: { [key: string]: string }; + } + /** + * Specifies which auctions a particular network fetch may be related to, and +in what role. Note that it is not ordered with respect to +Network.requestWillBeSent (but will happen before loadingFinished +loadingFailed). + */ + export type interestGroupAuctionNetworkRequestCreatedPayload = { + type: InterestGroupAuctionFetchType; + requestId: Network.RequestId; + /** + * This is the set of the auctions using the worklet that issued this +request. In the case of trusted signals, it's possible that only some of +them actually care about the keys being queried. + */ + auctions: InterestGroupAuctionId[]; } /** * Shared storage was accessed by the associated page. @@ -13694,7 +13949,7 @@ The following parameters are included in all events. */ ownerOrigin: string; /** - * The sub-parameters warapped by `params` are all optional and their + * The sub-parameters wrapped by `params` are all optional and their presence/absence depends on `type`. */ params: SharedStorageAccessParams; @@ -13705,14 +13960,15 @@ presence/absence depends on `type`. export type storageBucketDeletedPayload = { bucketId: string; } - /** - * TODO(crbug.com/1458532): Add other Attribution Reporting events, e.g. -trigger registration. - */ export type attributionReportingSourceRegisteredPayload = { registration: AttributionReportingSourceRegistration; result: AttributionReportingSourceRegistrationResult; } + export type attributionReportingTriggerRegisteredPayload = { + registration: AttributionReportingTriggerRegistration; + eventLevel: AttributionReportingEventLevelResult; + aggregatable: AttributionReportingAggregatableResult; + } /** * Returns a storage key given a frame id. @@ -13970,6 +14226,15 @@ Leaves other stored data, including the issuer's Redemption Records, intact. } export type setInterestGroupTrackingReturnValue = { } + /** + * Enables/Disables issuing of interestGroupAuctionEventOccurred and +interestGroupAuctionNetworkRequestCreated. + */ + export type setInterestGroupAuctionTrackingParameters = { + enable: boolean; + } + export type setInterestGroupAuctionTrackingReturnValue = { + } /** * Gets metadata for an origin's shared storage. */ @@ -14318,6 +14583,9 @@ supported. export interface TargetInfo { targetId: TargetID; type: string; + /** + * List of types: https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/devtools_agent_host_impl.cc?ss=chromium&q=f:devtools%20-f:out%20%22::kTypeTab%5B%5D%22 + */ title: string; url: string; /** @@ -14348,7 +14616,7 @@ the type of "page", this may be set to "portal" or "prerender". */ export interface FilterEntry { /** - * If set, causes exclusion of mathcing targets from the list. + * If set, causes exclusion of matching targets from the list. */ exclude?: boolean; /** @@ -14499,7 +14767,7 @@ channel with browser target. Injected object will be available as `window[bindingName]`. -The object has the follwing API: +The object has the following API: - `binding.send(json)` - a method to send messages over the remote debugging protocol - `binding.onmessage = json => handleMessage(json)` - a callback that will be called for the protocol notifications and command responses. */ @@ -15712,6 +15980,18 @@ See https://w3c.github.io/webauthn/#signature-counter See https://w3c.github.io/webauthn/#sctn-large-blob-extension */ largeBlob?: binary; + /** + * Assertions returned by this credential will have the backup eligibility +(BE) flag set to this value. Defaults to the authenticator's +defaultBackupEligibility value. + */ + backupEligibility?: boolean; + /** + * Assertions returned by this credential will have the backup state (BS) +flag set to this value. Defaults to the authenticator's +defaultBackupState value. + */ + backupState?: boolean; } /** @@ -15858,6 +16138,18 @@ The default is true. } export type setAutomaticPresenceSimulationReturnValue = { } + /** + * Allows setting credential properties. +https://w3c.github.io/webauthn/#sctn-automation-set-credential-properties + */ + export type setCredentialPropertiesParameters = { + authenticatorId: AuthenticatorId; + credentialId: binary; + backupEligibility?: boolean; + backupState?: boolean; + } + export type setCredentialPropertiesReturnValue = { + } } /** @@ -16093,7 +16385,7 @@ See also: requestId?: Network.RequestId; /** * Error information -`errorMessage` is null iff `errorType` is null. +`errorMessage` is null if `errorType` is null. */ errorType?: RuleSetErrorType; /** @@ -16132,7 +16424,7 @@ still keyed with the initial URL. that had a speculation rule that triggered the attempt, and the BackendNodeIds of <a href> or <area href> elements that triggered the attempt (in the case of attempts triggered by a document rule). It is -possible for mulitple rule sets and links to trigger a single attempt. +possible for multiple rule sets and links to trigger a single attempt. */ export interface PreloadingAttemptSource { key: PreloadingAttemptKey; @@ -16152,7 +16444,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"|"PrefetchEvicted"|"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"|"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"; /** * Information of headers to be displayed when the header mismatch occurred. */ @@ -16244,6 +16536,10 @@ whether this account has ever been used to sign in to this RP before. * The buttons on the FedCM dialog. */ export type DialogButton = "ConfirmIdpLoginContinue"|"ErrorGotIt"|"ErrorMoreDetails"; + /** + * The URLs that each account has + */ + export type AccountUrlType = "TermsOfService"|"PrivacyPolicy"; /** * Corresponds to IdentityRequestAccount */ @@ -16308,6 +16604,13 @@ normally happen, if this is unimportant to what's being tested. } export type clickDialogButtonReturnValue = { } + export type openUrlParameters = { + dialogId: string; + accountIndex: number; + accountUrlType: AccountUrlType; + } + export type openUrlReturnValue = { + } export type dismissDialogParameters = { dialogId: string; triggerCooldown?: boolean; @@ -19076,10 +19379,13 @@ Error was thrown. "Storage.indexedDBContentUpdated": Storage.indexedDBContentUpdatedPayload; "Storage.indexedDBListUpdated": Storage.indexedDBListUpdatedPayload; "Storage.interestGroupAccessed": Storage.interestGroupAccessedPayload; + "Storage.interestGroupAuctionEventOccurred": Storage.interestGroupAuctionEventOccurredPayload; + "Storage.interestGroupAuctionNetworkRequestCreated": Storage.interestGroupAuctionNetworkRequestCreatedPayload; "Storage.sharedStorageAccessed": Storage.sharedStorageAccessedPayload; "Storage.storageBucketCreatedOrUpdated": Storage.storageBucketCreatedOrUpdatedPayload; "Storage.storageBucketDeleted": Storage.storageBucketDeletedPayload; "Storage.attributionReportingSourceRegistered": Storage.attributionReportingSourceRegisteredPayload; + "Storage.attributionReportingTriggerRegistered": Storage.attributionReportingTriggerRegisteredPayload; "Target.attachedToTarget": Target.attachedToTargetPayload; "Target.detachedFromTarget": Target.detachedFromTargetPayload; "Target.receivedMessageFromTarget": Target.receivedMessageFromTargetPayload; @@ -19430,6 +19736,7 @@ Error was thrown. "Network.setAttachDebugStack": Network.setAttachDebugStackParameters; "Network.setRequestInterception": Network.setRequestInterceptionParameters; "Network.setUserAgentOverride": Network.setUserAgentOverrideParameters; + "Network.streamResourceContent": Network.streamResourceContentParameters; "Network.getSecurityIsolationStatus": Network.getSecurityIsolationStatusParameters; "Network.enableReportingApi": Network.enableReportingApiParameters; "Network.loadNetworkResource": Network.loadNetworkResourceParameters; @@ -19565,6 +19872,7 @@ Error was thrown. "Storage.clearTrustTokens": Storage.clearTrustTokensParameters; "Storage.getInterestGroupDetails": Storage.getInterestGroupDetailsParameters; "Storage.setInterestGroupTracking": Storage.setInterestGroupTrackingParameters; + "Storage.setInterestGroupAuctionTracking": Storage.setInterestGroupAuctionTrackingParameters; "Storage.getSharedStorageMetadata": Storage.getSharedStorageMetadataParameters; "Storage.getSharedStorageEntries": Storage.getSharedStorageEntriesParameters; "Storage.setSharedStorageEntry": Storage.setSharedStorageEntryParameters; @@ -19628,6 +19936,7 @@ Error was thrown. "WebAuthn.clearCredentials": WebAuthn.clearCredentialsParameters; "WebAuthn.setUserVerified": WebAuthn.setUserVerifiedParameters; "WebAuthn.setAutomaticPresenceSimulation": WebAuthn.setAutomaticPresenceSimulationParameters; + "WebAuthn.setCredentialProperties": WebAuthn.setCredentialPropertiesParameters; "Media.enable": Media.enableParameters; "Media.disable": Media.disableParameters; "DeviceAccess.enable": DeviceAccess.enableParameters; @@ -19640,6 +19949,7 @@ Error was thrown. "FedCm.disable": FedCm.disableParameters; "FedCm.selectAccount": FedCm.selectAccountParameters; "FedCm.clickDialogButton": FedCm.clickDialogButtonParameters; + "FedCm.openUrl": FedCm.openUrlParameters; "FedCm.dismissDialog": FedCm.dismissDialogParameters; "FedCm.resetCooldown": FedCm.resetCooldownParameters; "Console.clearMessages": Console.clearMessagesParameters; @@ -20008,6 +20318,7 @@ Error was thrown. "Network.setAttachDebugStack": Network.setAttachDebugStackReturnValue; "Network.setRequestInterception": Network.setRequestInterceptionReturnValue; "Network.setUserAgentOverride": Network.setUserAgentOverrideReturnValue; + "Network.streamResourceContent": Network.streamResourceContentReturnValue; "Network.getSecurityIsolationStatus": Network.getSecurityIsolationStatusReturnValue; "Network.enableReportingApi": Network.enableReportingApiReturnValue; "Network.loadNetworkResource": Network.loadNetworkResourceReturnValue; @@ -20143,6 +20454,7 @@ Error was thrown. "Storage.clearTrustTokens": Storage.clearTrustTokensReturnValue; "Storage.getInterestGroupDetails": Storage.getInterestGroupDetailsReturnValue; "Storage.setInterestGroupTracking": Storage.setInterestGroupTrackingReturnValue; + "Storage.setInterestGroupAuctionTracking": Storage.setInterestGroupAuctionTrackingReturnValue; "Storage.getSharedStorageMetadata": Storage.getSharedStorageMetadataReturnValue; "Storage.getSharedStorageEntries": Storage.getSharedStorageEntriesReturnValue; "Storage.setSharedStorageEntry": Storage.setSharedStorageEntryReturnValue; @@ -20206,6 +20518,7 @@ Error was thrown. "WebAuthn.clearCredentials": WebAuthn.clearCredentialsReturnValue; "WebAuthn.setUserVerified": WebAuthn.setUserVerifiedReturnValue; "WebAuthn.setAutomaticPresenceSimulation": WebAuthn.setAutomaticPresenceSimulationReturnValue; + "WebAuthn.setCredentialProperties": WebAuthn.setCredentialPropertiesReturnValue; "Media.enable": Media.enableReturnValue; "Media.disable": Media.disableReturnValue; "DeviceAccess.enable": DeviceAccess.enableReturnValue; @@ -20218,6 +20531,7 @@ Error was thrown. "FedCm.disable": FedCm.disableReturnValue; "FedCm.selectAccount": FedCm.selectAccountReturnValue; "FedCm.clickDialogButton": FedCm.clickDialogButtonReturnValue; + "FedCm.openUrl": FedCm.openUrlReturnValue; "FedCm.dismissDialog": FedCm.dismissDialogReturnValue; "FedCm.resetCooldown": FedCm.resetCooldownReturnValue; "Console.clearMessages": Console.clearMessagesReturnValue; diff --git a/playwright/packages/playwright-core/types/types.d.ts b/playwright/packages/playwright-core/types/types.d.ts index 4ce24d9297..95c0ddcdd0 100644 --- a/playwright/packages/playwright-core/types/types.d.ts +++ b/playwright/packages/playwright-core/types/types.d.ts @@ -901,8 +901,7 @@ export interface Page { on(event: 'close', listener: (page: Page) => void): this; /** - * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - * emitted if the page throws an error or a warning. + * 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. * @@ -1197,8 +1196,7 @@ export interface Page { addListener(event: 'close', listener: (page: Page) => void): this; /** - * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - * emitted if the page throws an error or a warning. + * 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. * @@ -1588,8 +1586,7 @@ export interface Page { prependListener(event: 'close', listener: (page: Page) => void): this; /** - * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - * emitted if the page throws an error or a warning. + * 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. * @@ -1783,6 +1780,86 @@ export interface Page { */ prependListener(event: 'worker', listener: (worker: Worker) => void): this; + /** + * Sometimes, the web page can show an overlay that obstructs elements behind it and prevents certain actions, like + * click, from completing. When such an overlay is shown predictably, we recommend dismissing it as a part of your + * test flow. However, sometimes such an overlay may appear non-deterministically, for example certain cookies consent + * dialogs behave this way. In this case, + * [page.addLocatorHandler(locator, handler)](https://playwright.dev/docs/api/class-page#page-add-locator-handler) + * allows handling an overlay during an action that it would block. + * + * This method registers a handler for an overlay that is executed once the locator is visible on the page. The + * handler should get rid of the overlay so that actions blocked by it can proceed. This is useful for + * nondeterministic interstitial pages or dialogs, like a cookie consent dialog. + * + * Note that execution time of the handler counts towards the timeout of the action/assertion that executed the + * handler. + * + * You can register multiple handlers. However, only a single handler will be running at a time. Any actions inside a + * handler must not require another handler to run. + * + * **NOTE** Running the interceptor will alter your page state mid-test. For example it will change the currently + * focused element and move the mouse. Make sure that the actions that run after the interceptor are self-contained + * and do not rely on the focus and mouse state. <br /> <br /> 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 + * [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 methods like + * [locator.click([options])](https://playwright.dev/docs/api/class-locator#locator-click) that are self-contained. + * + * **Usage** + * + * An example that closes a cookie dialog when it appears: + * + * ```js + * // Setup the handler. + * await page.addLocatorHandler(page.getByRole('button', { name: 'Accept all cookies' }), async () => { + * await page.getByRole('button', { name: 'Reject all cookies' }).click(); + * }); + * + * // Write the test as usual. + * await page.goto('https://example.com'); + * await page.getByRole('button', { name: 'Start here' }).click(); + * ``` + * + * An example that skips the "Confirm your security details" page when it is shown: + * + * ```js + * // Setup the handler. + * await page.addLocatorHandler(page.getByText('Confirm your security details'), async () => { + * await page.getByRole('button', 'Remind me later').click(); + * }); + * + * // Write the test as usual. + * await page.goto('https://example.com'); + * await page.getByRole('button', { name: 'Start here' }).click(); + * ``` + * + * 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: + * + * ```js + * // Setup the handler. + * await page.addLocatorHandler(page.locator('body'), async () => { + * await page.evaluate(() => window.removeObstructionsForTestIfNeeded()); + * }); + * + * // Write the test as usual. + * await page.goto('https://example.com'); + * await page.getByRole('button', { name: 'Start here' }).click(); + * ``` + * + * @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. + */ + addLocatorHandler(locator: Locator, handler: Function): Promise<void>; + /** * Adds a `<script>` tag into the page with the desired url or content. Returns the added tag when the script's onload * fires or when the script content was injected into frame. @@ -3415,6 +3492,11 @@ export interface Page { left?: string|number; }; + /** + * Whether or not to embed the document outline into the PDF. Defaults to `false`. + */ + outline?: boolean; + /** * Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages. */ @@ -3442,6 +3524,11 @@ export interface Page { */ scale?: number; + /** + * Whether or not to generate tagged (accessible) PDF. Defaults to `false`. + */ + tagged?: boolean; + /** * Paper width, accepts values labeled with units. */ @@ -3472,8 +3559,8 @@ export interface Page { * If `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"` 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. + * 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. * * **Usage** * @@ -3588,11 +3675,11 @@ export interface Page { * some post data, and leaving all other requests as is: * * ```js - * await page.route('/api/**', route => { + * await page.route('/api/**', async route => { * if (route.request().postData().includes('my-string')) - * route.fulfill({ body: 'mocked-data' }); + * await route.fulfill({ body: 'mocked-data' }); * else - * route.continue(); + * await route.continue(); * }); * ``` * @@ -4280,8 +4367,7 @@ export interface Page { waitForEvent(event: 'close', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise<boolean>, timeout?: number } | ((page: Page) => boolean | Promise<boolean>)): Promise<Page>; /** - * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - * emitted if the page throws an error or a warning. + * 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. * @@ -6759,8 +6845,8 @@ export interface Frame { * If `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"` 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. + * 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. * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be * used. * @param key Name of the key to press or a character to generate, such as `ArrowLeft` or `a`. @@ -7603,8 +7689,7 @@ export interface BrowserContext { on(event: 'close', listener: (browserContext: BrowserContext) => void): this; /** - * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - * emitted if the page throws an error or a warning. + * 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. @@ -7795,8 +7880,7 @@ export interface BrowserContext { addListener(event: 'close', listener: (browserContext: BrowserContext) => void): this; /** - * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - * emitted if the page throws an error or a warning. + * 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. @@ -8042,8 +8126,7 @@ export interface BrowserContext { prependListener(event: 'close', listener: (browserContext: BrowserContext) => void): this; /** - * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - * emitted if the page throws an error or a warning. + * 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. @@ -8391,11 +8474,11 @@ export interface BrowserContext { * some post data, and leaving all other requests as is: * * ```js - * await context.route('/api/**', route => { + * await context.route('/api/**', async route => { * if (route.request().postData().includes('my-string')) - * route.fulfill({ body: 'mocked-data' }); + * await route.fulfill({ body: 'mocked-data' }); * else - * route.continue(); + * await route.continue(); * }); * ``` * @@ -8660,8 +8743,7 @@ export interface BrowserContext { waitForEvent(event: 'close', optionsOrPredicate?: { predicate?: (browserContext: BrowserContext) => boolean | Promise<boolean>, timeout?: number } | ((browserContext: BrowserContext) => boolean | Promise<boolean>)): Promise<BrowserContext>; /** - * Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - * emitted if the page throws an error or a warning. + * 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. @@ -10039,8 +10121,8 @@ export interface ElementHandle<T=Node> extends JSHandle<T> { * If `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"` 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. + * 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. * @param key Name of the key to press or a character to generate, such as `ArrowLeft` or `a`. * @param options */ @@ -12231,8 +12313,8 @@ export interface Locator { * If `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"` 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. + * 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. * @param key Name of the key to press or a character to generate, such as `ArrowLeft` or `a`. * @param options */ @@ -13867,10 +13949,31 @@ export interface ElectronApplication { */ evaluateHandle<R>(pageFunction: PageFunctionOn<ElectronType, void, R>, arg?: any): Promise<SmartHandle<R>>; /** - * This event is issued when the application closes. + * This event is issued when the application process has been terminated. */ on(event: 'close', listener: () => void): this; + /** + * 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. + * + * **Usage** + * + * ```js + * electronApp.on('console', async msg => { + * const values = []; + * for (const arg of msg.args()) + * values.push(await arg.jsonValue()); + * console.log(...values); + * }); + * await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' })); + * ``` + * + */ + on(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): 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. @@ -13882,16 +13985,42 @@ export interface ElectronApplication { */ once(event: 'close', listener: () => void): this; + /** + * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. + */ + once(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ once(event: 'window', listener: (page: Page) => void): this; /** - * This event is issued when the application closes. + * This event is issued when the application process has been terminated. */ addListener(event: 'close', listener: () => void): this; + /** + * 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. + * + * **Usage** + * + * ```js + * electronApp.on('console', async msg => { + * const values = []; + * for (const arg of msg.args()) + * values.push(await arg.jsonValue()); + * console.log(...values); + * }); + * await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' })); + * ``` + * + */ + addListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): 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. @@ -13903,6 +14032,11 @@ export interface ElectronApplication { */ removeListener(event: 'close', listener: () => void): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + /** * Removes an event listener added by `on` or `addListener`. */ @@ -13913,16 +14047,42 @@ export interface ElectronApplication { */ off(event: 'close', listener: () => void): this; + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): this; + /** * Removes an event listener added by `on` or `addListener`. */ off(event: 'window', listener: (page: Page) => void): this; /** - * This event is issued when the application closes. + * This event is issued when the application process has been terminated. */ prependListener(event: 'close', listener: () => void): this; + /** + * 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. + * + * **Usage** + * + * ```js + * electronApp.on('console', async msg => { + * const values = []; + * for (const arg of msg.args()) + * values.push(await arg.jsonValue()); + * console.log(...values); + * }); + * await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' })); + * ``` + * + */ + prependListener(event: 'console', listener: (consoleMessage: ConsoleMessage) => void): 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. @@ -13975,10 +14135,31 @@ export interface ElectronApplication { process(): ChildProcess; /** - * This event is issued when the application closes. + * This event is issued when the application process has been terminated. */ waitForEvent(event: 'close', optionsOrPredicate?: { predicate?: () => boolean | Promise<boolean>, timeout?: number } | (() => boolean | Promise<boolean>)): Promise<void>; + /** + * 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. + * + * **Usage** + * + * ```js + * electronApp.on('console', async msg => { + * const values = []; + * for (const arg of msg.args()) + * values.push(await arg.jsonValue()); + * console.log(...values); + * }); + * await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' })); + * ``` + * + */ + 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. @@ -14165,13 +14346,6 @@ export {}; * })(); * ``` * - * Note that since you don't need Playwright to install web browsers when testing Android, you can omit browser - * download via setting the following environment variable when installing Playwright: - * - * ```bash - * PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright - * ``` - * */ export interface Android { /** @@ -17248,13 +17422,6 @@ export interface Download { * })(); * ``` * - * Note that since you don't need Playwright to install web browsers when testing Electron, you can omit browser - * download via setting the following environment variable when installing Playwright: - * - * ```bash - * PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright - * ``` - * * **Supported Electron versions are:** * - v12.2.0+ * - v13.4.0+ @@ -18070,8 +18237,8 @@ export interface Keyboard { * If `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"` 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. + * 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. * * **Usage** * diff --git a/playwright/packages/playwright-ct-core/.eslintrc.js b/playwright/packages/playwright-ct-core/.eslintrc.js index ae8768db65..84888f1ae3 100644 --- a/playwright/packages/playwright-ct-core/.eslintrc.js +++ b/playwright/packages/playwright-ct-core/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - extends: "../.eslintrc-with-ts-config.js", + extends: "../../.eslintrc-with-ts-config.js", }; diff --git a/playwright/packages/playwright-ct-core/.npmignore b/playwright/packages/playwright-ct-core/.npmignore index 478393ccba..fd3a0b88d5 100644 --- a/playwright/packages/playwright-ct-core/.npmignore +++ b/playwright/packages/playwright-ct-core/.npmignore @@ -7,3 +7,4 @@ !types/** !index.d.ts !index.js + diff --git a/playwright/packages/playwright-ct-core/cli.js b/playwright/packages/playwright-ct-core/cli.js index 2d0ff42281..5db59b62cd 100755 --- a/playwright/packages/playwright-ct-core/cli.js +++ b/playwright/packages/playwright-ct-core/cli.js @@ -14,4 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('playwright/lib/cli'); + +const { program } = require('./lib/program'); +program.parse(process.argv); diff --git a/playwright/packages/playwright-ct-core/index.js b/playwright/packages/playwright-ct-core/index.js index 1b8fd60a2d..9583273ac2 100644 --- a/playwright/packages/playwright-ct-core/index.js +++ b/playwright/packages/playwright-ct-core/index.js @@ -16,16 +16,26 @@ const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('playwright/test'); const { fixtures } = require('./lib/mount'); +const { clearCacheCommand, findRelatedTestFilesCommand } = require('./lib/cliOverrides'); +const { createPlugin } = require('./lib/vitePlugin'); -const defineConfig = (config, ...configs) => originalDefineConfig({ - ...config, - build: { - ...config.build, - babelPlugins: [ - [require.resolve('./lib/tsxTransform')] - ], - } -}, ...configs); +const defineConfig = (...configs) => { + const original = originalDefineConfig(...configs); + return { + ...original, + '@playwright/test': { + ...original['@playwright/test'], + plugins: [() => createPlugin()], + babelPlugins: [ + [require.resolve('./lib/tsxTransform')] + ], + cli: { + 'clear-cache': clearCacheCommand, + 'find-related-test-files': findRelatedTestFilesCommand, + }, + } + }; +}; const test = baseTest.extend(fixtures); diff --git a/playwright/packages/playwright-ct-core/package.json b/playwright/packages/playwright-ct-core/package.json index 2a123548f5..c8c4af6dad 100644 --- a/playwright/packages/playwright-ct-core/package.json +++ b/playwright/packages/playwright-ct-core/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-core", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright Component Testing Helpers", "repository": { "type": "git", @@ -19,17 +19,16 @@ "types": "./index.d.ts", "default": "./index.js" }, - "./cli": "./cli.js", "./lib/mount": "./lib/mount.js", - "./lib/vitePlugin": "./lib/vitePlugin.js", + "./lib/program": "./lib/program.js", "./types/component": { "types": "./types/component.d.ts" } }, "dependencies": { - "playwright-core": "1.41.1", - "vite": "^4.4.12", - "playwright": "1.41.1" + "playwright-core": "1.42.0", + "vite": "^5.0.12", + "playwright": "1.42.0" }, "bin": { "playwright": "cli.js" diff --git a/playwright/packages/playwright-ct-core/src/DEPS.list b/playwright/packages/playwright-ct-core/src/DEPS.list index c45c7c3a7b..76c2d71bd6 100644 --- a/playwright/packages/playwright-ct-core/src/DEPS.list +++ b/playwright/packages/playwright-ct-core/src/DEPS.list @@ -1,6 +1,12 @@ [vitePlugin.ts] generated/indexSource.ts +[viteDevPlugin.ts] +generated/indexSource.ts + +[devServer.ts] +generated/indexSource.ts + [mount.ts] generated/serializers.ts injected/** diff --git a/playwright/packages/playwright-ct-core/src/cliOverrides.ts b/playwright/packages/playwright-ct-core/src/cliOverrides.ts new file mode 100644 index 0000000000..3ebcd09cbc --- /dev/null +++ b/playwright/packages/playwright-ct-core/src/cliOverrides.ts @@ -0,0 +1,35 @@ + +/** + * 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 { removeFolder } from 'playwright/lib/program'; +import { affectedTestFiles, cacheDir } from 'playwright/lib/transform/compilationCache'; +import { buildBundle } from './vitePlugin'; +import { resolveDirs } from './viteUtils'; +import type { Suite } from 'playwright/lib/common/test'; +import type { FullConfig } from 'playwright/test'; + +export async function clearCacheCommand(config: FullConfig, configDir: string) { + const dirs = await resolveDirs(configDir, config); + if (dirs) + await removeFolder(dirs.outDir); + await removeFolder(cacheDir); +} + +export async function findRelatedTestFilesCommand(files: string[], config: FullConfig, configDir: string, suite: Suite) { + await buildBundle(config, configDir, suite); + return { testFiles: affectedTestFiles(files) }; +} diff --git a/playwright/packages/playwright-ct-core/src/devServer.ts b/playwright/packages/playwright-ct-core/src/devServer.ts new file mode 100644 index 0000000000..46fc604188 --- /dev/null +++ b/playwright/packages/playwright-ct-core/src/devServer.ts @@ -0,0 +1,88 @@ +/** + * 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 fs from 'fs'; +import path from 'path'; +import { Watcher } from 'playwright/lib/fsWatcher'; +import { loadConfigFromFileRestartIfNeeded } from 'playwright/lib/common/configLoader'; +import { Runner } from 'playwright/lib/runner/runner'; +import type { PluginContext } from 'rollup'; +import { source as injectedSource } from './generated/indexSource'; +import { createConfig, populateComponentsFromTests, resolveDirs, transformIndexFile, frameworkConfig } from './viteUtils'; +import type { ComponentRegistry } from './viteUtils'; + +export async function runDevServer(configFile: string) { + const config = await loadConfigFromFileRestartIfNeeded(configFile); + if (!config) + return; + + const { registerSourceFile, frameworkPluginFactory } = frameworkConfig(config.config); + const runner = new Runner(config); + await runner.loadAllTests(); + const componentRegistry: ComponentRegistry = new Map(); + await populateComponentsFromTests(componentRegistry); + + const dirs = await resolveDirs(config.configDir, config.config); + if (!dirs) { + // eslint-disable-next-line no-console + console.log(`Template file playwright/index.html is missing.`); + return; + } + const registerSource = injectedSource + '\n' + await fs.promises.readFile(registerSourceFile, 'utf-8'); + const viteConfig = await createConfig(dirs, config.config, frameworkPluginFactory, false); + viteConfig.plugins.push({ + name: 'playwright:component-index', + + async transform(this: PluginContext, content: string, id: string) { + return transformIndexFile(id, content, dirs.templateDir, registerSource, componentRegistry); + }, + }); + + const { createServer } = await import('vite'); + const devServer = await createServer(viteConfig); + await devServer.listen(); + const protocol = viteConfig.server.https ? 'https:' : 'http:'; + // eslint-disable-next-line no-console + console.log(`Test Server listening on ${protocol}//${viteConfig.server.host || 'localhost'}:${viteConfig.server.port}`); + + const projectDirs = new Set<string>(); + const projectOutputs = new Set<string>(); + for (const p of config.projects) { + projectDirs.add(p.project.testDir); + projectOutputs.add(p.project.outputDir); + } + + const globalWatcher = new Watcher('deep', async () => { + const registry: ComponentRegistry = new Map(); + await populateComponentsFromTests(registry); + // compare componentRegistry to registry key sets. + if (componentRegistry.size === registry.size && [...componentRegistry.keys()].every(k => registry.has(k))) + return; + + // eslint-disable-next-line no-console + console.log('List of components changed'); + componentRegistry.clear(); + for (const [k, v] of registry) + componentRegistry.set(k, v); + + const id = path.join(dirs.templateDir, 'index'); + const modules = [...devServer.moduleGraph.urlToModuleMap.values()]; + const rootModule = modules.find(m => m.file?.startsWith(id + '.ts') || m.file?.startsWith(id + '.js')); + if (rootModule) + devServer.moduleGraph.onFileChange(rootModule.file!); + }); + globalWatcher.update([...projectDirs], [...projectOutputs], false); +} diff --git a/playwright/packages/playwright-ct-core/src/injected/importRegistry.ts b/playwright/packages/playwright-ct-core/src/injected/importRegistry.ts index 138d31ce05..1f5c1adf4c 100644 --- a/playwright/packages/playwright-ct-core/src/injected/importRegistry.ts +++ b/playwright/packages/playwright-ct-core/src/injected/importRegistry.ts @@ -47,3 +47,9 @@ export class ImportRegistry { return importedObject; } } + +declare global { + interface Window { + __pwRegistry: ImportRegistry; + } +} diff --git a/playwright/packages/playwright-ct-core/src/mount.ts b/playwright/packages/playwright-ct-core/src/mount.ts index e8ee9272ac..7220916a4b 100644 --- a/playwright/packages/playwright-ct-core/src/mount.ts +++ b/playwright/packages/playwright-ct-core/src/mount.ts @@ -33,12 +33,12 @@ type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & { type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _ctWorker: { context: BrowserContext | undefined, hash: string } }; type BaseTestFixtures = { _contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>, - _contextReuseMode: ContextReuseMode + _optionContextReuseMode: ContextReuseMode }; export const fixtures: Fixtures<TestFixtures, WorkerFixtures, BaseTestFixtures> = { - _contextReuseMode: 'when-possible', + _optionContextReuseMode: 'when-possible', serviceWorkers: 'block', diff --git a/playwright/packages/playwright-ct-core/src/program.ts b/playwright/packages/playwright-ct-core/src/program.ts new file mode 100644 index 0000000000..68069ef68f --- /dev/null +++ b/playwright/packages/playwright-ct-core/src/program.ts @@ -0,0 +1,32 @@ +/** + * 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 { Command } from 'playwright-core/lib/utilsBundle'; + +import { program } from 'playwright/lib/program'; +import { runDevServer } from './devServer'; +export { program } from 'playwright/lib/program'; + +function addDevServerCommand(program: Command) { + const command = program.command('dev-server'); + command.description('start dev server'); + command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`); + command.action(options => { + runDevServer(options.config); + }); +} + +addDevServerCommand(program); diff --git a/playwright/packages/playwright-ct-core/src/tsxTransform.ts b/playwright/packages/playwright-ct-core/src/tsxTransform.ts index 6ef051a6b4..0a61d4e3d9 100644 --- a/playwright/packages/playwright-ct-core/src/tsxTransform.ts +++ b/playwright/packages/playwright-ct-core/src/tsxTransform.ts @@ -17,7 +17,6 @@ import path from 'path'; import type { T, BabelAPI, PluginObj } from 'playwright/src/transform/babelBundle'; import { types, declare, traverse } from 'playwright/lib/transform/babelBundle'; -import { resolveImportSpecifierExtension } from 'playwright/lib/util'; import { setTransformData } from 'playwright/lib/transform/transform'; const t: typeof T = types; @@ -144,25 +143,19 @@ function collectJsxComponentUsages(node: T.Node): Set<string> { export type ImportInfo = { id: string; - isModuleOrAlias: boolean; - importPath: string; + filename: string; + importSource: string; remoteName: string | undefined; }; export function importInfo(importNode: T.ImportDeclaration, specifier: T.ImportSpecifier | T.ImportDefaultSpecifier, filename: string): { localName: string, info: ImportInfo } { const importSource = importNode.source.value; - const isModuleOrAlias = !importSource.startsWith('.'); - const unresolvedImportPath = path.resolve(path.dirname(filename), importSource); - // Support following notations for Button.tsx: - // - import { Button } from './Button.js' - via resolveImportSpecifierExtension - // - import { Button } from './Button' - via require.resolve - const importPath = isModuleOrAlias ? importSource : resolveImportSpecifierExtension(unresolvedImportPath) || require.resolve(unresolvedImportPath); - const idPrefix = importPath.replace(/[^\w_\d]/g, '_'); + const idPrefix = importSource.replace(/[^\w_\d]/g, '_'); const result: ImportInfo = { id: idPrefix, - importPath, - isModuleOrAlias, + filename, + importSource, remoteName: undefined, }; diff --git a/playwright/packages/playwright-ct-core/src/vitePlugin.ts b/playwright/packages/playwright-ct-core/src/vitePlugin.ts index 333a7d8b07..cc26fdc807 100644 --- a/playwright/packages/playwright-ct-core/src/vitePlugin.ts +++ b/playwright/packages/playwright-ct-core/src/vitePlugin.ts @@ -14,43 +14,31 @@ * limitations under the License. */ -import type { Suite } from 'playwright/types/testReporter'; -import type { PlaywrightTestConfig as BasePlaywrightTestConfig, FullConfig } from 'playwright/test'; - -import type { InlineConfig, Plugin, ResolveFn, ResolvedConfig, UserConfig } from 'vite'; -import type { TestRunnerPlugin } from '../../playwright/src/plugins'; -import type { AddressInfo } from 'net'; -import type { PluginContext } from 'rollup'; -import { debug } from 'playwright-core/lib/utilsBundle'; - import fs from 'fs'; +import type http from 'http'; +import type { AddressInfo } from 'net'; import path from 'path'; +import { assert, calculateSha1, getPlaywrightVersion, isURLAvailable } from 'playwright-core/lib/utils'; +import { debug } from 'playwright-core/lib/utilsBundle'; +import { internalDependenciesForTestFile, setExternalDependencies } from 'playwright/lib/transform/compilationCache'; import { stoppable } from 'playwright/lib/utilsBundle'; -import { assert, calculateSha1 } from 'playwright-core/lib/utils'; -import { getPlaywrightVersion } from 'playwright-core/lib/utils'; -import { getUserData, internalDependenciesForTestFile, setExternalDependencies } from 'playwright/lib/transform/compilationCache'; -import { version as viteVersion, build, preview, mergeConfig } from 'vite'; +import type { FullConfig } from 'playwright/test'; +import type { Suite } from 'playwright/types/testReporter'; +import type { PluginContext } from 'rollup'; +import type { Plugin, ResolveFn, ResolvedConfig } from 'vite'; +import type { TestRunnerPlugin } from '../../playwright/src/plugins'; import { source as injectedSource } from './generated/indexSource'; import type { ImportInfo } from './tsxTransform'; +import type { ComponentRegistry } from './viteUtils'; +import { createConfig, frameworkConfig, hasJSComponents, populateComponentsFromTests, resolveDirs, resolveEndpoint, transformIndexFile } from './viteUtils'; +import { resolveHook } from 'playwright/lib/transform/transform'; const log = debug('pw:vite'); let stoppableServer: any; const playwrightVersion = getPlaywrightVersion(); -type CtConfig = BasePlaywrightTestConfig['use'] & { - ctPort?: number; - ctTemplateDir?: string; - ctCacheDir?: string; - ctViteConfig?: InlineConfig | (() => Promise<InlineConfig>); -}; - -const importReactRE = /(^|\n|;)import\s+(\*\s+as\s+)?React(,|\s+)/; -const compiledReactRE = /(const|var)\s+React\s*=/; - -export function createPlugin( - registerSourceFile: string, - frameworkPluginFactory?: () => Promise<Plugin>): TestRunnerPlugin { +export function createPlugin(): TestRunnerPlugin { let configDir: string; let config: FullConfig; return { @@ -62,167 +50,19 @@ export function createPlugin( }, begin: async (suite: Suite) => { - // We are going to have 3 config files: - // - the defaults that user config overrides (baseConfig) - // - the user config (userConfig) - // - frameworks overrides (frameworkOverrides); - - const use = config.projects[0].use as CtConfig; - const baseURL = new URL(use.baseURL || 'http://localhost'); - const relativeTemplateDir = use.ctTemplateDir || 'playwright'; - - // FIXME: use build plugin to determine html location to resolve this. - // TemplateDir must be relative, otherwise we can't move the final index.html into its target location post-build. - // This regressed in https://github.com/microsoft/playwright/pull/26526 - const templateDir = path.join(configDir, relativeTemplateDir); - - // Compose base config from the playwright config only. - const baseConfig: InlineConfig = { - root: configDir, - configFile: false, - define: { - __VUE_PROD_DEVTOOLS__: true, - }, - css: { - devSourcemap: true, - }, - build: { - outDir: use.ctCacheDir ? path.resolve(configDir, use.ctCacheDir) : path.resolve(templateDir, '.cache') - }, - preview: { - https: baseURL.protocol.startsWith('https:'), - host: baseURL.hostname, - port: use.ctPort || Number(baseURL.port) || 3100 - }, - // Vite preview server will otherwise always return the index.html with 200. - appType: 'custom', - }; - - // Apply user config on top of the base config. This could have changed root and build.outDir. - const userConfig = typeof use.ctViteConfig === 'function' ? await use.ctViteConfig() : (use.ctViteConfig || {}); - const baseAndUserConfig = mergeConfig(baseConfig, userConfig); - const buildInfoFile = path.join(baseAndUserConfig.build.outDir, 'metainfo.json'); - - let buildExists = false; - let buildInfo: BuildInfo; - - const registerSource = injectedSource + '\n' + await fs.promises.readFile(registerSourceFile, 'utf-8'); - const registerSourceHash = calculateSha1(registerSource); - - try { - buildInfo = JSON.parse(await fs.promises.readFile(buildInfoFile, 'utf-8')) as BuildInfo; - assert(buildInfo.version === playwrightVersion); - assert(buildInfo.viteVersion === viteVersion); - assert(buildInfo.registerSourceHash === registerSourceHash); - buildExists = true; - } catch (e) { - buildInfo = { - version: playwrightVersion, - viteVersion, - registerSourceHash, - components: [], - sources: {}, - deps: {}, - }; - } - log('build exists:', buildExists); - - const componentRegistry: ComponentRegistry = new Map(); - const componentsByImportingFile = new Map<string, string[]>(); - // 1. Populate component registry based on tests' component imports. - await populateComponentsFromTests(componentRegistry, componentsByImportingFile); - - // 2. Check if the set of required components has changed. - const hasNewComponents = await checkNewComponents(buildInfo, componentRegistry); - log('has new components:', hasNewComponents); - - // 3. Check component sources. - const sourcesDirty = !buildExists || hasNewComponents || await checkSources(buildInfo); - log('sourcesDirty:', sourcesDirty); - - // 4. Update component info. - buildInfo.components = [...componentRegistry.values()]; - - const frameworkOverrides: UserConfig = { plugins: [] }; - - // React heuristic. If we see a component in a file with .js extension, - // consider it a potential JSX-in-JS scenario and enable JSX loader for all - // .js files. - if (hasJSComponents(buildInfo.components)) { - log('jsx-in-js detected'); - frameworkOverrides.esbuild = { - loader: 'jsx', - include: /.*\.jsx?$/, - exclude: [], - }; - frameworkOverrides.optimizeDeps = { - esbuildOptions: { - loader: { '.js': 'jsx' }, - } - }; - } - - // We assume that any non-empty plugin list includes `vite-react` or similar. - if (frameworkPluginFactory && !baseAndUserConfig.plugins?.length) - frameworkOverrides.plugins = [await frameworkPluginFactory()]; - - // But only add out own plugin when we actually build / transform. - const depsCollector = new Map<string, string[]>(); - if (sourcesDirty) - frameworkOverrides.plugins!.push(vitePlugin(registerSource, templateDir, buildInfo, componentRegistry, depsCollector)); - - frameworkOverrides.build = { - target: 'esnext', - minify: false, - rollupOptions: { - treeshake: false, - input: { - index: path.join(templateDir, 'index.html') - }, - }, - sourcemap: true, - }; - - const finalConfig = mergeConfig(baseAndUserConfig, frameworkOverrides); - - if (sourcesDirty) { - log('build'); - await build(finalConfig); - await fs.promises.rename(`${finalConfig.build.outDir}/${relativeTemplateDir}/index.html`, `${finalConfig.build.outDir}/index.html`); - buildInfo.deps = Object.fromEntries(depsCollector.entries()); - } - - if (hasNewComponents || sourcesDirty) { - log('write manifest'); - await fs.promises.writeFile(buildInfoFile, JSON.stringify(buildInfo, undefined, 2)); - } - - for (const projectSuite of suite.suites) { - for (const fileSuite of projectSuite.suites) { - // For every test file... - const testFile = fileSuite.location!.file; - const deps = new Set<string>(); - // Collect its JS dependencies (helpers). - for (const file of [testFile, ...(internalDependenciesForTestFile(testFile) || [])]) { - // For each helper, get all the imported components. - for (const componentFile of componentsByImportingFile.get(file) || []) { - // For each component, get all the dependencies. - for (const d of depsCollector.get(componentFile) || []) - deps.add(d); - } - } - // Now we have test file => all components along with dependencies. - setExternalDependencies(testFile, [...deps]); - } - } + const result = await buildBundle(config, configDir, suite); + if (!result) + return; - const previewServer = await preview(finalConfig); - stoppableServer = stoppable(previewServer.httpServer, 0); + const { viteConfig } = result; + const { preview } = await import('vite'); + const previewServer = await preview(viteConfig); + stoppableServer = stoppable(previewServer.httpServer as http.Server, 0); const isAddressInfo = (x: any): x is AddressInfo => x?.address; const address = previewServer.httpServer.address(); if (isAddressInfo(address)) { - const protocol = finalConfig.preview.https ? 'https:' : 'http:'; - process.env.PLAYWRIGHT_TEST_BASE_URL = `${protocol}//${finalConfig.preview.host}:${address.port}`; + const protocol = viteConfig.preview.https ? 'https:' : 'http:'; + process.env.PLAYWRIGHT_TEST_BASE_URL = `${protocol}//${viteConfig.preview.host}:${address.port}`; } }, @@ -248,7 +88,114 @@ type BuildInfo = { } }; -type ComponentRegistry = Map<string, ImportInfo>; +export async function buildBundle(config: FullConfig, configDir: string, suite: Suite): Promise<{ buildInfo: BuildInfo, viteConfig: Record<string, any> } | null> { + const { registerSourceFile, frameworkPluginFactory } = frameworkConfig(config); + { + // Detect a running dev server and use it if available. + const endpoint = resolveEndpoint(config); + const protocol = endpoint.https ? 'https:' : 'http:'; + const url = new URL(`${protocol}//${endpoint.host}:${endpoint.port}`); + if (await isURLAvailable(url, true)) { + // eslint-disable-next-line no-console + console.log(`Test Server is already running at ${url.toString()}, using it.\n`); + process.env.PLAYWRIGHT_TEST_BASE_URL = url.toString(); + return null; + } + } + + const dirs = await resolveDirs(configDir, config); + if (!dirs) { + // eslint-disable-next-line no-console + console.log(`Template file playwright/index.html is missing.`); + return null; + } + + const buildInfoFile = path.join(dirs.outDir, 'metainfo.json'); + + let buildExists = false; + let buildInfo: BuildInfo; + + const registerSource = injectedSource + '\n' + await fs.promises.readFile(registerSourceFile, 'utf-8'); + const registerSourceHash = calculateSha1(registerSource); + + const { version: viteVersion, build, mergeConfig } = await import('vite'); + + try { + buildInfo = JSON.parse(await fs.promises.readFile(buildInfoFile, 'utf-8')) as BuildInfo; + assert(buildInfo.version === playwrightVersion); + assert(buildInfo.viteVersion === viteVersion); + assert(buildInfo.registerSourceHash === registerSourceHash); + buildExists = true; + } catch (e) { + buildInfo = { + version: playwrightVersion, + viteVersion, + registerSourceHash, + components: [], + sources: {}, + deps: {}, + }; + } + log('build exists:', buildExists); + + const componentRegistry: ComponentRegistry = new Map(); + const componentsByImportingFile = new Map<string, string[]>(); + // 1. Populate component registry based on tests' component imports. + await populateComponentsFromTests(componentRegistry, componentsByImportingFile); + + // 2. Check if the set of required components has changed. + const hasNewComponents = await checkNewComponents(buildInfo, componentRegistry); + log('has new components:', hasNewComponents); + + // 3. Check component sources. + const sourcesDirty = !buildExists || hasNewComponents || await checkSources(buildInfo); + log('sourcesDirty:', sourcesDirty); + + // 4. Update component info. + buildInfo.components = [...componentRegistry.values()]; + + const jsxInJS = hasJSComponents(buildInfo.components); + const viteConfig = await createConfig(dirs, config, frameworkPluginFactory, jsxInJS); + + if (sourcesDirty) { + // Only add out own plugin when we actually build / transform. + log('build'); + const depsCollector = new Map<string, string[]>(); + const buildConfig = mergeConfig(viteConfig, { + plugins: [vitePlugin(registerSource, dirs.templateDir, buildInfo, componentRegistry, depsCollector)] + }); + await build(buildConfig); + buildInfo.deps = Object.fromEntries(depsCollector.entries()); + } + + { + // Update dependencies based on the vite build. + for (const projectSuite of suite.suites) { + for (const fileSuite of projectSuite.suites) { + // For every test file... + const testFile = fileSuite.location!.file; + const deps = new Set<string>(); + // Collect its JS dependencies (helpers). + for (const file of [testFile, ...(internalDependenciesForTestFile(testFile) || [])]) { + // For each helper, get all the imported components. + for (const componentFile of componentsByImportingFile.get(file) || []) { + // For each component, get all the dependencies. + for (const d of buildInfo.deps[componentFile] || []) + deps.add(d); + } + } + // Now we have test file => all components along with dependencies. + setExternalDependencies(testFile, [...deps]); + } + } + } + + if (hasNewComponents || sourcesDirty) { + log('write manifest'); + await fs.promises.writeFile(buildInfoFile, JSON.stringify(buildInfo, undefined, 2)); + } + return { buildInfo, viteConfig }; +} async function checkSources(buildInfo: BuildInfo): Promise<boolean> { for (const [source, sourceInfo] of Object.entries(buildInfo.sources)) { @@ -266,15 +213,6 @@ async function checkSources(buildInfo: BuildInfo): Promise<boolean> { return false; } -async function populateComponentsFromTests(componentRegistry: ComponentRegistry, componentsByImportingFile: Map<string, string[]>) { - const importInfos: Map<string, ImportInfo[]> = await getUserData('playwright-ct-core'); - for (const [file, importList] of importInfos) { - for (const importInfo of importList) - componentRegistry.set(importInfo.id, importInfo); - componentsByImportingFile.set(file, importList.filter(i => !i.isModuleOrAlias).map(i => i.importPath)); - } -} - async function checkNewComponents(buildInfo: BuildInfo, componentRegistry: ComponentRegistry): Promise<boolean> { const newComponents = [...componentRegistry.keys()]; const oldComponents = new Map(buildInfo.components.map(c => [c.id, c])); @@ -313,45 +251,20 @@ function vitePlugin(registerSource: string, templateDir: string, buildInfo: Buil // Silent if can't read the file. } } - - // Vite React plugin will do this for .jsx files, but not .js files. - if (id.endsWith('.js') && content.includes('React.createElement') && !content.match(importReactRE) && !content.match(compiledReactRE)) { - const code = `import React from 'react';\n${content}`; - return { code, map: { mappings: '' } }; - } - - const indexTs = path.join(templateDir, 'index.ts'); - const indexTsx = path.join(templateDir, 'index.tsx'); - const indexJs = path.join(templateDir, 'index.js'); - const indexJsx = path.join(templateDir, 'index.jsx'); - const idResolved = path.resolve(id); - if (!idResolved.endsWith(indexTs) && !idResolved.endsWith(indexTsx) && !idResolved.endsWith(indexJs) && !idResolved.endsWith(indexJsx)) - return; - - const folder = path.dirname(id); - const lines = [content, '']; - lines.push(registerSource); - - for (const value of importInfos.values()) { - const importPath = value.isModuleOrAlias ? value.importPath : './' + path.relative(folder, value.importPath).replace(/\\/g, '/'); - lines.push(`const ${value.id} = () => import('${importPath}').then((mod) => mod.${value.remoteName || 'default'});`); - } - - lines.push(`__pwRegistry.initialize({ ${[...importInfos.keys()].join(',\n ')} });`); - return { - code: lines.join('\n'), - map: { mappings: '' } - }; + return transformIndexFile(id, content, templateDir, registerSource, importInfos); }, async writeBundle(this: PluginContext) { for (const importInfo of importInfos.values()) { + const importPath = resolveHook(importInfo.filename, importInfo.importSource); + if (!importPath) + continue; const deps = new Set<string>(); - const id = await moduleResolver(importInfo.importPath); + const id = await moduleResolver(importPath); if (!id) continue; collectViteModuleDependencies(this, id, deps); - depsCollector.set(importInfo.importPath, [...deps]); + depsCollector.set(importPath, [...deps]); } }, }; @@ -370,12 +283,3 @@ function collectViteModuleDependencies(context: PluginContext, id: string, deps: for (const importedId of module?.dynamicallyImportedIds || []) collectViteModuleDependencies(context, importedId, deps); } - -function hasJSComponents(components: ImportInfo[]): boolean { - for (const component of components) { - const extname = path.extname(component.importPath); - if (extname === '.js' || !extname && fs.existsSync(component.importPath + '.js')) - return true; - } - return false; -} diff --git a/playwright/packages/playwright-ct-core/src/viteUtils.ts b/playwright/packages/playwright-ct-core/src/viteUtils.ts new file mode 100644 index 0000000000..00da627432 --- /dev/null +++ b/playwright/packages/playwright-ct-core/src/viteUtils.ts @@ -0,0 +1,198 @@ +/** + * 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 fs from 'fs'; +import path from 'path'; +import { debug } from 'playwright-core/lib/utilsBundle'; +import { getUserData } from 'playwright/lib/transform/compilationCache'; +import type { PlaywrightTestConfig as BasePlaywrightTestConfig, FullConfig } from 'playwright/test'; +import type { InlineConfig, Plugin, TransformResult, UserConfig } from 'vite'; +import type { ImportInfo } from './tsxTransform'; +import { resolveHook } from 'playwright/lib/transform/transform'; + +const log = debug('pw:vite'); + +export type CtConfig = BasePlaywrightTestConfig['use'] & { + ctPort?: number; + ctTemplateDir?: string; + ctCacheDir?: string; + ctViteConfig?: InlineConfig | (() => Promise<InlineConfig>); +}; + +export type ComponentRegistry = Map<string, ImportInfo>; +export type ComponentDirs = { + configDir: string; + outDir: string; + templateDir: string; +}; + +export async function resolveDirs(configDir: string, config: FullConfig): Promise<ComponentDirs | null> { + const use = config.projects[0].use as CtConfig; + // FIXME: use build plugin to determine html location to resolve this. + // TemplateDir must be relative, otherwise we can't move the final index.html into its target location post-build. + // This regressed in https://github.com/microsoft/playwright/pull/26526 + const relativeTemplateDir = use.ctTemplateDir || 'playwright'; + const templateDir = await fs.promises.realpath(path.normalize(path.join(configDir, relativeTemplateDir))).catch(() => undefined); + if (!templateDir) + return null; + const outDir = use.ctCacheDir ? path.resolve(configDir, use.ctCacheDir) : path.resolve(templateDir, '.cache'); + return { + configDir, + outDir, + templateDir + }; +} + +export function resolveEndpoint(config: FullConfig) { + const use = config.projects[0].use as CtConfig; + const baseURL = new URL(use.baseURL || 'http://localhost'); + return { + https: baseURL.protocol.startsWith('https:') ? {} : undefined, + host: baseURL.hostname, + port: use.ctPort || Number(baseURL.port) || 3100 + }; +} + +export async function createConfig(dirs: ComponentDirs, config: FullConfig, frameworkPluginFactory: (() => Promise<Plugin>) | undefined, supportJsxInJs: boolean) { + // We are going to have 3 config files: + // - the defaults that user config overrides (baseConfig) + // - the user config (userConfig) + // - frameworks overrides (frameworkOverrides); + + const endpoint = resolveEndpoint(config); + const use = config.projects[0].use as CtConfig; + + // Compose base config from the playwright config only. + const baseConfig: InlineConfig = { + root: dirs.templateDir, + configFile: false, + publicDir: path.join(dirs.configDir, 'public'), + define: { + __VUE_PROD_DEVTOOLS__: true, + }, + css: { + devSourcemap: true, + }, + build: { + outDir: dirs.outDir + }, + preview: endpoint, + server: endpoint, + // Vite preview server will otherwise always return the index.html with 200. + appType: 'mpa', + }; + + // Vite 5 refuses to support CJS. + const { mergeConfig } = await import('vite'); + + // Apply user config on top of the base config. This could have changed root and build.outDir. + const userConfig = typeof use.ctViteConfig === 'function' ? await use.ctViteConfig() : (use.ctViteConfig || {}); + const baseAndUserConfig = mergeConfig(baseConfig, userConfig); + + const frameworkOverrides: UserConfig = { plugins: [] }; + + // React heuristic. If we see a component in a file with .js extension, + // consider it a potential JSX-in-JS scenario and enable JSX loader for all + // .js files. + if (supportJsxInJs) { + log('jsx-in-js detected'); + frameworkOverrides.esbuild = { + loader: 'jsx', + include: /.*\.jsx?$/, + exclude: [], + }; + frameworkOverrides.optimizeDeps = { + esbuildOptions: { + loader: { '.js': 'jsx' }, + } + }; + } + + frameworkOverrides.build = { + target: 'esnext', + minify: false, + rollupOptions: { + treeshake: false, + input: { + index: path.join(dirs.templateDir, 'index.html') + }, + }, + sourcemap: true, + }; + + // We assume that any non-empty plugin list includes `vite-react` or similar. + if (frameworkPluginFactory && !baseAndUserConfig.plugins?.length) + frameworkOverrides.plugins = [await frameworkPluginFactory()]; + + return mergeConfig(baseAndUserConfig, frameworkOverrides); +} + +export async function populateComponentsFromTests(componentRegistry: ComponentRegistry, componentsByImportingFile?: Map<string, string[]>) { + const importInfos: Map<string, ImportInfo[]> = await getUserData('playwright-ct-core'); + for (const [file, importList] of importInfos) { + for (const importInfo of importList) + componentRegistry.set(importInfo.id, importInfo); + if (componentsByImportingFile) + componentsByImportingFile.set(file, importList.map(i => resolveHook(i.filename, i.importSource)).filter(Boolean) as string[]); + } +} + +export function hasJSComponents(components: ImportInfo[]): boolean { + for (const component of components) { + const importPath = resolveHook(component.filename, component.importSource); + const extname = importPath ? path.extname(importPath) : ''; + if (extname === '.js' || (importPath && !extname && fs.existsSync(importPath + '.js'))) + return true; + } + return false; +} + +const importReactRE = /(^|\n|;)import\s+(\*\s+as\s+)?React(,|\s+)/; +const compiledReactRE = /(const|var)\s+React\s*=/; + +export function transformIndexFile(id: string, content: string, templateDir: string, registerSource: string, importInfos: Map<string, ImportInfo>): TransformResult | null { + // Vite React plugin will do this for .jsx files, but not .js files. + if (id.endsWith('.js') && content.includes('React.createElement') && !content.match(importReactRE) && !content.match(compiledReactRE)) { + const code = `import React from 'react';\n${content}`; + return { code, map: { mappings: '' } }; + } + + const indexTs = path.join(templateDir, 'index.ts'); + const indexTsx = path.join(templateDir, 'index.tsx'); + const indexJs = path.join(templateDir, 'index.js'); + const indexJsx = path.join(templateDir, 'index.jsx'); + const idResolved = path.resolve(id); + if (!idResolved.endsWith(indexTs) && !idResolved.endsWith(indexTsx) && !idResolved.endsWith(indexJs) && !idResolved.endsWith(indexJsx)) + return null; + + const lines = [content, '']; + lines.push(registerSource); + + for (const value of importInfos.values()) { + const importPath = resolveHook(value.filename, value.importSource); + lines.push(`const ${value.id} = () => import('${importPath?.replaceAll(path.sep, '/')}').then((mod) => mod.${value.remoteName || 'default'});`); + } + + lines.push(`__pwRegistry.initialize({ ${[...importInfos.keys()].join(',\n ')} });`); + return { + code: lines.join('\n'), + map: { mappings: '' } + }; +} + +export function frameworkConfig(config: FullConfig): { registerSourceFile: string, frameworkPluginFactory?: () => Promise<Plugin> } { + return (config as any)['@playwright/experimental-ct-core']; +} diff --git a/playwright/packages/playwright-ct-core/types/component.d.ts b/playwright/packages/playwright-ct-core/types/component.d.ts index 1b43a944e4..7502ec2f06 100644 --- a/playwright/packages/playwright-ct-core/types/component.d.ts +++ b/playwright/packages/playwright-ct-core/types/component.d.ts @@ -55,7 +55,6 @@ declare global { __pw_hooks_after_mount?: (<HooksConfig extends JsonObject = JsonObject>( params: { hooksConfig?: HooksConfig; [key: string]: any } ) => Promise<void>)[]; - __pwRegistry: ImportRegistry; // Can't start with __pw due to core reuse bindings logic for __pw*. __ctDispatchFunction: (ordinal: number, args: any[]) => void; __pwUnwrapObject: (value: any) => Promise<any>; diff --git a/playwright/packages/playwright-ct-react/cli.js b/playwright/packages/playwright-ct-react/cli.js index 14f0c7df88..9cc834ee95 100755 --- a/playwright/packages/playwright-ct-react/cli.js +++ b/playwright/packages/playwright-ct-react/cli.js @@ -14,4 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('@playwright/experimental-ct-core/cli'); + +const { program } = require('@playwright/experimental-ct-core/lib/program'); + +program.parse(process.argv); diff --git a/playwright/packages/playwright-ct-react/index.d.ts b/playwright/packages/playwright-ct-react/index.d.ts index 807b929b9b..7cfcf04ba0 100644 --- a/playwright/packages/playwright-ct-react/index.d.ts +++ b/playwright/packages/playwright-ct-react/index.d.ts @@ -22,7 +22,7 @@ export interface MountOptions<HooksConfig extends JsonObject> { hooksConfig?: HooksConfig; } -interface MountResult extends Locator { +export interface MountResult extends Locator { unmount(): Promise<void>; update(component: JSX.Element): Promise<void>; } diff --git a/playwright/packages/playwright-ct-react/index.js b/playwright/packages/playwright-ct-react/index.js index cc7b4fcb03..3308747541 100644 --- a/playwright/packages/playwright-ct-react/index.js +++ b/playwright/packages/playwright-ct-react/index.js @@ -17,13 +17,17 @@ const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); -const plugin = () => { - // Only fetch upon request to avoid resolution in workers. - const { createPlugin } = require('@playwright/experimental-ct-core/lib/vitePlugin'); - return createPlugin( - path.join(__dirname, 'registerSource.mjs'), - () => import('@vitejs/plugin-react').then(plugin => plugin.default())); +const defineConfig = (config, ...configs) => { + return originalDefineConfig({ + ...config, + '@playwright/test': { + packageJSON: require.resolve('./package.json'), + }, + '@playwright/experimental-ct-core': { + registerSourceFile: path.join(__dirname, 'registerSource.mjs'), + frameworkPluginFactory: () => import('@vitejs/plugin-react').then(plugin => plugin.default()), + }, + }, ...configs); }; -const defineConfig = (config, ...configs) => originalDefineConfig({ ...config, _plugins: [plugin] }, ...configs); module.exports = { test, expect, devices, defineConfig }; diff --git a/playwright/packages/playwright-ct-react/package.json b/playwright/packages/playwright-ct-react/package.json index a832cd0745..184a0810f6 100644 --- a/playwright/packages/playwright-ct-react/package.json +++ b/playwright/packages/playwright-ct-react/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -29,10 +29,11 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-react": "^4.0.0" + "@playwright/experimental-ct-core": "1.42.0", + "@vitejs/plugin-react": "^4.2.1" }, "bin": { - "playwright": "cli.js" + "playwright": "cli.js", + "pw-react": "cli.js" } } diff --git a/playwright/packages/playwright-ct-react/registerSource.mjs b/playwright/packages/playwright-ct-react/registerSource.mjs index 0a30836824..9ad2612bc4 100644 --- a/playwright/packages/playwright-ct-react/registerSource.mjs +++ b/playwright/packages/playwright-ct-react/registerSource.mjs @@ -21,7 +21,7 @@ import __pwReact from 'react'; import { createRoot as __pwCreateRoot } from 'react-dom/client'; /** @typedef {import('../playwright-ct-core/types/component').JsxComponent} JsxComponent */ -/** @type {Map<Element, import('react-dom/client').Root>} */ +/** @type {Map<Element, { root: import('react-dom/client').Root, setRenderer: (renderer: any) => void }>} */ const __pwRootRegistry = new Map(); /** @@ -40,7 +40,11 @@ function __pwRender(value) { if (isJsxComponent(v)) { const component = v; const props = component.props ? __pwRender(component.props) : {}; - return { result: __pwReact.createElement(/** @type { any } */ (component.type), { ...props, children: undefined }, props.children) }; + const { children, ...propsWithoutChildren } = props; + const createElementArguments = [propsWithoutChildren]; + if (children) + createElementArguments.push(children); + return { result: __pwReact.createElement(component.type, ...createElementArguments) }; } }); } @@ -48,33 +52,41 @@ function __pwRender(value) { window.playwrightMount = async (component, rootElement, hooksConfig) => { if (!isJsxComponent(component)) throw new Error('Object mount notation is not supported'); - - let App = () => __pwRender(component); - for (const hook of window.__pw_hooks_before_mount || []) { - const wrapper = await hook({ App, hooksConfig }); - if (wrapper) - App = () => wrapper; - } - if (__pwRootRegistry.has(rootElement)) { throw new Error( 'Attempting to mount a component into an container that already has a React root' ); } + const root = __pwCreateRoot(rootElement); - __pwRootRegistry.set(rootElement, root); - root.render(App()); + const entry = { root, setRenderer: () => undefined }; + __pwRootRegistry.set(rootElement, entry); + + const App = () => { + /** @type {any} */ + const [renderer, setRenderer] = __pwReact.useState(() => __pwRender(component)); + entry.setRenderer = setRenderer; + return renderer; + }; + let AppWrapper = App; + for (const hook of window.__pw_hooks_before_mount || []) { + const wrapper = await hook({ App: AppWrapper, hooksConfig }); + if (wrapper) + AppWrapper = () => wrapper; + } + + root.render(__pwReact.createElement(AppWrapper)); for (const hook of window.__pw_hooks_after_mount || []) await hook({ hooksConfig }); }; window.playwrightUnmount = async rootElement => { - const root = __pwRootRegistry.get(rootElement); - if (root === undefined) + const entry = __pwRootRegistry.get(rootElement); + if (!entry) throw new Error('Component was not mounted'); - root.unmount(); + entry.root.unmount(); __pwRootRegistry.delete(rootElement); }; @@ -82,9 +94,8 @@ window.playwrightUpdate = async (rootElement, component) => { if (!isJsxComponent(component)) throw new Error('Object mount notation is not supported'); - const root = __pwRootRegistry.get(rootElement); - if (root === undefined) + const entry = __pwRootRegistry.get(rootElement); + if (!entry) throw new Error('Component was not mounted'); - - root.render(__pwRender(component)); + entry.setRenderer(() => __pwRender(component)); }; diff --git a/playwright/packages/playwright-ct-react17/cli.js b/playwright/packages/playwright-ct-react17/cli.js index 14f0c7df88..b6f935eb52 100755 --- a/playwright/packages/playwright-ct-react17/cli.js +++ b/playwright/packages/playwright-ct-react17/cli.js @@ -14,4 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('@playwright/experimental-ct-core/cli'); + +const { program, initializePlugin } = require('@playwright/experimental-ct-core/lib/program'); +const { _framework } = require('./index'); + +initializePlugin(_framework); +program.parse(process.argv); diff --git a/playwright/packages/playwright-ct-react17/index.d.ts b/playwright/packages/playwright-ct-react17/index.d.ts index 807b929b9b..7cfcf04ba0 100644 --- a/playwright/packages/playwright-ct-react17/index.d.ts +++ b/playwright/packages/playwright-ct-react17/index.d.ts @@ -22,7 +22,7 @@ export interface MountOptions<HooksConfig extends JsonObject> { hooksConfig?: HooksConfig; } -interface MountResult extends Locator { +export interface MountResult extends Locator { unmount(): Promise<void>; update(component: JSX.Element): Promise<void>; } diff --git a/playwright/packages/playwright-ct-react17/index.js b/playwright/packages/playwright-ct-react17/index.js index cc7b4fcb03..3308747541 100644 --- a/playwright/packages/playwright-ct-react17/index.js +++ b/playwright/packages/playwright-ct-react17/index.js @@ -17,13 +17,17 @@ const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); -const plugin = () => { - // Only fetch upon request to avoid resolution in workers. - const { createPlugin } = require('@playwright/experimental-ct-core/lib/vitePlugin'); - return createPlugin( - path.join(__dirname, 'registerSource.mjs'), - () => import('@vitejs/plugin-react').then(plugin => plugin.default())); +const defineConfig = (config, ...configs) => { + return originalDefineConfig({ + ...config, + '@playwright/test': { + packageJSON: require.resolve('./package.json'), + }, + '@playwright/experimental-ct-core': { + registerSourceFile: path.join(__dirname, 'registerSource.mjs'), + frameworkPluginFactory: () => import('@vitejs/plugin-react').then(plugin => plugin.default()), + }, + }, ...configs); }; -const defineConfig = (config, ...configs) => originalDefineConfig({ ...config, _plugins: [plugin] }, ...configs); module.exports = { test, expect, devices, defineConfig }; diff --git a/playwright/packages/playwright-ct-react17/package.json b/playwright/packages/playwright-ct-react17/package.json index aeb032ca12..73dbbb9dea 100644 --- a/playwright/packages/playwright-ct-react17/package.json +++ b/playwright/packages/playwright-ct-react17/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react17", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -29,10 +29,11 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", - "@vitejs/plugin-react": "^4.0.0" + "@playwright/experimental-ct-core": "1.42.0", + "@vitejs/plugin-react": "^4.2.1" }, "bin": { - "playwright": "cli.js" + "playwright": "cli.js", + "pw-react17": "cli.js" } } diff --git a/playwright/packages/playwright-ct-react17/registerSource.mjs b/playwright/packages/playwright-ct-react17/registerSource.mjs index 9363b4bb22..158984f3ac 100644 --- a/playwright/packages/playwright-ct-react17/registerSource.mjs +++ b/playwright/packages/playwright-ct-react17/registerSource.mjs @@ -21,6 +21,9 @@ import __pwReact from 'react'; import __pwReactDOM from 'react-dom'; /** @typedef {import('../playwright-ct-core/types/component').JsxComponent} JsxComponent */ +/** @type {Map<Element, { setRenderer: (renderer: any) => void }>} */ +const __pwRootRegistry = new Map(); + /** * @param {any} component * @returns {component is JsxComponent} @@ -37,7 +40,11 @@ function __pwRender(value) { if (isJsxComponent(v)) { const component = v; const props = component.props ? __pwRender(component.props) : {}; - return { result: __pwReact.createElement(/** @type { any } */ (component.type), { ...props, children: undefined }, props.children) }; + const { children, ...propsWithoutChildren } = props; + const createElementArguments = [propsWithoutChildren]; + if (children) + createElementArguments.push(children); + return { result: __pwReact.createElement(component.type, ...createElementArguments) }; } }); } @@ -45,15 +52,29 @@ function __pwRender(value) { window.playwrightMount = async (component, rootElement, hooksConfig) => { if (!isJsxComponent(component)) throw new Error('Object mount notation is not supported'); + if (__pwRootRegistry.has(rootElement)) { + throw new Error( + 'Attempting to mount a component into an container that already has a React root' + ); + } - let App = () => __pwRender(component); + const entry = { setRenderer: () => undefined }; + __pwRootRegistry.set(rootElement, entry); + + const App = () => { + /** @type {any} */ + const [renderer, setRenderer] = __pwReact.useState(() => __pwRender(component)); + entry.setRenderer = setRenderer; + return renderer; + }; + let AppWrapper = App; for (const hook of window.__pw_hooks_before_mount || []) { - const wrapper = await hook({ App, hooksConfig }); + const wrapper = await hook({ App: AppWrapper, hooksConfig }); if (wrapper) - App = () => wrapper; + AppWrapper = () => wrapper; } - __pwReactDOM.render(App(), rootElement); + __pwReactDOM.render(__pwReact.createElement(AppWrapper), rootElement); for (const hook of window.__pw_hooks_after_mount || []) await hook({ hooksConfig }); @@ -68,5 +89,8 @@ window.playwrightUpdate = async (rootElement, component) => { if (!isJsxComponent(component)) throw new Error('Object mount notation is not supported'); - __pwReactDOM.render(__pwRender(component), rootElement); + const entry = __pwRootRegistry.get(rootElement); + if (!entry) + throw new Error('Component was not mounted'); + entry.setRenderer(() => __pwRender(component)); }; diff --git a/playwright/packages/playwright-ct-solid/cli.js b/playwright/packages/playwright-ct-solid/cli.js index 14f0c7df88..b6f935eb52 100755 --- a/playwright/packages/playwright-ct-solid/cli.js +++ b/playwright/packages/playwright-ct-solid/cli.js @@ -14,4 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('@playwright/experimental-ct-core/cli'); + +const { program, initializePlugin } = require('@playwright/experimental-ct-core/lib/program'); +const { _framework } = require('./index'); + +initializePlugin(_framework); +program.parse(process.argv); diff --git a/playwright/packages/playwright-ct-solid/index.d.ts b/playwright/packages/playwright-ct-solid/index.d.ts index 807b929b9b..7cfcf04ba0 100644 --- a/playwright/packages/playwright-ct-solid/index.d.ts +++ b/playwright/packages/playwright-ct-solid/index.d.ts @@ -22,7 +22,7 @@ export interface MountOptions<HooksConfig extends JsonObject> { hooksConfig?: HooksConfig; } -interface MountResult extends Locator { +export interface MountResult extends Locator { unmount(): Promise<void>; update(component: JSX.Element): Promise<void>; } diff --git a/playwright/packages/playwright-ct-solid/index.js b/playwright/packages/playwright-ct-solid/index.js index 110c394255..c2feef7455 100644 --- a/playwright/packages/playwright-ct-solid/index.js +++ b/playwright/packages/playwright-ct-solid/index.js @@ -17,13 +17,17 @@ const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); -const plugin = () => { - // Only fetch upon request to avoid resolution in workers. - const { createPlugin } = require('@playwright/experimental-ct-core/lib/vitePlugin'); - return createPlugin( - path.join(__dirname, 'registerSource.mjs'), - () => import('vite-plugin-solid').then(plugin => plugin.default())); +const defineConfig = (config, ...configs) => { + return originalDefineConfig({ + ...config, + '@playwright/test': { + packageJSON: require.resolve('./package.json'), + }, + '@playwright/experimental-ct-core': { + registerSourceFile: path.join(__dirname, 'registerSource.mjs'), + frameworkPluginFactory: () => import('vite-plugin-solid').then(plugin => plugin.default()), + }, + }, ...configs); }; -const defineConfig = (config, ...configs) => originalDefineConfig({ ...config, _plugins: [plugin] }, ...configs); module.exports = { test, expect, devices, defineConfig }; diff --git a/playwright/packages/playwright-ct-solid/package.json b/playwright/packages/playwright-ct-solid/package.json index 5b28afd9b6..9d474256ff 100644 --- a/playwright/packages/playwright-ct-solid/package.json +++ b/playwright/packages/playwright-ct-solid/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-solid", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright Component Testing for Solid", "repository": { "type": "git", @@ -29,13 +29,14 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", + "@playwright/experimental-ct-core": "1.42.0", "vite-plugin-solid": "^2.7.0" }, "devDependencies": { "solid-js": "^1.7.0" }, "bin": { - "playwright": "cli.js" + "playwright": "cli.js", + "pw-solid": "cli.js" } } diff --git a/playwright/packages/playwright-ct-solid/registerSource.mjs b/playwright/packages/playwright-ct-solid/registerSource.mjs index a76792c513..d0077dd494 100644 --- a/playwright/packages/playwright-ct-solid/registerSource.mjs +++ b/playwright/packages/playwright-ct-solid/registerSource.mjs @@ -19,9 +19,7 @@ import { render as __pwSolidRender, createComponent as __pwSolidCreateComponent } from 'solid-js/web'; import __pwH from 'solid-js/h'; - /** @typedef {import('../playwright-ct-core/types/component').JsxComponent} JsxComponent */ -/** @typedef {() => import('solid-js').JSX.Element} FrameworkComponent */ /** * @param {any} component @@ -32,42 +30,20 @@ function isJsxComponent(component) { } /** - * @param {any} child - */ -function __pwCreateChild(child) { - if (Array.isArray(child)) - return child.map(grandChild => __pwCreateChild(grandChild)); - if (isJsxComponent(child)) - return __pwCreateComponent(child); - return child; -} - -/** - * @param {JsxComponent} component - * @returns {any[] | undefined} + * @param {any} value */ -function __pwJsxChildArray(component) { - if (!component.props.children) - return; - if (Array.isArray(component.props.children)) - return component.props.children; - return [component.props.children]; -} - -/** - * @param {JsxComponent} component - */ -function __pwCreateComponent(component) { - const children = __pwJsxChildArray(component)?.map(child => __pwCreateChild(child)).filter(child => { - if (typeof child === 'string') - return !!child.trim(); - return true; +function __pwCreateComponent(value) { + return window.__pwTransformObject(value, v => { + if (isJsxComponent(v)) { + const component = v; + const props = component.props ? __pwCreateComponent(component.props) : {}; + if (typeof component.type === 'string') { + const { children, ...propsWithoutChildren } = props; + return { result: __pwH(component.type, propsWithoutChildren, children) }; + } + return { result: __pwSolidCreateComponent(component.type, props) }; + } }); - - if (typeof component.type === 'string') - return __pwH(component.type, component.props, children); - - return __pwSolidCreateComponent(component.type, { ...component.props, children }); } const __pwUnmountKey = Symbol('unmountKey'); @@ -96,6 +72,7 @@ window.playwrightUnmount = async rootElement => { throw new Error('Component was not mounted'); unmount(); + delete rootElement[__pwUnmountKey]; }; window.playwrightUpdate = async (rootElement, component) => { diff --git a/playwright/packages/playwright-ct-svelte/cli.js b/playwright/packages/playwright-ct-svelte/cli.js index 14f0c7df88..b6f935eb52 100755 --- a/playwright/packages/playwright-ct-svelte/cli.js +++ b/playwright/packages/playwright-ct-svelte/cli.js @@ -14,4 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('@playwright/experimental-ct-core/cli'); + +const { program, initializePlugin } = require('@playwright/experimental-ct-core/lib/program'); +const { _framework } = require('./index'); + +initializePlugin(_framework); +program.parse(process.argv); diff --git a/playwright/packages/playwright-ct-svelte/index.d.ts b/playwright/packages/playwright-ct-svelte/index.d.ts index 197f6228c1..bd1ddd5b36 100644 --- a/playwright/packages/playwright-ct-svelte/index.d.ts +++ b/playwright/packages/playwright-ct-svelte/index.d.ts @@ -30,7 +30,7 @@ export interface MountOptions<HooksConfig extends JsonObject, Component extends hooksConfig?: HooksConfig; } -interface MountResult<Component extends SvelteComponent> extends Locator { +export interface MountResult<Component extends SvelteComponent> extends Locator { unmount(): Promise<void>; update(options: { props?: Partial<ComponentProps<Component>>; diff --git a/playwright/packages/playwright-ct-svelte/index.js b/playwright/packages/playwright-ct-svelte/index.js index 313af1b634..841ff852c7 100644 --- a/playwright/packages/playwright-ct-svelte/index.js +++ b/playwright/packages/playwright-ct-svelte/index.js @@ -17,13 +17,17 @@ const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); -const plugin = () => { - // Only fetch upon request to avoid resolution in workers. - const { createPlugin } = require('@playwright/experimental-ct-core/lib/vitePlugin'); - return createPlugin( - path.join(__dirname, 'registerSource.mjs'), - () => import('@sveltejs/vite-plugin-svelte').then(plugin => plugin.svelte())); +const defineConfig = (config, ...configs) => { + return originalDefineConfig({ + ...config, + '@playwright/test': { + packageJSON: require.resolve('./package.json'), + }, + '@playwright/experimental-ct-core': { + registerSourceFile: path.join(__dirname, 'registerSource.mjs'), + frameworkPluginFactory: () => import('@sveltejs/vite-plugin-svelte').then(plugin => plugin.svelte()), + }, + }, ...configs); }; -const defineConfig = (config, ...configs) => originalDefineConfig({ ...config, _plugins: [plugin] }, ...configs); module.exports = { test, expect, devices, defineConfig }; diff --git a/playwright/packages/playwright-ct-svelte/package.json b/playwright/packages/playwright-ct-svelte/package.json index 9c4bd12ab9..69454c5603 100644 --- a/playwright/packages/playwright-ct-svelte/package.json +++ b/playwright/packages/playwright-ct-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-svelte", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright Component Testing for Svelte", "repository": { "type": "git", @@ -29,22 +29,14 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", + "@playwright/experimental-ct-core": "1.42.0", "@sveltejs/vite-plugin-svelte": "^3.0.1" }, "devDependencies": { "svelte": "^4.2.8" }, - "peerDependencies": { - "svelte": "^4.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0" - }, - "peerDependenciesMeta": { - "@sveltejs/vite-plugin-svelte": { - "optional": true - } - }, "bin": { - "playwright": "cli.js" + "playwright": "cli.js", + "pw-svelte": "cli.js" } } diff --git a/playwright/packages/playwright-ct-svelte/registerSource.mjs b/playwright/packages/playwright-ct-svelte/registerSource.mjs index 4552a4ba0a..5901458813 100644 --- a/playwright/packages/playwright-ct-svelte/registerSource.mjs +++ b/playwright/packages/playwright-ct-svelte/registerSource.mjs @@ -42,8 +42,8 @@ function __pwCreateSlots(slots) { for (const slotName in slots) { const template = document - .createRange() - .createContextualFragment(slots[slotName]); + .createRange() + .createContextualFragment(slots[slotName]); svelteSlots[slotName] = [createSlotFn(template)]; } @@ -55,7 +55,8 @@ function __pwCreateSlots(slots) { __pwInsert(target, element, anchor); }, d: function destroy(detaching) { - if (detaching) __pwDetach(element); + if (detaching) + __pwDetach(element); }, l: __pwNoop, }; @@ -108,6 +109,7 @@ window.playwrightUnmount = async rootElement => { if (!svelteComponent) throw new Error('Component was not mounted'); svelteComponent.$destroy(); + delete rootElement[__pwSvelteComponentKey]; }; window.playwrightUpdate = async (rootElement, component) => { diff --git a/playwright/packages/playwright-ct-vue/cli.js b/playwright/packages/playwright-ct-vue/cli.js index 14f0c7df88..b6f935eb52 100755 --- a/playwright/packages/playwright-ct-vue/cli.js +++ b/playwright/packages/playwright-ct-vue/cli.js @@ -14,4 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('@playwright/experimental-ct-core/cli'); + +const { program, initializePlugin } = require('@playwright/experimental-ct-core/lib/program'); +const { _framework } = require('./index'); + +initializePlugin(_framework); +program.parse(process.argv); diff --git a/playwright/packages/playwright-ct-vue/index.d.ts b/playwright/packages/playwright-ct-vue/index.d.ts index e253bc1a35..308e47c18c 100644 --- a/playwright/packages/playwright-ct-vue/index.d.ts +++ b/playwright/packages/playwright-ct-vue/index.d.ts @@ -40,7 +40,7 @@ export interface MountOptionsJsx<HooksConfig extends JsonObject> { hooksConfig?: HooksConfig; } -interface MountResult<Component> extends Locator { +export interface MountResult<Component> extends Locator { unmount(): Promise<void>; update(options: { props?: Partial<ComponentProps<Component>>; @@ -49,7 +49,7 @@ interface MountResult<Component> extends Locator { }): Promise<void>; } -interface MountResultJsx extends Locator { +export interface MountResultJsx extends Locator { unmount(): Promise<void>; update(component: JSX.Element): Promise<void>; } diff --git a/playwright/packages/playwright-ct-vue/index.js b/playwright/packages/playwright-ct-vue/index.js index ce321f8415..b01b23e412 100644 --- a/playwright/packages/playwright-ct-vue/index.js +++ b/playwright/packages/playwright-ct-vue/index.js @@ -17,13 +17,17 @@ const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); -const plugin = () => { - // Only fetch upon request to avoid resolution in workers. - const { createPlugin } = require('@playwright/experimental-ct-core/lib/vitePlugin'); - return createPlugin( - path.join(__dirname, 'registerSource.mjs'), - () => import('@vitejs/plugin-vue').then(plugin => plugin.default())); -} -const defineConfig = (config, ...configs) => originalDefineConfig({ ...config, _plugins: [plugin] }, ...configs); +const defineConfig = (config, ...configs) => { + return originalDefineConfig({ + ...config, + '@playwright/test': { + packageJSON: require.resolve('./package.json'), + }, + '@playwright/experimental-ct-core': { + registerSourceFile: path.join(__dirname, 'registerSource.mjs'), + frameworkPluginFactory: () => import('@vitejs/plugin-vue').then(plugin => plugin.default()), + }, + }, ...configs); +}; module.exports = { test, expect, devices, defineConfig }; diff --git a/playwright/packages/playwright-ct-vue/package.json b/playwright/packages/playwright-ct-vue/package.json index a19e633adc..716d368ae6 100644 --- a/playwright/packages/playwright-ct-vue/package.json +++ b/playwright/packages/playwright-ct-vue/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright Component Testing for Vue", "repository": { "type": "git", @@ -29,10 +29,11 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", + "@playwright/experimental-ct-core": "1.42.0", "@vitejs/plugin-vue": "^4.2.1" }, "bin": { - "playwright": "cli.js" + "playwright": "cli.js", + "pw-vue": "cli.js" } } diff --git a/playwright/packages/playwright-ct-vue/registerSource.mjs b/playwright/packages/playwright-ct-vue/registerSource.mjs index 5256a241a4..07ce5298f4 100644 --- a/playwright/packages/playwright-ct-vue/registerSource.mjs +++ b/playwright/packages/playwright-ct-vue/registerSource.mjs @@ -88,6 +88,9 @@ function __pwCreateSlot(html) { }; } +/** + * @param {string | string[]} slot + */ function __pwSlotToFunction(slot) { if (typeof slot === 'string') return __pwCreateSlot(slot)(); @@ -175,7 +178,11 @@ function __pwCreateComponent(component) { return { Component: component.type, props, slots: lastArg, listeners }; } +/** + * @param {any} slots + */ function __pwWrapFunctions(slots) { + /** @type {import('vue').ComponentInternalInstance['slots']} */ const slotsWithRenderFunctions = {}; if (!Array.isArray(slots)) { for (const [key, value] of Object.entries(slots || {})) @@ -198,25 +205,27 @@ function __pwCreateWrapper(component) { return wrapper; } -/** - * @returns {any} - */ -function __pwCreateDevTools() { - return { +function __pwSetDevTools() { + __pwSetDevtoolsHook({ emit(eventType, ...payload) { - if (eventType === 'component:emit') { - const [, componentVM, event, eventArgs] = payload; - for (const [wrapper, listeners] of __pwAllListeners) { - if (wrapper.component !== componentVM) - continue; - const listener = listeners[event]; - if (!listener) - return; - listener(...eventArgs); - } + if (eventType !== 'component:emit') + return; + + const [, componentVM, event, eventArgs] = payload; + for (const [wrapper, listeners] of __pwAllListeners) { + if (wrapper.component !== componentVM) + continue; + const listener = listeners[event]; + if (!listener) + return; + listener(...eventArgs); } - } - }; + }, + on() {}, + off() {}, + once() {}, + appRecords: [] + }, {}); } const __pwAppKey = Symbol('appKey'); @@ -230,7 +239,7 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => { return wrapper; } }); - __pwSetDevtoolsHook(__pwCreateDevTools(), {}); + __pwSetDevTools(); for (const hook of window.__pw_hooks_before_mount || []) await hook({ app, hooksConfig }); @@ -242,13 +251,16 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => { }; window.playwrightUnmount = async rootElement => { - const app = /** @type {import('vue').App} */ (rootElement[__pwAppKey]); + /** @type {import('vue').App<Element> | undefined} */ + const app = rootElement[__pwAppKey]; if (!app) throw new Error('Component was not mounted'); app.unmount(); + delete rootElement[__pwAppKey]; }; window.playwrightUpdate = async (rootElement, component) => { + /** @type {import('vue').VNode | undefined} */ const wrapper = rootElement[__pwWrapperKey]; if (!wrapper) throw new Error('Component was not mounted'); diff --git a/playwright/packages/playwright-ct-vue2/cli.js b/playwright/packages/playwright-ct-vue2/cli.js index 14f0c7df88..b6f935eb52 100755 --- a/playwright/packages/playwright-ct-vue2/cli.js +++ b/playwright/packages/playwright-ct-vue2/cli.js @@ -14,4 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('@playwright/experimental-ct-core/cli'); + +const { program, initializePlugin } = require('@playwright/experimental-ct-core/lib/program'); +const { _framework } = require('./index'); + +initializePlugin(_framework); +program.parse(process.argv); diff --git a/playwright/packages/playwright-ct-vue2/index.d.ts b/playwright/packages/playwright-ct-vue2/index.d.ts index f4bda8830f..f76285fb8f 100644 --- a/playwright/packages/playwright-ct-vue2/index.d.ts +++ b/playwright/packages/playwright-ct-vue2/index.d.ts @@ -40,7 +40,7 @@ export interface MountOptionsJsx<HooksConfig extends JsonObject> { hooksConfig?: HooksConfig; } -interface MountResult<Component> extends Locator { +export interface MountResult<Component> extends Locator { unmount(): Promise<void>; update(options: { props?: Partial<ComponentProps<Component>>; @@ -49,7 +49,7 @@ interface MountResult<Component> extends Locator { }): Promise<void>; } -interface MountResultJsx extends Locator { +export interface MountResultJsx extends Locator { unmount(): Promise<void>; update(component: JSX.Element): Promise<void>; } diff --git a/playwright/packages/playwright-ct-vue2/index.js b/playwright/packages/playwright-ct-vue2/index.js index 8930a8a4f1..2eeabb0d08 100644 --- a/playwright/packages/playwright-ct-vue2/index.js +++ b/playwright/packages/playwright-ct-vue2/index.js @@ -17,13 +17,17 @@ const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); -const plugin = () => { - // Only fetch upon request to avoid resolution in workers. - const { createPlugin } = require('@playwright/experimental-ct-core/lib/vitePlugin'); - return createPlugin( - path.join(__dirname, 'registerSource.mjs'), - () => import('@vitejs/plugin-vue2').then(plugin => plugin.default())); +const defineConfig = (config, ...configs) => { + return originalDefineConfig({ + ...config, + '@playwright/test': { + packageJSON: require.resolve('./package.json'), + }, + '@playwright/experimental-ct-core': { + registerSourceFile: path.join(__dirname, 'registerSource.mjs'), + frameworkPluginFactory: () => import('@vitejs/plugin-vue2').then(plugin => plugin.default()), + }, + }, ...configs); }; -const defineConfig = (config, ...configs) => originalDefineConfig({ ...config, _plugins: [plugin] }, ...configs); module.exports = { test, expect, devices, defineConfig }; diff --git a/playwright/packages/playwright-ct-vue2/package.json b/playwright/packages/playwright-ct-vue2/package.json index 4303731fcd..06fde874e5 100644 --- a/playwright/packages/playwright-ct-vue2/package.json +++ b/playwright/packages/playwright-ct-vue2/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue2", - "version": "1.41.1", + "version": "1.42.0", "description": "Playwright Component Testing for Vue2", "repository": { "type": "git", @@ -29,13 +29,14 @@ } }, "dependencies": { - "@playwright/experimental-ct-core": "1.41.1", + "@playwright/experimental-ct-core": "1.42.0", "@vitejs/plugin-vue2": "^2.2.0" }, "devDependencies": { "vue": "^2.7.14" }, "bin": { - "playwright": "cli.js" + "playwright": "cli.js", + "pw-vue2": "cli.js" } } diff --git a/playwright/packages/playwright-ct-vue2/registerSource.mjs b/playwright/packages/playwright-ct-vue2/registerSource.mjs index b0a7ae71dc..19b4d41c08 100644 --- a/playwright/packages/playwright-ct-vue2/registerSource.mjs +++ b/playwright/packages/playwright-ct-vue2/registerSource.mjs @@ -182,6 +182,7 @@ window.playwrightUnmount = async rootElement => { throw new Error('Component was not mounted'); component.$destroy(); component.$el.remove(); + delete rootElement[instanceKey]; }; window.playwrightUpdate = async (element, options) => { diff --git a/playwright/packages/playwright-firefox/cli.js b/playwright/packages/playwright-firefox/cli.js index bd995de114..86adb86a84 100755 --- a/playwright/packages/playwright-firefox/cli.js +++ b/playwright/packages/playwright-firefox/cli.js @@ -14,4 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('playwright-core/lib/cli/cli'); + +const { program } = require('playwright-core/lib/program'); +program.parse(process.argv); diff --git a/playwright/packages/playwright-firefox/package.json b/playwright/packages/playwright-firefox/package.json index 69da28873c..80cb766440 100644 --- a/playwright/packages/playwright-firefox/package.json +++ b/playwright/packages/playwright-firefox/package.json @@ -1,6 +1,6 @@ { "name": "playwright-firefox", - "version": "1.41.1", + "version": "1.42.0", "description": "A high-level API to automate Firefox", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.42.0" } } diff --git a/playwright/packages/playwright-test/cli.js b/playwright/packages/playwright-test/cli.js index 9d6dee9693..e42facb076 100755 --- a/playwright/packages/playwright-test/cli.js +++ b/playwright/packages/playwright-test/cli.js @@ -14,4 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('playwright/cli'); + +const { program } = require('playwright/lib/program'); +program.parse(process.argv); diff --git a/playwright/packages/playwright-test/package.json b/playwright/packages/playwright-test/package.json index 7c344dbfb4..6e7d05730d 100644 --- a/playwright/packages/playwright-test/package.json +++ b/playwright/packages/playwright-test/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/test", - "version": "1.41.1", + "version": "1.42.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -30,6 +30,6 @@ }, "scripts": {}, "dependencies": { - "playwright": "1.41.1" + "playwright": "1.42.0" } } diff --git a/playwright/packages/playwright-webkit/cli.js b/playwright/packages/playwright-webkit/cli.js index bd995de114..86adb86a84 100755 --- a/playwright/packages/playwright-webkit/cli.js +++ b/playwright/packages/playwright-webkit/cli.js @@ -14,4 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('playwright-core/lib/cli/cli'); + +const { program } = require('playwright-core/lib/program'); +program.parse(process.argv); diff --git a/playwright/packages/playwright-webkit/package.json b/playwright/packages/playwright-webkit/package.json index 784f4f29c6..dafb1e5a96 100644 --- a/playwright/packages/playwright-webkit/package.json +++ b/playwright/packages/playwright-webkit/package.json @@ -1,6 +1,6 @@ { "name": "playwright-webkit", - "version": "1.41.1", + "version": "1.42.0", "description": "A high-level API to automate WebKit", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.42.0" } } diff --git a/playwright/packages/playwright/.eslintrc.js b/playwright/packages/playwright/.eslintrc.js index 67c9b6313d..b7e707b35f 100644 --- a/playwright/packages/playwright/.eslintrc.js +++ b/playwright/packages/playwright/.eslintrc.js @@ -1,14 +1,5 @@ -const path = require('path'); - module.exports = { - extends: '../.eslintrc.js', - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint", "notice"], - parserOptions: { - ecmaVersion: 9, - sourceType: "module", - project: path.join(__dirname, '..', '..', 'tsconfig.json'), - }, + extends: '../../.eslintrc-with-ts-config.js', rules: { '@typescript-eslint/no-floating-promises': 'error', }, diff --git a/playwright/packages/playwright/ThirdPartyNotices.txt b/playwright/packages/playwright/ThirdPartyNotices.txt index 32ee414789..ad10348fa1 100644 --- a/playwright/packages/playwright/ThirdPartyNotices.txt +++ b/playwright/packages/playwright/ThirdPartyNotices.txt @@ -5,34 +5,34 @@ THIRD-PARTY SOFTWARE NOTICES AND INFORMATION This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. - @ampproject/remapping@2.2.1 (https://github.com/ampproject/remapping) -- @babel/code-frame@7.22.13 (https://github.com/babel/babel) - @babel/code-frame@7.22.5 (https://github.com/babel/babel) -- @babel/compat-data@7.22.20 (https://github.com/babel/babel) -- @babel/core@7.23.2 (https://github.com/babel/babel) -- @babel/generator@7.23.0 (https://github.com/babel/babel) +- @babel/code-frame@7.23.5 (https://github.com/babel/babel) +- @babel/compat-data@7.23.5 (https://github.com/babel/babel) +- @babel/core@7.23.7 (https://github.com/babel/babel) +- @babel/generator@7.23.6 (https://github.com/babel/babel) - @babel/helper-annotate-as-pure@7.22.5 (https://github.com/babel/babel) -- @babel/helper-compilation-targets@7.22.15 (https://github.com/babel/babel) -- @babel/helper-create-class-features-plugin@7.22.15 (https://github.com/babel/babel) +- @babel/helper-compilation-targets@7.23.6 (https://github.com/babel/babel) +- @babel/helper-create-class-features-plugin@7.23.7 (https://github.com/babel/babel) - @babel/helper-environment-visitor@7.22.20 (https://github.com/babel/babel) - @babel/helper-function-name@7.23.0 (https://github.com/babel/babel) - @babel/helper-hoist-variables@7.22.5 (https://github.com/babel/babel) - @babel/helper-member-expression-to-functions@7.23.0 (https://github.com/babel/babel) - @babel/helper-module-imports@7.22.15 (https://github.com/babel/babel) -- @babel/helper-module-transforms@7.23.0 (https://github.com/babel/babel) +- @babel/helper-module-transforms@7.23.3 (https://github.com/babel/babel) - @babel/helper-optimise-call-expression@7.22.5 (https://github.com/babel/babel) - @babel/helper-plugin-utils@7.22.5 (https://github.com/babel/babel) - @babel/helper-replace-supers@7.22.20 (https://github.com/babel/babel) - @babel/helper-simple-access@7.22.5 (https://github.com/babel/babel) - @babel/helper-skip-transparent-expression-wrappers@7.22.5 (https://github.com/babel/babel) - @babel/helper-split-export-declaration@7.22.6 (https://github.com/babel/babel) -- @babel/helper-string-parser@7.22.5 (https://github.com/babel/babel) +- @babel/helper-string-parser@7.23.4 (https://github.com/babel/babel) - @babel/helper-validator-identifier@7.22.20 (https://github.com/babel/babel) - @babel/helper-validator-identifier@7.22.5 (https://github.com/babel/babel) -- @babel/helper-validator-option@7.22.15 (https://github.com/babel/babel) -- @babel/helpers@7.23.2 (https://github.com/babel/babel) -- @babel/highlight@7.22.20 (https://github.com/babel/babel) +- @babel/helper-validator-option@7.23.5 (https://github.com/babel/babel) +- @babel/helpers@7.23.8 (https://github.com/babel/babel) - @babel/highlight@7.22.5 (https://github.com/babel/babel) -- @babel/parser@7.23.0 (https://github.com/babel/babel) +- @babel/highlight@7.23.4 (https://github.com/babel/babel) +- @babel/parser@7.23.6 (https://github.com/babel/babel) - @babel/plugin-proposal-decorators@7.23.2 (https://github.com/babel/babel) - @babel/plugin-proposal-explicit-resource-management@7.23.0 (https://github.com/babel/babel) - @babel/plugin-syntax-async-generators@7.8.4 (https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-async-generators) @@ -43,7 +43,7 @@ This project incorporates components from the projects listed below. The origina - @babel/plugin-syntax-export-namespace-from@7.8.3 (https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-export-namespace-from) - @babel/plugin-syntax-import-assertions@7.22.5 (https://github.com/babel/babel) - @babel/plugin-syntax-json-strings@7.8.3 (https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-json-strings) -- @babel/plugin-syntax-jsx@7.22.5 (https://github.com/babel/babel) +- @babel/plugin-syntax-jsx@7.23.3 (https://github.com/babel/babel) - @babel/plugin-syntax-logical-assignment-operators@7.10.4 (https://github.com/babel/babel) - @babel/plugin-syntax-nullish-coalescing-operator@7.8.3 (https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-nullish-coalescing-operator) - @babel/plugin-syntax-numeric-separator@7.10.4 (https://github.com/babel/babel) @@ -51,24 +51,24 @@ This project incorporates components from the projects listed below. The origina - @babel/plugin-syntax-optional-catch-binding@7.8.3 (https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-optional-catch-binding) - @babel/plugin-syntax-optional-chaining@7.8.3 (https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-optional-chaining) - @babel/plugin-syntax-private-property-in-object@7.14.5 (https://github.com/babel/babel) -- @babel/plugin-syntax-typescript@7.22.5 (https://github.com/babel/babel) -- @babel/plugin-transform-class-properties@7.22.5 (https://github.com/babel/babel) -- @babel/plugin-transform-class-static-block@7.22.11 (https://github.com/babel/babel) -- @babel/plugin-transform-dynamic-import@7.22.11 (https://github.com/babel/babel) -- @babel/plugin-transform-export-namespace-from@7.22.11 (https://github.com/babel/babel) -- @babel/plugin-transform-logical-assignment-operators@7.22.11 (https://github.com/babel/babel) -- @babel/plugin-transform-modules-commonjs@7.23.0 (https://github.com/babel/babel) -- @babel/plugin-transform-nullish-coalescing-operator@7.22.11 (https://github.com/babel/babel) -- @babel/plugin-transform-numeric-separator@7.22.11 (https://github.com/babel/babel) -- @babel/plugin-transform-optional-chaining@7.23.0 (https://github.com/babel/babel) -- @babel/plugin-transform-private-methods@7.22.5 (https://github.com/babel/babel) -- @babel/plugin-transform-private-property-in-object@7.22.11 (https://github.com/babel/babel) -- @babel/plugin-transform-react-jsx@7.22.15 (https://github.com/babel/babel) -- @babel/plugin-transform-typescript@7.22.15 (https://github.com/babel/babel) -- @babel/preset-typescript@7.23.2 (https://github.com/babel/babel) +- @babel/plugin-syntax-typescript@7.23.3 (https://github.com/babel/babel) +- @babel/plugin-transform-class-properties@7.23.3 (https://github.com/babel/babel) +- @babel/plugin-transform-class-static-block@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-dynamic-import@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-export-namespace-from@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-logical-assignment-operators@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-modules-commonjs@7.23.3 (https://github.com/babel/babel) +- @babel/plugin-transform-nullish-coalescing-operator@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-numeric-separator@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-optional-chaining@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-private-methods@7.23.3 (https://github.com/babel/babel) +- @babel/plugin-transform-private-property-in-object@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-react-jsx@7.23.4 (https://github.com/babel/babel) +- @babel/plugin-transform-typescript@7.23.6 (https://github.com/babel/babel) +- @babel/preset-typescript@7.23.3 (https://github.com/babel/babel) - @babel/template@7.22.15 (https://github.com/babel/babel) -- @babel/traverse@7.23.2 (https://github.com/babel/babel) -- @babel/types@7.23.0 (https://github.com/babel/babel) +- @babel/traverse@7.23.7 (https://github.com/babel/babel) +- @babel/types@7.23.6 (https://github.com/babel/babel) - @jest/expect-utils@29.5.0 (https://github.com/facebook/jest) - @jest/schemas@29.4.3 (https://github.com/facebook/jest) - @jest/types@29.5.0 (https://github.com/facebook/jest) @@ -92,9 +92,9 @@ This project incorporates components from the projects listed below. The origina - anymatch@3.1.3 (https://github.com/micromatch/anymatch) - binary-extensions@2.2.0 (https://github.com/sindresorhus/binary-extensions) - braces@3.0.2 (https://github.com/micromatch/braces) -- browserslist@4.22.1 (https://github.com/browserslist/browserslist) +- browserslist@4.22.2 (https://github.com/browserslist/browserslist) - buffer-from@1.1.2 (https://github.com/LinusU/buffer-from) -- caniuse-lite@1.0.30001541 (https://github.com/browserslist/caniuse-lite) +- caniuse-lite@1.0.30001579 (https://github.com/browserslist/caniuse-lite) - chalk@2.4.2 (https://github.com/chalk/chalk) - chalk@4.1.2 (https://github.com/chalk/chalk) - chokidar@3.5.3 (https://github.com/paulmillr/chokidar) @@ -107,7 +107,7 @@ This project incorporates components from the projects listed below. The origina - convert-source-map@2.0.0 (https://github.com/thlorenz/convert-source-map) - debug@4.3.4 (https://github.com/debug-js/debug) - diff-sequences@29.4.3 (https://github.com/facebook/jest) -- electron-to-chromium@1.4.535 (https://github.com/kilian/electron-to-chromium) +- electron-to-chromium@1.4.638 (https://github.com/kilian/electron-to-chromium) - enquirer@2.3.6 (https://github.com/enquirer/enquirer) - escalade@3.1.1 (https://github.com/lukeed/escalade) - escape-string-regexp@1.0.5 (https://github.com/sindresorhus/escape-string-regexp) @@ -135,7 +135,7 @@ This project incorporates components from the projects listed below. The origina - lru-cache@5.1.1 (https://github.com/isaacs/node-lru-cache) - micromatch@4.0.5 (https://github.com/micromatch/micromatch) - ms@2.1.2 (https://github.com/zeit/ms) -- node-releases@2.0.13 (https://github.com/chicoxyzzy/node-releases) +- node-releases@2.0.14 (https://github.com/chicoxyzzy/node-releases) - normalize-path@3.0.0 (https://github.com/jonschlinkert/normalize-path) - picocolors@1.0.0 (https://github.com/alexeyraspopov/picocolors) - picomatch@2.3.1 (https://github.com/micromatch/picomatch) @@ -362,7 +362,7 @@ Apache License ========================================= END OF @ampproject/remapping@2.2.1 AND INFORMATION -%% @babel/code-frame@7.22.13 NOTICES AND INFORMATION BEGIN HERE +%% @babel/code-frame@7.22.5 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -387,9 +387,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/code-frame@7.22.13 AND INFORMATION +END OF @babel/code-frame@7.22.5 AND INFORMATION -%% @babel/code-frame@7.22.5 NOTICES AND INFORMATION BEGIN HERE +%% @babel/code-frame@7.23.5 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -414,9 +414,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/code-frame@7.22.5 AND INFORMATION +END OF @babel/code-frame@7.23.5 AND INFORMATION -%% @babel/compat-data@7.22.20 NOTICES AND INFORMATION BEGIN HERE +%% @babel/compat-data@7.23.5 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -441,9 +441,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/compat-data@7.22.20 AND INFORMATION +END OF @babel/compat-data@7.23.5 AND INFORMATION -%% @babel/core@7.23.2 NOTICES AND INFORMATION BEGIN HERE +%% @babel/core@7.23.7 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -468,9 +468,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/core@7.23.2 AND INFORMATION +END OF @babel/core@7.23.7 AND INFORMATION -%% @babel/generator@7.23.0 NOTICES AND INFORMATION BEGIN HERE +%% @babel/generator@7.23.6 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -495,7 +495,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/generator@7.23.0 AND INFORMATION +END OF @babel/generator@7.23.6 AND INFORMATION %% @babel/helper-annotate-as-pure@7.22.5 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -524,7 +524,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF @babel/helper-annotate-as-pure@7.22.5 AND INFORMATION -%% @babel/helper-compilation-targets@7.22.15 NOTICES AND INFORMATION BEGIN HERE +%% @babel/helper-compilation-targets@7.23.6 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -549,9 +549,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/helper-compilation-targets@7.22.15 AND INFORMATION +END OF @babel/helper-compilation-targets@7.23.6 AND INFORMATION -%% @babel/helper-create-class-features-plugin@7.22.15 NOTICES AND INFORMATION BEGIN HERE +%% @babel/helper-create-class-features-plugin@7.23.7 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -576,7 +576,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/helper-create-class-features-plugin@7.22.15 AND INFORMATION +END OF @babel/helper-create-class-features-plugin@7.23.7 AND INFORMATION %% @babel/helper-environment-visitor@7.22.20 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -713,7 +713,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF @babel/helper-module-imports@7.22.15 AND INFORMATION -%% @babel/helper-module-transforms@7.23.0 NOTICES AND INFORMATION BEGIN HERE +%% @babel/helper-module-transforms@7.23.3 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -738,7 +738,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/helper-module-transforms@7.23.0 AND INFORMATION +END OF @babel/helper-module-transforms@7.23.3 AND INFORMATION %% @babel/helper-optimise-call-expression@7.22.5 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -902,7 +902,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF @babel/helper-split-export-declaration@7.22.6 AND INFORMATION -%% @babel/helper-string-parser@7.22.5 NOTICES AND INFORMATION BEGIN HERE +%% @babel/helper-string-parser@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -927,7 +927,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/helper-string-parser@7.22.5 AND INFORMATION +END OF @babel/helper-string-parser@7.23.4 AND INFORMATION %% @babel/helper-validator-identifier@7.22.20 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -983,7 +983,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF @babel/helper-validator-identifier@7.22.5 AND INFORMATION -%% @babel/helper-validator-option@7.22.15 NOTICES AND INFORMATION BEGIN HERE +%% @babel/helper-validator-option@7.23.5 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1008,9 +1008,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/helper-validator-option@7.22.15 AND INFORMATION +END OF @babel/helper-validator-option@7.23.5 AND INFORMATION -%% @babel/helpers@7.23.2 NOTICES AND INFORMATION BEGIN HERE +%% @babel/helpers@7.23.8 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1035,9 +1035,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/helpers@7.23.2 AND INFORMATION +END OF @babel/helpers@7.23.8 AND INFORMATION -%% @babel/highlight@7.22.20 NOTICES AND INFORMATION BEGIN HERE +%% @babel/highlight@7.22.5 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1062,9 +1062,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/highlight@7.22.20 AND INFORMATION +END OF @babel/highlight@7.22.5 AND INFORMATION -%% @babel/highlight@7.22.5 NOTICES AND INFORMATION BEGIN HERE +%% @babel/highlight@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1089,9 +1089,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/highlight@7.22.5 AND INFORMATION +END OF @babel/highlight@7.23.4 AND INFORMATION -%% @babel/parser@7.23.0 NOTICES AND INFORMATION BEGIN HERE +%% @babel/parser@7.23.6 NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (C) 2012-2014 by various contributors (see AUTHORS) @@ -1113,7 +1113,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/parser@7.23.0 AND INFORMATION +END OF @babel/parser@7.23.6 AND INFORMATION %% @babel/plugin-proposal-decorators@7.23.2 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -1385,7 +1385,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF @babel/plugin-syntax-json-strings@7.8.3 AND INFORMATION -%% @babel/plugin-syntax-jsx@7.22.5 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-syntax-jsx@7.23.3 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1410,7 +1410,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-syntax-jsx@7.22.5 AND INFORMATION +END OF @babel/plugin-syntax-jsx@7.23.3 AND INFORMATION %% @babel/plugin-syntax-logical-assignment-operators@7.10.4 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -1601,7 +1601,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF @babel/plugin-syntax-private-property-in-object@7.14.5 AND INFORMATION -%% @babel/plugin-syntax-typescript@7.22.5 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-syntax-typescript@7.23.3 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1626,9 +1626,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-syntax-typescript@7.22.5 AND INFORMATION +END OF @babel/plugin-syntax-typescript@7.23.3 AND INFORMATION -%% @babel/plugin-transform-class-properties@7.22.5 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-class-properties@7.23.3 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1653,9 +1653,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-class-properties@7.22.5 AND INFORMATION +END OF @babel/plugin-transform-class-properties@7.23.3 AND INFORMATION -%% @babel/plugin-transform-class-static-block@7.22.11 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-class-static-block@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1680,9 +1680,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-class-static-block@7.22.11 AND INFORMATION +END OF @babel/plugin-transform-class-static-block@7.23.4 AND INFORMATION -%% @babel/plugin-transform-dynamic-import@7.22.11 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-dynamic-import@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1707,9 +1707,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-dynamic-import@7.22.11 AND INFORMATION +END OF @babel/plugin-transform-dynamic-import@7.23.4 AND INFORMATION -%% @babel/plugin-transform-export-namespace-from@7.22.11 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-export-namespace-from@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1734,9 +1734,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-export-namespace-from@7.22.11 AND INFORMATION +END OF @babel/plugin-transform-export-namespace-from@7.23.4 AND INFORMATION -%% @babel/plugin-transform-logical-assignment-operators@7.22.11 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-logical-assignment-operators@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1761,9 +1761,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-logical-assignment-operators@7.22.11 AND INFORMATION +END OF @babel/plugin-transform-logical-assignment-operators@7.23.4 AND INFORMATION -%% @babel/plugin-transform-modules-commonjs@7.23.0 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-modules-commonjs@7.23.3 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1788,9 +1788,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-modules-commonjs@7.23.0 AND INFORMATION +END OF @babel/plugin-transform-modules-commonjs@7.23.3 AND INFORMATION -%% @babel/plugin-transform-nullish-coalescing-operator@7.22.11 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-nullish-coalescing-operator@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1815,9 +1815,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-nullish-coalescing-operator@7.22.11 AND INFORMATION +END OF @babel/plugin-transform-nullish-coalescing-operator@7.23.4 AND INFORMATION -%% @babel/plugin-transform-numeric-separator@7.22.11 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-numeric-separator@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1842,9 +1842,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-numeric-separator@7.22.11 AND INFORMATION +END OF @babel/plugin-transform-numeric-separator@7.23.4 AND INFORMATION -%% @babel/plugin-transform-optional-chaining@7.23.0 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-optional-chaining@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1869,9 +1869,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-optional-chaining@7.23.0 AND INFORMATION +END OF @babel/plugin-transform-optional-chaining@7.23.4 AND INFORMATION -%% @babel/plugin-transform-private-methods@7.22.5 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-private-methods@7.23.3 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1896,9 +1896,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-private-methods@7.22.5 AND INFORMATION +END OF @babel/plugin-transform-private-methods@7.23.3 AND INFORMATION -%% @babel/plugin-transform-private-property-in-object@7.22.11 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-private-property-in-object@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1923,9 +1923,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-private-property-in-object@7.22.11 AND INFORMATION +END OF @babel/plugin-transform-private-property-in-object@7.23.4 AND INFORMATION -%% @babel/plugin-transform-react-jsx@7.22.15 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-react-jsx@7.23.4 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1950,9 +1950,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-react-jsx@7.22.15 AND INFORMATION +END OF @babel/plugin-transform-react-jsx@7.23.4 AND INFORMATION -%% @babel/plugin-transform-typescript@7.22.15 NOTICES AND INFORMATION BEGIN HERE +%% @babel/plugin-transform-typescript@7.23.6 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -1977,9 +1977,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/plugin-transform-typescript@7.22.15 AND INFORMATION +END OF @babel/plugin-transform-typescript@7.23.6 AND INFORMATION -%% @babel/preset-typescript@7.23.2 NOTICES AND INFORMATION BEGIN HERE +%% @babel/preset-typescript@7.23.3 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -2004,7 +2004,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/preset-typescript@7.23.2 AND INFORMATION +END OF @babel/preset-typescript@7.23.3 AND INFORMATION %% @babel/template@7.22.15 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -2033,7 +2033,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF @babel/template@7.22.15 AND INFORMATION -%% @babel/traverse@7.23.2 NOTICES AND INFORMATION BEGIN HERE +%% @babel/traverse@7.23.7 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -2058,9 +2058,9 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/traverse@7.23.2 AND INFORMATION +END OF @babel/traverse@7.23.7 AND INFORMATION -%% @babel/types@7.23.0 NOTICES AND INFORMATION BEGIN HERE +%% @babel/types@7.23.6 NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -2085,7 +2085,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF @babel/types@7.23.0 AND INFORMATION +END OF @babel/types@7.23.6 AND INFORMATION %% @jest/expect-utils@29.5.0 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -2625,7 +2625,7 @@ THE SOFTWARE. ========================================= END OF braces@3.0.2 AND INFORMATION -%% browserslist@4.22.1 NOTICES AND INFORMATION BEGIN HERE +%% browserslist@4.22.2 NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) @@ -2648,7 +2648,7 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF browserslist@4.22.1 AND INFORMATION +END OF browserslist@4.22.2 AND INFORMATION %% buffer-from@1.1.2 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -2676,7 +2676,7 @@ SOFTWARE. ========================================= END OF buffer-from@1.1.2 AND INFORMATION -%% caniuse-lite@1.0.30001541 NOTICES AND INFORMATION BEGIN HERE +%% caniuse-lite@1.0.30001579 NOTICES AND INFORMATION BEGIN HERE ========================================= Attribution 4.0 International @@ -3074,7 +3074,7 @@ public licenses. Creative Commons may be contacted at creativecommons.org. ========================================= -END OF caniuse-lite@1.0.30001541 AND INFORMATION +END OF caniuse-lite@1.0.30001579 AND INFORMATION %% chalk@2.4.2 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -3336,7 +3336,7 @@ SOFTWARE. ========================================= END OF diff-sequences@29.4.3 AND INFORMATION -%% electron-to-chromium@1.4.535 NOTICES AND INFORMATION BEGIN HERE +%% electron-to-chromium@1.4.638 NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright 2018 Kilian Valkhof @@ -3344,7 +3344,7 @@ Permission to use, copy, modify, and/or distribute this software for any purpose THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ========================================= -END OF electron-to-chromium@1.4.535 AND INFORMATION +END OF electron-to-chromium@1.4.638 AND INFORMATION %% enquirer@2.3.6 NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -3945,7 +3945,7 @@ SOFTWARE. ========================================= END OF ms@2.1.2 AND INFORMATION -%% node-releases@2.0.13 NOTICES AND INFORMATION BEGIN HERE +%% node-releases@2.0.14 NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License @@ -3969,7 +3969,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF node-releases@2.0.13 AND INFORMATION +END OF node-releases@2.0.14 AND INFORMATION %% normalize-path@3.0.0 NOTICES AND INFORMATION BEGIN HERE ========================================= diff --git a/playwright/packages/playwright/bundles/babel/package-lock.json b/playwright/packages/playwright/bundles/babel/package-lock.json index a55f8eff0b..34199679bb 100644 --- a/playwright/packages/playwright/bundles/babel/package-lock.json +++ b/playwright/packages/playwright/bundles/babel/package-lock.json @@ -8,10 +8,10 @@ "name": "babel-bundle", "version": "0.0.1", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/core": "^7.23.2", + "@babel/code-frame": "^7.23.5", + "@babel/core": "^7.23.7", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/parser": "^7.23.0", + "@babel/parser": "^7.23.6", "@babel/plugin-proposal-decorators": "^7.23.2", "@babel/plugin-proposal-explicit-resource-management": "^7.23.0", "@babel/plugin-syntax-async-generators": "^7.8.4", @@ -19,19 +19,19 @@ "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.11", - "@babel/plugin-transform-dynamic-import": "^7.22.11", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", - "@babel/plugin-transform-numeric-separator": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.23.0", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/preset-typescript": "^7.23.2" + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-react-jsx": "^7.23.4", + "@babel/preset-typescript": "^7.23.3" }, "devDependencies": { "@types/babel__code-frame": "^7.0.4", @@ -53,11 +53,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -65,28 +65,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", - "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -102,11 +102,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -127,13 +127,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -142,16 +142,16 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz", + "integrity": "sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -217,9 +217,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -303,9 +303,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "engines": { "node": ">=6.9.0" } @@ -319,30 +319,30 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -353,9 +353,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -497,9 +497,9 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -591,9 +591,9 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -605,11 +605,11 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -620,11 +620,11 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", - "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -636,9 +636,9 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", - "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -651,9 +651,9 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", - "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -666,9 +666,9 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", - "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -681,11 +681,11 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", - "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", "dependencies": { - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, @@ -697,9 +697,9 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", - "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -712,9 +712,9 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", - "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -727,9 +727,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", - "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -743,11 +743,11 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -758,12 +758,12 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, @@ -775,15 +775,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz", - "integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.15" + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" }, "engines": { "node": ">=6.9.0" @@ -793,14 +793,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", - "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", + "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-create-class-features-plugin": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" + "@babel/plugin-syntax-typescript": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -810,15 +810,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", - "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-typescript": "^7.22.15" + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -841,19 +841,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -861,11 +861,11 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -984,9 +984,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "funding": [ { "type": "opencollective", @@ -1002,9 +1002,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" }, "bin": { @@ -1015,9 +1015,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001541", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz", - "integrity": "sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "funding": [ { "type": "opencollective", @@ -1081,9 +1081,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.535", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.535.tgz", - "integrity": "sha512-4548PpR4S5X5dlvX8NUIw0njH7btQtBoJWcgzpq7n2F9NQ5gMXOPP/6p6iVx6+YT3FVioNhEGa14WJj1k+2SfA==" + "version": "1.4.638", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.638.tgz", + "integrity": "sha512-gpmbAG2LbfPKcDaL5m9IKutKjUx4ZRkvGNkgL/8nKqxkXsBVYykVULboWlqCrHsh3razucgDJDuKoWJmGPdItA==" }, "node_modules/escalade": { "version": "3.1.1", @@ -1166,9 +1166,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/picocolors": { "version": "1.0.0", @@ -1248,34 +1248,34 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "requires": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" } }, "@babel/compat-data": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", - "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==" + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==" }, "@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1284,11 +1284,11 @@ } }, "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "requires": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -1303,28 +1303,28 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz", + "integrity": "sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -1369,9 +1369,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "requires": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -1428,9 +1428,9 @@ } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==" }, "@babel/helper-validator-identifier": { "version": "7.22.20", @@ -1438,24 +1438,24 @@ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==" + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==" }, "@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", "requires": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" } }, "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "requires": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -1463,9 +1463,9 @@ } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==" }, "@babel/plugin-proposal-decorators": { "version": "7.23.2", @@ -1553,9 +1553,9 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -1617,91 +1617,91 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "requires": { "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/plugin-transform-class-static-block": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", - "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-transform-dynamic-import": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", - "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, "@babel/plugin-transform-export-namespace-from": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", - "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", - "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", - "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", "requires": { - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" } }, "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", - "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, "@babel/plugin-transform-numeric-separator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", - "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", - "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -1709,58 +1709,58 @@ } }, "@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz", - "integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.15" + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" } }, "@babel/plugin-transform-typescript": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", - "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", + "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-create-class-features-plugin": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" + "@babel/plugin-syntax-typescript": "^7.23.3" } }, "@babel/preset-typescript": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz", - "integrity": "sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-typescript": "^7.22.15" + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" } }, "@babel/template": { @@ -1774,28 +1774,28 @@ } }, "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "requires": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } @@ -1899,20 +1899,20 @@ } }, "browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "requires": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" } }, "caniuse-lite": { - "version": "1.0.30001541", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz", - "integrity": "sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==" + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==" }, "chalk": { "version": "2.4.2", @@ -1951,9 +1951,9 @@ } }, "electron-to-chromium": { - "version": "1.4.535", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.535.tgz", - "integrity": "sha512-4548PpR4S5X5dlvX8NUIw0njH7btQtBoJWcgzpq7n2F9NQ5gMXOPP/6p6iVx6+YT3FVioNhEGa14WJj1k+2SfA==" + "version": "1.4.638", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.638.tgz", + "integrity": "sha512-gpmbAG2LbfPKcDaL5m9IKutKjUx4ZRkvGNkgL/8nKqxkXsBVYykVULboWlqCrHsh3razucgDJDuKoWJmGPdItA==" }, "escalade": { "version": "3.1.1", @@ -2009,9 +2009,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "picocolors": { "version": "1.0.0", diff --git a/playwright/packages/playwright/bundles/babel/package.json b/playwright/packages/playwright/bundles/babel/package.json index c4c2cdc853..01d61fe5a2 100644 --- a/playwright/packages/playwright/bundles/babel/package.json +++ b/playwright/packages/playwright/bundles/babel/package.json @@ -9,10 +9,10 @@ "generate-license": "node ../../../../utils/generate_third_party_notice.js" }, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/core": "^7.23.2", + "@babel/code-frame": "^7.23.5", + "@babel/core": "^7.23.7", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/parser": "^7.23.0", + "@babel/parser": "^7.23.6", "@babel/plugin-proposal-decorators": "^7.23.2", "@babel/plugin-proposal-explicit-resource-management": "^7.23.0", "@babel/plugin-syntax-async-generators": "^7.8.4", @@ -20,19 +20,19 @@ "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.11", - "@babel/plugin-transform-dynamic-import": "^7.22.11", - "@babel/plugin-transform-export-namespace-from": "^7.22.11", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", - "@babel/plugin-transform-numeric-separator": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.23.0", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-react-jsx": "^7.22.15", - "@babel/preset-typescript": "^7.23.2" + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-react-jsx": "^7.23.4", + "@babel/preset-typescript": "^7.23.3" }, "devDependencies": { "@types/babel__code-frame": "^7.0.4", diff --git a/playwright/packages/playwright/cli.js b/playwright/packages/playwright/cli.js index d4eb087af8..5db59b62cd 100755 --- a/playwright/packages/playwright/cli.js +++ b/playwright/packages/playwright/cli.js @@ -14,4 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module.exports = require('./lib/cli'); + +const { program } = require('./lib/program'); +program.parse(process.argv); diff --git a/playwright/packages/playwright/package.json b/playwright/packages/playwright/package.json index 22dd5a649d..d675f024a0 100644 --- a/playwright/packages/playwright/package.json +++ b/playwright/packages/playwright/package.json @@ -1,6 +1,6 @@ { "name": "playwright", - "version": "1.41.1", + "version": "1.42.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -18,11 +18,14 @@ "require": "./index.js", "default": "./index.js" }, - "./cli": "./cli.js", "./package.json": "./package.json", - "./lib/cli": "./lib/cli.js", + "./lib/common/configLoader": "./lib/common/configLoader.js", + "./lib/fsWatcher": "./lib/fsWatcher.js", + "./lib/program": "./lib/program.js", "./lib/transform/babelBundle": "./lib/transform/babelBundle.js", "./lib/transform/compilationCache": "./lib/transform/compilationCache.js", + "./lib/runner/runner": "./lib/runner/runner.js", + "./lib/runner/testServer": "./lib/runner/testServer.js", "./lib/transform/esmLoader": "./lib/transform/esmLoader.js", "./lib/transform/transform": "./lib/transform/transform.js", "./lib/internalsForTest": "./lib/internalsForTest.js", @@ -55,7 +58,7 @@ }, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.42.0" }, "optionalDependencies": { "fsevents": "2.3.2" diff --git a/playwright/packages/playwright/src/DEPS.list b/playwright/packages/playwright/src/DEPS.list index c7a905a82b..200d75de1a 100644 --- a/playwright/packages/playwright/src/DEPS.list +++ b/playwright/packages/playwright/src/DEPS.list @@ -3,7 +3,7 @@ common/ ./utils.ts ./utilsBundle.ts -[cli.ts] +[program.ts] ** [index.ts] diff --git a/playwright/packages/playwright/src/common/config.ts b/playwright/packages/playwright/src/common/config.ts index b3a124e47c..d1c86fe159 100644 --- a/playwright/packages/playwright/src/common/config.ts +++ b/playwright/packages/playwright/src/common/config.ts @@ -26,6 +26,11 @@ import type { ConfigCLIOverrides } from './ipc'; import type { FullConfig, FullProject } from '../../types/test'; import { setTransformConfig } from '../transform/transform'; +export type ConfigLocation = { + resolvedConfigFile?: string; + configDir: string; +}; + export type FixturesWithLocation = { fixtures: Fixtures; location: Location; @@ -39,8 +44,8 @@ export class FullConfigInternal { readonly globalOutputDir: string; readonly configDir: string; readonly configCLIOverrides: ConfigCLIOverrides; - readonly storeDir: string; readonly ignoreSnapshots: boolean; + readonly preserveOutputDir: boolean; readonly webServers: Exclude<FullConfig['webServer'], null>[]; readonly plugins: TestRunnerPluginRegistration[]; readonly projects: FullProjectInternal[] = []; @@ -58,47 +63,54 @@ export class FullConfigInternal { return (config as any)[configInternalSymbol]; } - constructor(configDir: string, configFile: string | undefined, config: Config, configCLIOverrides: ConfigCLIOverrides) { - if (configCLIOverrides.projects && config.projects) + constructor(location: ConfigLocation, userConfig: Config, configCLIOverrides: ConfigCLIOverrides) { + if (configCLIOverrides.projects && userConfig.projects) throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`); + const { resolvedConfigFile, configDir } = location; const packageJsonPath = getPackageJsonPath(configDir); const packageJsonDir = packageJsonPath ? path.dirname(packageJsonPath) : undefined; const throwawayArtifactsPath = packageJsonDir || process.cwd(); this.configDir = configDir; this.configCLIOverrides = configCLIOverrides; - this.storeDir = path.resolve(configDir, (config as any)._storeDir || 'playwright'); - this.globalOutputDir = takeFirst(configCLIOverrides.outputDir, pathResolve(configDir, config.outputDir), throwawayArtifactsPath, path.resolve(process.cwd())); - this.ignoreSnapshots = takeFirst(configCLIOverrides.ignoreSnapshots, config.ignoreSnapshots, false); - this.plugins = ((config as any)._plugins || []).map((p: any) => ({ factory: p })); + this.globalOutputDir = takeFirst(configCLIOverrides.outputDir, pathResolve(configDir, userConfig.outputDir), throwawayArtifactsPath, path.resolve(process.cwd())); + this.preserveOutputDir = configCLIOverrides.preserveOutputDir || false; + this.ignoreSnapshots = takeFirst(configCLIOverrides.ignoreSnapshots, userConfig.ignoreSnapshots, false); + const privateConfiguration = (userConfig as any)['@playwright/test']; + this.plugins = (privateConfiguration?.plugins || []).map((p: any) => ({ factory: p })); this.config = { - configFile, - rootDir: pathResolve(configDir, config.testDir) || configDir, - forbidOnly: takeFirst(configCLIOverrides.forbidOnly, config.forbidOnly, false), - fullyParallel: takeFirst(configCLIOverrides.fullyParallel, config.fullyParallel, false), - globalSetup: takeFirst(resolveScript(config.globalSetup, configDir), null), - globalTeardown: takeFirst(resolveScript(config.globalTeardown, configDir), null), - globalTimeout: takeFirst(configCLIOverrides.globalTimeout, config.globalTimeout, 0), - grep: takeFirst(config.grep, defaultGrep), - grepInvert: takeFirst(config.grepInvert, null), - maxFailures: takeFirst(configCLIOverrides.maxFailures, config.maxFailures, 0), - metadata: takeFirst(config.metadata, {}), - preserveOutput: takeFirst(config.preserveOutput, 'always'), - reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(config.reporter, configDir), [[defaultReporter]]), - reportSlowTests: takeFirst(config.reportSlowTests, { max: 5, threshold: 15000 }), - quiet: takeFirst(configCLIOverrides.quiet, config.quiet, false), + configFile: resolvedConfigFile, + rootDir: pathResolve(configDir, userConfig.testDir) || configDir, + forbidOnly: takeFirst(configCLIOverrides.forbidOnly, userConfig.forbidOnly, false), + fullyParallel: takeFirst(configCLIOverrides.fullyParallel, userConfig.fullyParallel, false), + globalSetup: takeFirst(resolveScript(userConfig.globalSetup, configDir), null), + globalTeardown: takeFirst(resolveScript(userConfig.globalTeardown, configDir), null), + globalTimeout: takeFirst(configCLIOverrides.globalTimeout, userConfig.globalTimeout, 0), + grep: takeFirst(userConfig.grep, defaultGrep), + grepInvert: takeFirst(userConfig.grepInvert, null), + maxFailures: takeFirst(configCLIOverrides.maxFailures, userConfig.maxFailures, 0), + metadata: takeFirst(userConfig.metadata, {}), + preserveOutput: takeFirst(userConfig.preserveOutput, 'always'), + reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]), + reportSlowTests: takeFirst(userConfig.reportSlowTests, { max: 5, threshold: 15000 }), + quiet: takeFirst(configCLIOverrides.quiet, userConfig.quiet, false), projects: [], - shard: takeFirst(configCLIOverrides.shard, config.shard, null), - updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, config.updateSnapshots, 'missing'), + shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null), + updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, 'missing'), version: require('../../package.json').version, workers: 0, webServer: null, }; + for (const key in userConfig) { + if (key.startsWith('@')) + (this.config as any)[key] = (userConfig as any)[key]; + } + (this.config as any)[configInternalSymbol] = this; - const workers = takeFirst(configCLIOverrides.workers, config.workers, '50%'); + const workers = takeFirst(configCLIOverrides.workers, userConfig.workers, '50%'); if (typeof workers === 'string') { if (workers.endsWith('%')) { const cpus = os.cpus().length; @@ -110,7 +122,7 @@ export class FullConfigInternal { this.config.workers = workers; } - const webServers = takeFirst(config.webServer, null); + const webServers = takeFirst(userConfig.webServer, null); if (Array.isArray(webServers)) { // multiple web server mode // Due to previous choices, this value shows up to the user in globalSetup as part of FullConfig. Arrays are not supported by the old type. this.config.webServer = null; @@ -122,13 +134,13 @@ export class FullConfigInternal { this.webServers = []; } - const projectConfigs = configCLIOverrides.projects || config.projects || [config]; - this.projects = projectConfigs.map(p => new FullProjectInternal(configDir, config, this, p, this.configCLIOverrides, throwawayArtifactsPath)); + const projectConfigs = configCLIOverrides.projects || userConfig.projects || [userConfig]; + this.projects = projectConfigs.map(p => new FullProjectInternal(configDir, userConfig, this, p, this.configCLIOverrides, throwawayArtifactsPath)); resolveProjectDependencies(this.projects); this._assignUniqueProjectIds(this.projects); setTransformConfig({ - babelPlugins: (config as any).build?.babelPlugins || [], - external: config.build?.external || [], + babelPlugins: privateConfiguration?.babelPlugins || [], + external: userConfig.build?.external || [], }); this.config.projects = this.projects.map(p => p.project); } diff --git a/playwright/packages/playwright/src/common/configLoader.ts b/playwright/packages/playwright/src/common/configLoader.ts index 7fbbed257a..59ba309b2e 100644 --- a/playwright/packages/playwright/src/common/configLoader.ts +++ b/playwright/packages/playwright/src/common/configLoader.ts @@ -16,15 +16,16 @@ import * as fs from 'fs'; import * as path from 'path'; -import { isRegExp } from 'playwright-core/lib/utils'; +import { gracefullyProcessExitDoNotHang, isRegExp } from 'playwright-core/lib/utils'; import type { ConfigCLIOverrides, SerializedConfig } from './ipc'; import { requireOrImport } from '../transform/transform'; import type { Config, Project } from '../../types/test'; -import { errorWithFile } from '../util'; -import { setCurrentConfig } from './globals'; +import { errorWithFile, fileIsModule } from '../util'; +import type { ConfigLocation } from './config'; import { FullConfigInternal } from './config'; import { addToCompilationCache } from '../transform/compilationCache'; -import { initializeEsmLoader } from './esmLoaderHost'; +import { initializeEsmLoader, registerESMLoader } from './esmLoaderHost'; +import { execArgvWithExperimentalLoaderOptions, execArgvWithoutExperimentalLoaderOptions } from '../transform/esmUtils'; const kDefineConfigWasUsed = Symbol('defineConfigWasUsed'); export const defineConfig = (...configs: any[]) => { @@ -83,59 +84,34 @@ export const defineConfig = (...configs: any[]) => { return result; }; -export class ConfigLoader { - private _configCLIOverrides: ConfigCLIOverrides; - private _fullConfig: FullConfigInternal | undefined; - - constructor(configCLIOverrides?: ConfigCLIOverrides) { - this._configCLIOverrides = configCLIOverrides || {}; - } - - static async deserialize(data: SerializedConfig): Promise<FullConfigInternal> { +export async function deserializeConfig(data: SerializedConfig): Promise<FullConfigInternal> { + if (data.compilationCache) addToCompilationCache(data.compilationCache); - const loader = new ConfigLoader(data.configCLIOverrides); - const config = data.configFile ? await loader.loadConfigFile(data.configFile) : await loader.loadEmptyConfig(data.configDir); - await initializeEsmLoader(); - return config; - } - - async loadConfigFile(file: string, ignoreProjectDependencies = false): Promise<FullConfigInternal> { - if (this._fullConfig) - throw new Error('Cannot load two config files'); - const config = await requireOrImportDefaultObject(file) as Config; - const fullConfig = await this._loadConfig(config, path.dirname(file), file); - setCurrentConfig(fullConfig); - if (ignoreProjectDependencies) { - for (const project of fullConfig.projects) { - project.deps = []; - project.teardown = undefined; - } - } - this._fullConfig = fullConfig; - return fullConfig; - } - - async loadEmptyConfig(configDir: string): Promise<FullConfigInternal> { - const fullConfig = await this._loadConfig({}, configDir); - setCurrentConfig(fullConfig); - return fullConfig; - } - - private async _loadConfig(config: Config, configDir: string, configFile?: string): Promise<FullConfigInternal> { - // 1. Validate data provided in the config file. - validateConfig(configFile || '<default config>', config); - const fullConfig = new FullConfigInternal(configDir, configFile, config, this._configCLIOverrides); - fullConfig.defineConfigWasUsed = !!(config as any)[kDefineConfigWasUsed]; - return fullConfig; - } + const config = await loadConfig(data.location, data.configCLIOverrides); + await initializeEsmLoader(); + return config; } -async function requireOrImportDefaultObject(file: string) { - let object = await requireOrImport(file); +async function loadUserConfig(location: ConfigLocation): Promise<Config> { + let object = location.resolvedConfigFile ? await requireOrImport(location.resolvedConfigFile) : {}; if (object && typeof object === 'object' && ('default' in object)) object = object['default']; - return object; + return object as Config; +} + +export async function loadConfig(location: ConfigLocation, overrides?: ConfigCLIOverrides, ignoreProjectDependencies = false): Promise<FullConfigInternal> { + const userConfig = await loadUserConfig(location); + validateConfig(location.resolvedConfigFile || '<default config>', userConfig); + const fullConfig = new FullConfigInternal(location, userConfig, overrides || {}); + fullConfig.defineConfigWasUsed = !!(userConfig as any)[kDefineConfigWasUsed]; + if (ignoreProjectDependencies) { + for (const project of fullConfig.projects) { + project.deps = []; + project.teardown = undefined; + } + } + return fullConfig; } function validateConfig(file: string, config: Config) { @@ -310,7 +286,7 @@ function validateProject(file: string, project: Project, title: string) { } } -export function resolveConfigFile(configFileOrDirectory: string): string | null { +export function resolveConfigFile(configFileOrDirectory: string): string | undefined { const resolveConfig = (configFile: string) => { if (fs.existsSync(configFile)) return configFile; @@ -332,10 +308,66 @@ export function resolveConfigFile(configFileOrDirectory: string): string | null if (configFile) return configFile; // If there is no config, assume this as a root testing directory. - return null; + return undefined; } else { // When passed a file, it must be a config file. const configFile = resolveConfig(configFileOrDirectory); return configFile!; } } + +export async function loadConfigFromFileRestartIfNeeded(configFile: string | undefined, overrides?: ConfigCLIOverrides, ignoreDeps?: boolean): Promise<FullConfigInternal | null> { + const configFileOrDirectory = configFile ? path.resolve(process.cwd(), configFile) : process.cwd(); + const resolvedConfigFile = resolveConfigFile(configFileOrDirectory); + if (restartWithExperimentalTsEsm(resolvedConfigFile)) + return null; + const location: ConfigLocation = { + configDir: resolvedConfigFile ? path.dirname(resolvedConfigFile) : configFileOrDirectory, + resolvedConfigFile, + }; + return await loadConfig(location, overrides, ignoreDeps); +} + +export async function loadEmptyConfigForMergeReports() { + // Merge reports is "different" for no good reason. It should not pick up local config from the cwd. + return await loadConfig({ configDir: process.cwd() }); +} + +export function restartWithExperimentalTsEsm(configFile: string | undefined, force: boolean = false): boolean { + const nodeVersion = +process.versions.node.split('.')[0]; + // New experimental loader is only supported on Node 16+. + if (nodeVersion < 16) + return false; + if (!configFile && !force) + return false; + if (process.env.PW_DISABLE_TS_ESM) + return false; + // Node.js < 20 + if ((globalThis as any).__esmLoaderPortPreV20) { + // clear execArgv after restart, so that childProcess.fork in user code does not inherit our loader. + process.execArgv = execArgvWithoutExperimentalLoaderOptions(); + return false; + } + if (!force && !fileIsModule(configFile!)) + return false; + + // Node.js < 20 + if (!require('node:module').register) { + const innerProcess = (require('child_process') as typeof import('child_process')).fork(require.resolve('../../cli'), process.argv.slice(2), { + env: { + ...process.env, + PW_TS_ESM_LEGACY_LOADER_ON: '1', + }, + execArgv: execArgvWithExperimentalLoaderOptions(), + }); + + innerProcess.on('close', (code: number | null) => { + if (code !== 0 && code !== null) + gracefullyProcessExitDoNotHang(code); + }); + return true; + } + // Nodejs >= 21 + registerESMLoader(); + return false; +} diff --git a/playwright/packages/playwright/src/common/esmLoaderHost.ts b/playwright/packages/playwright/src/common/esmLoaderHost.ts index 24f55784ea..f165318c08 100644 --- a/playwright/packages/playwright/src/common/esmLoaderHost.ts +++ b/playwright/packages/playwright/src/common/esmLoaderHost.ts @@ -60,6 +60,9 @@ export async function stopCollectingFileDeps(file: string) { export async function incorporateCompilationCache() { if (!loaderChannel) return; + // This is needed to gather dependency information from the esm loader + // that is populated from the resovle hook. We do not need to push + // this information proactively during load, but gather it at the end. const result = await loaderChannel.send('getCompilationCache', {}); addToCompilationCache(result.cache); } diff --git a/playwright/packages/playwright/src/common/fixtures.ts b/playwright/packages/playwright/src/common/fixtures.ts index e03da64c20..8d2c7c2304 100644 --- a/playwright/packages/playwright/src/common/fixtures.ts +++ b/playwright/packages/playwright/src/common/fixtures.ts @@ -72,11 +72,11 @@ function isFixtureOption(value: any): value is FixtureTuple { export class FixturePool { readonly digest: string; - readonly registrations: Map<string, FixtureRegistration>; + private readonly _registrations: Map<string, FixtureRegistration>; private _onLoadError: LoadErrorSink; constructor(fixturesList: FixturesWithLocation[], onLoadError: LoadErrorSink, parentPool?: FixturePool, disallowWorkerFixtures?: boolean, optionOverrides?: OptionOverrides) { - this.registrations = new Map(parentPool ? parentPool.registrations : []); + this._registrations = new Map(parentPool ? parentPool._registrations : []); this._onLoadError = onLoadError; const allOverrides = optionOverrides?.overrides ?? {}; @@ -117,7 +117,7 @@ export class FixturePool { } let fn = value as (Function | any); - const previous = this.registrations.get(name); + const previous = this._registrations.get(name); if (previous && options) { if (previous.scope !== options.scope) { this._addLoadError(`Fixture "${name}" has already been registered as a { scope: '${previous.scope}' } fixture defined in ${formatLocation(previous.location)}.`, location); @@ -154,7 +154,7 @@ export class FixturePool { const deps = fixtureParameterNames(fn, location, e => this._onLoadError(e)); const registration: FixtureRegistration = { id: '', name, location, scope: options.scope, fn, auto: options.auto, option: options.option, timeout: options.timeout, customTitle: options.customTitle, hideStep: options.hideStep, deps, super: previous, optionOverride: isOptionsOverride }; registrationId(registration); - this.registrations.set(name, registration); + this._registrations.set(name, registration); } } @@ -165,7 +165,7 @@ export class FixturePool { markers.set(registration, 'visiting'); stack.push(registration); for (const name of registration.deps) { - const dep = this.resolveDependency(registration, name); + const dep = this.resolve(name, registration); if (!dep) { if (name === registration.name) this._addLoadError(`Fixture "${registration.name}" references itself, but does not have a base implementation.`, registration.location); @@ -192,9 +192,9 @@ export class FixturePool { }; const hash = crypto.createHash('sha1'); - const names = Array.from(this.registrations.keys()).sort(); + const names = Array.from(this._registrations.keys()).sort(); for (const name of names) { - const registration = this.registrations.get(name)!; + const registration = this._registrations.get(name)!; visit(registration); if (registration.scope === 'worker') hash.update(registration.id + ';'); @@ -204,16 +204,20 @@ export class FixturePool { validateFunction(fn: Function, prefix: string, location: Location) { for (const name of fixtureParameterNames(fn, location, e => this._onLoadError(e))) { - const registration = this.registrations.get(name); + const registration = this._registrations.get(name); if (!registration) this._addLoadError(`${prefix} has unknown parameter "${name}".`, location); } } - resolveDependency(registration: FixtureRegistration, name: string): FixtureRegistration | undefined { - if (name === registration.name) - return registration.super; - return this.registrations.get(name); + resolve(name: string, forFixture?: FixtureRegistration): FixtureRegistration | undefined { + if (name === forFixture?.name) + return forFixture.super; + return this._registrations.get(name); + } + + autoFixtures() { + return [...this._registrations.values()].filter(r => r.auto !== false); } private _addLoadError(message: string, location: Location) { @@ -231,6 +235,10 @@ export function fixtureParameterNames(fn: Function | any, location: Location, on return fn[signatureSymbol]; } +export function inheritFixutreNames(from: Function, to: Function) { + (to as any)[signatureSymbol] = (from as any)[signatureSymbol]; +} + function innerFixtureParameterNames(fn: Function, location: Location, onError: LoadErrorSink): string[] { const text = filterOutComments(fn.toString()); const match = text.match(/(?:async)?(?:\s+function)?[^(]*\(([^)]*)/); diff --git a/playwright/packages/playwright/src/common/globals.ts b/playwright/packages/playwright/src/common/globals.ts index 9443db25d3..c2ead505e6 100644 --- a/playwright/packages/playwright/src/common/globals.ts +++ b/playwright/packages/playwright/src/common/globals.ts @@ -16,7 +16,6 @@ import type { TestInfoImpl } from '../worker/testInfo'; import type { Suite } from './test'; -import type { FullConfigInternal } from './config'; let currentTestInfoValue: TestInfoImpl | null = null; export function setCurrentTestInfo(testInfo: TestInfoImpl | null) { @@ -61,11 +60,3 @@ export function setIsWorkerProcess() { export function isWorkerProcess() { return _isWorkerProcess; } - -let currentConfigValue: FullConfigInternal | null = null; -export function setCurrentConfig(config: FullConfigInternal | null) { - currentConfigValue = config; -} -export function currentConfig(): FullConfigInternal | null { - return currentConfigValue; -} diff --git a/playwright/packages/playwright/src/common/ipc.ts b/playwright/packages/playwright/src/common/ipc.ts index 17b1056a78..bb35f6c06d 100644 --- a/playwright/packages/playwright/src/common/ipc.ts +++ b/playwright/packages/playwright/src/common/ipc.ts @@ -16,7 +16,7 @@ import util from 'util'; import { serializeCompilationCache } from '../transform/compilationCache'; -import type { FullConfigInternal } from './config'; +import type { ConfigLocation, FullConfigInternal } from './config'; import type { ReporterDescription, TestInfoError, TestStatus } from '../../types/test'; export type ConfigCLIOverrides = { @@ -25,10 +25,12 @@ export type ConfigCLIOverrides = { globalTimeout?: number; maxFailures?: number; outputDir?: string; + preserveOutputDir?: boolean; quiet?: boolean; repeatEach?: number; retries?: number; reporter?: ReporterDescription[]; + additionalReporters?: ReporterDescription[]; shard?: { current: number, total: number }; timeout?: number; ignoreSnapshots?: boolean; @@ -39,8 +41,7 @@ export type ConfigCLIOverrides = { }; export type SerializedConfig = { - configFile: string | undefined; - configDir: string; + location: ConfigLocation; configCLIOverrides: ConfigCLIOverrides; compilationCache: any; }; @@ -84,6 +85,7 @@ export type TestEndPayload = { duration: number; status: TestStatus; errors: TestInfoError[]; + hasNonRetriableError: boolean; expectedStatus: TestStatus; annotations: { type: string, description?: string }[]; timeout: number; @@ -133,12 +135,11 @@ export type TeardownErrorsPayload = { export type EnvProducedPayload = [string, string | null][]; -export function serializeConfig(config: FullConfigInternal): SerializedConfig { +export function serializeConfig(config: FullConfigInternal, passCompilationCache: boolean): SerializedConfig { const result: SerializedConfig = { - configFile: config.config.configFile, - configDir: config.configDir, + location: { configDir: config.configDir, resolvedConfigFile: config.config.configFile }, configCLIOverrides: config.configCLIOverrides, - compilationCache: serializeCompilationCache(), + compilationCache: passCompilationCache ? serializeCompilationCache() : undefined, }; return result; } diff --git a/playwright/packages/playwright/src/common/suiteUtils.ts b/playwright/packages/playwright/src/common/suiteUtils.ts index 7994c10e04..c2140a8a96 100644 --- a/playwright/packages/playwright/src/common/suiteUtils.ts +++ b/playwright/packages/playwright/src/common/suiteUtils.ts @@ -15,7 +15,7 @@ */ import path from 'path'; -import { calculateSha1 } from 'playwright-core/lib/utils'; +import { calculateSha1, toPosixPath } from 'playwright-core/lib/utils'; import type { Suite, TestCase } from './test'; import type { FullProjectInternal } from './config'; import type { Matcher, TestFileFilter } from '../util'; @@ -39,9 +39,10 @@ export function filterTestsRemoveEmptySuites(suite: Suite, filter: (test: TestCa suite._entries = suite._entries.filter(e => entries.has(e)); // Preserve the order. return !!suite._entries.length; } + export function bindFileSuiteToProject(project: FullProjectInternal, suite: Suite): Suite { - const relativeFile = path.relative(project.project.testDir, suite.location!.file).split(path.sep).join('/'); - const fileId = calculateSha1(relativeFile).slice(0, 20); + const relativeFile = path.relative(project.project.testDir, suite.location!.file); + const fileId = calculateSha1(toPosixPath(relativeFile)).slice(0, 20); // Clone suite. const result = suite._deepClone(); @@ -51,7 +52,8 @@ export function bindFileSuiteToProject(project: FullProjectInternal, suite: Suit result.forEachTest((test, suite) => { suite._fileId = fileId; // At the point of the query, suite is not yet attached to the project, so we only get file, describe and test titles. - const testIdExpression = `[project=${project.id}]${test.titlePath().join('\x1e')}`; + const [file, ...titles] = test.titlePath(); + const testIdExpression = `[project=${project.id}]${toPosixPath(file)}\x1e${titles.join('\x1e')}`; const testId = fileId + '-' + calculateSha1(testIdExpression).slice(0, 20); test.id = testId; test._projectId = project.id; @@ -59,8 +61,10 @@ export function bindFileSuiteToProject(project: FullProjectInternal, suite: Suit // Inherit properties from parent suites. let inheritedRetries: number | undefined; let inheritedTimeout: number | undefined; + test.annotations = []; for (let parentSuite: Suite | undefined = suite; parentSuite; parentSuite = parentSuite.parent) { - test._staticAnnotations.push(...parentSuite._staticAnnotations); + if (parentSuite._staticAnnotations.length) + test.annotations = [...parentSuite._staticAnnotations, ...test.annotations]; if (inheritedRetries === undefined && parentSuite._retries !== undefined) inheritedRetries = parentSuite._retries; if (inheritedTimeout === undefined && parentSuite._timeout !== undefined) @@ -68,10 +72,10 @@ export function bindFileSuiteToProject(project: FullProjectInternal, suite: Suit } test.retries = inheritedRetries ?? project.project.retries; test.timeout = inheritedTimeout ?? project.project.timeout; - test.annotations = [...test._staticAnnotations]; + test.annotations.push(...test._staticAnnotations); // Skip annotations imply skipped expectedStatus. - if (test._staticAnnotations.some(a => a.type === 'skip' || a.type === 'fixme')) + if (test.annotations.some(a => a.type === 'skip' || a.type === 'fixme')) test.expectedStatus = 'skipped'; // We only compute / set digest in the runner. diff --git a/playwright/packages/playwright/src/common/test.ts b/playwright/packages/playwright/src/common/test.ts index a1afee5b08..ec67168826 100644 --- a/playwright/packages/playwright/src/common/test.ts +++ b/playwright/packages/playwright/src/common/test.ts @@ -48,16 +48,21 @@ export class Suite extends Base implements SuitePrivate { _hooks: { type: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', fn: Function, title: string, location: Location }[] = []; _timeout: number | undefined; _retries: number | undefined; + // Annotations known statically before running the test, e.g. `test.describe.skip()` or `test.describe({ annotation }, body)`. _staticAnnotations: Annotation[] = []; + // Explicitly declared tags that are not a part of the title. + _tags: string[] = []; _modifiers: Modifier[] = []; _parallelMode: 'none' | 'default' | 'serial' | 'parallel' = 'none'; _fullProject: FullProjectInternal | undefined; _fileId: string | undefined; readonly _type: 'root' | 'project' | 'file' | 'describe'; + readonly _testTypeImpl: TestTypeImpl | undefined; - constructor(title: string, type: 'root' | 'project' | 'file' | 'describe') { + constructor(title: string, type: 'root' | 'project' | 'file' | 'describe', testTypeImpl?: TestTypeImpl) { super(title); this._type = type; + this._testTypeImpl = testTypeImpl; } get suites(): Suite[] { @@ -121,6 +126,14 @@ export class Suite extends Base implements SuitePrivate { return titlePath; } + _collectGrepTitlePath(path: string[]) { + if (this.parent) + this.parent._collectGrepTitlePath(path); + if (this.title || this._type !== 'describe') + path.push(this.title); + path.push(...this._tags); + } + _getOnlyItems(): (TestCase | Suite)[] { const items: (TestCase | Suite)[] = []; if (this._only) @@ -185,6 +198,7 @@ export class Suite extends Base implements SuitePrivate { timeout: this._timeout, retries: this._retries, staticAnnotations: this._staticAnnotations.slice(), + tags: this._tags.slice(), modifiers: this._modifiers.slice(), parallelMode: this._parallelMode, hooks: this._hooks.map(h => ({ type: h.type, location: h.location, title: h.title })), @@ -200,6 +214,7 @@ export class Suite extends Base implements SuitePrivate { suite._timeout = data.timeout; suite._retries = data.retries; suite._staticAnnotations = data.staticAnnotations; + suite._tags = data.tags; suite._modifiers = data.modifiers; suite._parallelMode = data.parallelMode; suite._hooks = data.hooks.map((h: any) => ({ type: h.type, location: h.location, title: h.title, fn: () => { } })); @@ -239,8 +254,10 @@ export class TestCase extends Base implements reporterTypes.TestCase { _poolDigest = ''; _workerHash = ''; _projectId = ''; - // Annotations known statically before running the test, e.g. `test.skip()` or `test.describe.skip()`. + // Annotations known statically before running the test, e.g. `test.skip()` or `test(title, { annotation }, body)`. _staticAnnotations: Annotation[] = []; + // Explicitly declared tags that are not a part of the title. + _tags: string[] = []; constructor(title: string, fn: Function, testType: TestTypeImpl, location: Location) { super(title); @@ -278,6 +295,10 @@ export class TestCase extends Base implements reporterTypes.TestCase { return status === 'expected' || status === 'flaky' || status === 'skipped'; } + get tags(): string[] { + return this._grepTitle().match(/@[\S]+/g) || []; + } + _serialize(): any { return { kind: 'test', @@ -293,6 +314,7 @@ export class TestCase extends Base implements reporterTypes.TestCase { workerHash: this._workerHash, staticAnnotations: this._staticAnnotations.slice(), annotations: this.annotations.slice(), + tags: this._tags.slice(), projectId: this._projectId, }; } @@ -309,6 +331,7 @@ export class TestCase extends Base implements reporterTypes.TestCase { test._workerHash = data.workerHash; test._staticAnnotations = data.staticAnnotations; test.annotations = data.annotations; + test._tags = data.tags; test._projectId = data.projectId; return test; } @@ -338,4 +361,12 @@ export class TestCase extends Base implements reporterTypes.TestCase { this.results.push(result); return result; } + + _grepTitle() { + const path: string[] = []; + this.parent._collectGrepTitlePath(path); + path.push(this.title); + path.push(...this._tags); + return path.join(' '); + } } diff --git a/playwright/packages/playwright/src/common/testType.ts b/playwright/packages/playwright/src/common/testType.ts index 0b3dc6e1c9..1411b249b4 100644 --- a/playwright/packages/playwright/src/common/testType.ts +++ b/playwright/packages/playwright/src/common/testType.ts @@ -19,7 +19,7 @@ import { currentlyLoadingFileSuite, currentTestInfo, setCurrentlyLoadingFileSuit import { TestCase, Suite } from './test'; import { wrapFunctionWithLocation } from '../transform/transform'; import type { FixturesWithLocation } from './config'; -import type { Fixtures, TestType } from '../../types/test'; +import type { Fixtures, TestType, TestDetails } from '../../types/test'; import type { Location } from '../../types/testReporter'; import { getPackageManagerExecCommand } from 'playwright-core/lib/utils'; @@ -38,7 +38,7 @@ export class TestTypeImpl { test.only = wrapFunctionWithLocation(this._createTest.bind(this, 'only')); test.describe = wrapFunctionWithLocation(this._describe.bind(this, 'default')); test.describe.only = wrapFunctionWithLocation(this._describe.bind(this, 'only')); - test.describe.configure = wrapFunctionWithLocation(this._configure.bind(this)); + test.describe.configure = this._configure.bind(this); test.describe.fixme = wrapFunctionWithLocation(this._describe.bind(this, 'fixme')); test.describe.parallel = wrapFunctionWithLocation(this._describe.bind(this, 'parallel')); test.describe.parallel.only = wrapFunctionWithLocation(this._describe.bind(this, 'parallel.only')); @@ -53,7 +53,7 @@ export class TestTypeImpl { test.fixme = wrapFunctionWithLocation(this._modifier.bind(this, 'fixme')); test.fail = wrapFunctionWithLocation(this._modifier.bind(this, 'fail')); test.slow = wrapFunctionWithLocation(this._modifier.bind(this, 'slow')); - test.setTimeout = wrapFunctionWithLocation(this._setTimeout.bind(this)); + test.setTimeout = this._setTimeout.bind(this); test.step = this._step.bind(this); test.use = wrapFunctionWithLocation(this._use.bind(this)); test.extend = wrapFunctionWithLocation(this._extend.bind(this)); @@ -66,7 +66,7 @@ export class TestTypeImpl { this.test = test; } - private _currentSuite(location: Location, title: string): Suite | undefined { + private _currentSuite(title: string): Suite | undefined { const suite = currentlyLoadingFileSuite(); if (!suite) { throw new Error([ @@ -78,38 +78,74 @@ export class TestTypeImpl { ` when one of the dependencies in your package.json depends on @playwright/test.`, ].join('\n')); } + if (suite._testTypeImpl && suite._testTypeImpl !== this) { + throw new Error([ + `Can't call ${title} inside a describe() suite of a different test type.`, + `Make sure to use the same "test" function (created by the test.extend() call) for all declarations inside a suite.`, + ].join('\n')); + } return suite; } - private _createTest(type: 'default' | 'only' | 'skip' | 'fixme', location: Location, title: string, fn: Function) { + private _createTest(type: 'default' | 'only' | 'skip' | 'fixme' | 'fail', location: Location, title: string, fnOrDetails: Function | TestDetails, fn?: Function) { throwIfRunningInsideJest(); - const suite = this._currentSuite(location, 'test()'); + const suite = this._currentSuite('test()'); if (!suite) return; - const test = new TestCase(title, fn, this, location); + + let details: TestDetails; + let body: Function; + if (typeof fnOrDetails === 'function') { + body = fnOrDetails; + details = {}; + } else { + body = fn!; + details = fnOrDetails; + } + + const validatedDetails = validateTestDetails(details); + const test = new TestCase(title, body, this, location); test._requireFile = suite._requireFile; + test._staticAnnotations.push(...validatedDetails.annotations); + test._tags.push(...validatedDetails.tags); suite._addTest(test); if (type === 'only') test._only = true; - if (type === 'skip' || type === 'fixme') + if (type === 'skip' || type === 'fixme' || type === 'fail') test._staticAnnotations.push({ type }); } - private _describe(type: 'default' | 'only' | 'serial' | 'serial.only' | 'parallel' | 'parallel.only' | 'skip' | 'fixme', location: Location, title: string | Function, fn?: Function) { + private _describe(type: 'default' | 'only' | 'serial' | 'serial.only' | 'parallel' | 'parallel.only' | 'skip' | 'fixme', location: Location, titleOrFn: string | Function, fnOrDetails?: TestDetails | Function, fn?: Function) { throwIfRunningInsideJest(); - const suite = this._currentSuite(location, 'test.describe()'); + const suite = this._currentSuite('test.describe()'); if (!suite) return; - if (typeof title === 'function') { - fn = title; + let title: string; + let body: Function; + let details: TestDetails; + + if (typeof titleOrFn === 'function') { title = ''; + details = {}; + body = titleOrFn; + } else if (typeof fnOrDetails === 'function') { + title = titleOrFn; + details = {}; + body = fnOrDetails; + } else { + title = titleOrFn; + details = fnOrDetails!; + body = fn!; } - const child = new Suite(title, 'describe'); + const validatedDetails = validateTestDetails(details); + const child = new Suite(title, 'describe', this); child._requireFile = suite._requireFile; child.location = location; + child._staticAnnotations.push(...validatedDetails.annotations); + child._tags.push(...validatedDetails.tags); suite._addSuite(child); if (type === 'only' || type === 'serial.only' || type === 'parallel.only') @@ -129,12 +165,12 @@ export class TestTypeImpl { } setCurrentlyLoadingFileSuite(child); - fn!(); + body(); setCurrentlyLoadingFileSuite(suite); } private _hook(name: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', location: Location, title: string | Function, fn?: Function) { - const suite = this._currentSuite(location, `test.${name}()`); + const suite = this._currentSuite(`test.${name}()`); if (!suite) return; if (typeof title === 'function') { @@ -145,9 +181,9 @@ export class TestTypeImpl { suite._hooks.push({ type: name, fn: fn!, title, location }); } - private _configure(location: Location, options: { mode?: 'default' | 'parallel' | 'serial', retries?: number, timeout?: number }) { + private _configure(options: { mode?: 'default' | 'parallel' | 'serial', retries?: number, timeout?: number }) { throwIfRunningInsideJest(); - const suite = this._currentSuite(location, `test.describe.configure()`); + const suite = this._currentSuite(`test.describe.configure()`); if (!suite) return; @@ -170,14 +206,19 @@ export class TestTypeImpl { } } - private _modifier(type: 'skip' | 'fail' | 'fixme' | 'slow', location: Location, ...modifierArgs: [arg?: any | Function, description?: string]) { + private _modifier(type: 'skip' | 'fail' | 'fixme' | 'slow', location: Location, ...modifierArgs: any[]) { const suite = currentlyLoadingFileSuite(); if (suite) { - if (typeof modifierArgs[0] === 'string' && typeof modifierArgs[1] === 'function' && (type === 'skip' || type === 'fixme')) { - // Support for test.{skip,fixme}('title', () => {}) + if (typeof modifierArgs[0] === 'string' && typeof modifierArgs[1] === 'function' && (type === 'skip' || type === 'fixme' || type === 'fail')) { + // Support for test.{skip,fixme,fail}(title, body) this._createTest(type, location, modifierArgs[0], modifierArgs[1]); return; } + if (typeof modifierArgs[0] === 'string' && typeof modifierArgs[1] === 'object' && typeof modifierArgs[2] === 'function' && (type === 'skip' || type === 'fixme' || type === 'fail')) { + // Support for test.{skip,fixme,fail}(title, details, body) + this._createTest(type, location, modifierArgs[0], modifierArgs[1], modifierArgs[2]); + return; + } if (typeof modifierArgs[0] === 'function') { suite._modifiers.push({ type, fn: modifierArgs[0], location, description: modifierArgs[1] }); @@ -198,7 +239,7 @@ export class TestTypeImpl { testInfo[type](...modifierArgs as [any, any]); } - private _setTimeout(location: Location, timeout: number) { + private _setTimeout(timeout: number) { const suite = currentlyLoadingFileSuite(); if (suite) { suite._timeout = timeout; @@ -212,7 +253,7 @@ export class TestTypeImpl { } private _use(location: Location, fixtures: Fixtures) { - const suite = this._currentSuite(location, `test.use()`); + const suite = this._currentSuite(`test.use()`); if (!suite) return; suite._use.push({ fixtures, location }); @@ -247,6 +288,16 @@ function throwIfRunningInsideJest() { } } +function validateTestDetails(details: TestDetails) { + const annotations = Array.isArray(details.annotation) ? details.annotation : (details.annotation ? [details.annotation] : []); + const tags = Array.isArray(details.tag) ? details.tag : (details.tag ? [details.tag] : []); + for (const tag of tags) { + if (tag[0] !== '@') + throw new Error(`Tag must start with "@" symbol, got "${tag}" instead.`); + } + return { annotations, tags }; +} + export const rootTestType = new TestTypeImpl([]); export function mergeTests(...tests: TestType<any, any>[]) { diff --git a/playwright/packages/playwright/src/fsWatcher.ts b/playwright/packages/playwright/src/fsWatcher.ts new file mode 100644 index 0000000000..a4627beb88 --- /dev/null +++ b/playwright/packages/playwright/src/fsWatcher.ts @@ -0,0 +1,71 @@ +/** + * 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 { chokidar } from './utilsBundle'; +import type { FSWatcher } from 'chokidar'; + +export type FSEvent = { event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', file: string }; + +export class Watcher { + private _onChange: (events: FSEvent[]) => void; + private _watchedFiles: string[] = []; + private _ignoredFolders: string[] = []; + private _collector: FSEvent[] = []; + private _fsWatcher: FSWatcher | undefined; + private _throttleTimer: NodeJS.Timeout | undefined; + private _mode: 'flat' | 'deep'; + + constructor(mode: 'flat' | 'deep', onChange: (events: FSEvent[]) => void) { + this._mode = mode; + this._onChange = onChange; + } + + update(watchedFiles: string[], ignoredFolders: string[], reportPending: boolean) { + if (JSON.stringify([this._watchedFiles, this._ignoredFolders]) === JSON.stringify(watchedFiles, ignoredFolders)) + return; + + if (reportPending) + this._reportEventsIfAny(); + + this._watchedFiles = watchedFiles; + this._ignoredFolders = ignoredFolders; + void this._fsWatcher?.close(); + this._fsWatcher = undefined; + this._collector.length = 0; + clearTimeout(this._throttleTimer); + this._throttleTimer = undefined; + + if (!this._watchedFiles.length) + return; + + this._fsWatcher = chokidar.watch(watchedFiles, { ignoreInitial: true, ignored: this._ignoredFolders }).on('all', async (event, file) => { + if (this._throttleTimer) + clearTimeout(this._throttleTimer); + if (this._mode === 'flat' && event !== 'add' && event !== 'change') + return; + if (this._mode === 'deep' && event !== 'add' && event !== 'change' && event !== 'unlink' && event !== 'addDir' && event !== 'unlinkDir') + return; + this._collector.push({ event, file }); + this._throttleTimer = setTimeout(() => this._reportEventsIfAny(), 250); + }); + } + + private _reportEventsIfAny() { + if (this._collector.length) + this._onChange(this._collector.slice()); + this._collector.length = 0; + } +} diff --git a/playwright/packages/playwright/src/index.ts b/playwright/packages/playwright/src/index.ts index 2887577da1..59b70a1f62 100644 --- a/playwright/packages/playwright/src/index.ts +++ b/playwright/packages/playwright/src/index.ts @@ -19,15 +19,13 @@ import * as path from 'path'; import type { APIRequestContext, BrowserContext, Browser, BrowserContextOptions, LaunchOptions, Page, Tracing, Video } from 'playwright-core'; import * as playwrightLibrary from 'playwright-core'; import { createGuid, debugMode, addInternalStackPrefix, isString, asLocator, jsonStringifyForceASCII } from 'playwright-core/lib/utils'; -import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, TraceMode, VideoMode } from '../types/test'; +import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, VideoMode } from '../types/test'; import type { TestInfoImpl } from './worker/testInfo'; import { rootTestType } from './common/testType'; import type { ContextReuseMode } from './common/config'; import type { ClientInstrumentation, ClientInstrumentationListener } from '../../playwright-core/src/client/clientInstrumentation'; import { currentTestInfo } from './common/globals'; -import { mergeTraceFiles } from './worker/testTracing'; export { expect } from './matchers/expect'; -export { store as _store } from './store'; export const _baseTest: TestType<{}, {}> = rootTestType.test; addInternalStackPrefix(path.dirname(require.resolve('../package.json'))); @@ -46,15 +44,16 @@ if ((process as any)['__pw_initiator__']) { type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & { _combinedContextOptions: BrowserContextOptions, - _contextReuseMode: ContextReuseMode, - _reuseContext: boolean, _setupContextOptions: void; _setupArtifacts: void; _contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>; }; + type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _browserOptions: LaunchOptions; - _artifactsDir: string; + _optionContextReuseMode: ContextReuseMode, + _optionConnectOptions: PlaywrightWorkerOptions['connectOptions'], + _reuseContext: boolean, }; const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ @@ -66,18 +65,14 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ headless: [({ launchOptions }, use) => use(launchOptions.headless ?? true), { scope: 'worker', option: true }], channel: [({ launchOptions }, use) => use(launchOptions.channel), { scope: 'worker', option: true }], launchOptions: [{}, { scope: 'worker', option: true }], - connectOptions: [async ({}, use) => { - await use(connectOptionsFromEnv()); + connectOptions: [async ({ _optionConnectOptions }, use) => { + await use(connectOptionsFromEnv() || _optionConnectOptions); }, { scope: 'worker', option: true }], screenshot: ['off', { scope: 'worker', option: true }], video: ['off', { scope: 'worker', option: true }], trace: ['off', { scope: 'worker', option: true }], - _artifactsDir: [async ({}, use) => { - await use(process.env.TEST_ARTIFACTS_DIR!); - }, { scope: 'worker', _title: 'playwright configuration' } as any], - - _browserOptions: [async ({ playwright, headless, channel, launchOptions, _artifactsDir }, use) => { + _browserOptions: [async ({ playwright, headless, channel, launchOptions }, use) => { const options: LaunchOptions = { handleSIGINT: false, ...launchOptions, @@ -86,7 +81,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ options.headless = headless; if (channel !== undefined) options.channel = channel; - options.tracesDir = path.join(_artifactsDir, 'traces'); + options.tracesDir = tracing().tracesDir(); for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) (browserType as any)._defaultLaunchOptions = options; @@ -95,7 +90,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ (browserType as any)._defaultLaunchOptions = undefined; }, { scope: 'worker', auto: true }], - browser: [async ({ playwright, browserName, _browserOptions, connectOptions }, use, testInfo) => { + browser: [async ({ playwright, browserName, _browserOptions, connectOptions, _reuseContext }, use, testInfo) => { if (!['chromium', 'firefox', 'webkit'].includes(browserName)) throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`); @@ -104,7 +99,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ ...connectOptions, exposeNetwork: connectOptions.exposeNetwork ?? (connectOptions as any)._exposeNetwork, headers: { - ...(process.env.PW_TEST_REUSE_CONTEXT ? { 'x-playwright-reuse-context': '1' } : {}), + ...(_reuseContext ? { 'x-playwright-reuse-context': '1' } : {}), // HTTP headers are ASCII only (not UTF-8). 'x-playwright-launch-options': jsonStringifyForceASCII(_browserOptions), ...connectOptions.headers, @@ -225,7 +220,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ }); }, - _setupContextOptions: [async ({ playwright, _combinedContextOptions, _artifactsDir, actionTimeout, navigationTimeout, testIdAttribute }, use, testInfo) => { + _setupContextOptions: [async ({ playwright, _combinedContextOptions, actionTimeout, navigationTimeout, testIdAttribute }, use, testInfo) => { if (testIdAttribute) playwrightLibrary.selectors.setTestIdAttribute(testIdAttribute); testInfo.snapshotSuffix = process.platform; @@ -237,7 +232,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ (browserType as any)._defaultContextNavigationTimeout = navigationTimeout || 0; } (playwright.request as any)._defaultContextOptions = { ..._combinedContextOptions }; - (playwright.request as any)._defaultContextOptions.tracesDir = path.join(_artifactsDir, 'traces'); + (playwright.request as any)._defaultContextOptions.tracesDir = tracing().tracesDir(); (playwright.request as any)._defaultContextOptions.timeout = actionTimeout || 0; await use(); (playwright.request as any)._defaultContextOptions = undefined; @@ -248,8 +243,8 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ } }, { auto: 'all-hooks-included', _title: 'context configuration' } as any], - _setupArtifacts: [async ({ playwright, _artifactsDir, trace, screenshot }, use, testInfo) => { - const artifactsRecorder = new ArtifactsRecorder(playwright, _artifactsDir, trace, screenshot); + _setupArtifacts: [async ({ playwright, screenshot }, use, testInfo) => { + const artifactsRecorder = new ArtifactsRecorder(playwright, tracing().artifactsDir(), screenshot); await artifactsRecorder.willStartTest(testInfo as TestInfoImpl); const csiListener: ClientInstrumentationListener = { onApiCallBegin: (apiName: string, params: Record<string, any>, frames: StackFrame[], wallTime: number, userData: any) => { @@ -263,7 +258,6 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ apiName, params, wallTime, - laxParent: true, }); userData.userObject = step; }, @@ -297,11 +291,11 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ await use(); clientInstrumentation.removeListener(csiListener); - await artifactsRecorder?.didFinishTest(); + await artifactsRecorder.didFinishTest(); }, { auto: 'all-hooks-included', _title: 'trace recording' } as any], - _contextFactory: [async ({ browser, video, _artifactsDir, _reuseContext }, use, testInfo) => { + _contextFactory: [async ({ browser, video, _reuseContext }, use, testInfo) => { const testInfoImpl = testInfo as TestInfoImpl; const videoMode = normalizeVideoMode(video); const captureVideo = shouldCaptureVideo(videoMode, testInfo) && !_reuseContext; @@ -318,7 +312,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ } const videoOptions: BrowserContextOptions = captureVideo ? { recordVideo: { - dir: _artifactsDir, + dir: tracing().artifactsDir(), size: typeof video === 'string' ? undefined : video.size, } } : {}; @@ -333,7 +327,6 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ let counter = 0; const closeReason = testInfo.status === 'timedOut' ? 'Test timeout of ' + testInfo.timeout + 'ms exceeded.' : 'Test ended.'; await Promise.all([...contexts.keys()].map(async context => { - (context as any)[kStartedContextTearDown] = true; await (context as any)._wrapApiCall(async () => { await context.close({ reason: closeReason }); }, true); @@ -357,12 +350,16 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ }, { scope: 'test', _title: 'context' } as any], - _contextReuseMode: process.env.PW_TEST_REUSE_CONTEXT === 'when-possible' ? 'when-possible' : (process.env.PW_TEST_REUSE_CONTEXT ? 'force' : 'none'), + _optionContextReuseMode: ['none', { scope: 'worker', option: true }], + _optionConnectOptions: [undefined, { scope: 'worker', option: true }], - _reuseContext: [async ({ video, _contextReuseMode }, use, testInfo) => { - const reuse = _contextReuseMode === 'force' || (_contextReuseMode === 'when-possible' && !shouldCaptureVideo(normalizeVideoMode(video), testInfo)); + _reuseContext: [async ({ video, _optionContextReuseMode }, use) => { + let mode = _optionContextReuseMode; + if (process.env.PW_TEST_REUSE_CONTEXT) + mode = process.env.PW_TEST_REUSE_CONTEXT === 'when-possible' ? 'when-possible' : (process.env.PW_TEST_REUSE_CONTEXT ? 'force' : 'none'); + const reuse = mode === 'force' || (mode === 'when-possible' && normalizeVideoMode(video) === 'off'); await use(reuse); - }, { scope: 'test', _title: 'context' } as any], + }, { scope: 'worker', _title: 'context' } as any], context: async ({ playwright, browser, _reuseContext, _contextFactory }, use, testInfo) => { attachConnectedHeaderIfNeeded(testInfo, browser); @@ -395,7 +392,6 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({ request: async ({ playwright }, use) => { const request = await playwright.request.newContext(); await use(request); - (request as any)[kStartedContextTearDown] = true; await request.dispose(); }, }); @@ -414,7 +410,6 @@ type StackFrame = { }; type ScreenshotOption = PlaywrightWorkerOptions['screenshot'] | undefined; -type TraceOption = PlaywrightWorkerOptions['trace'] | undefined; type Playwright = PlaywrightWorkerArgs['playwright']; function normalizeVideoMode(video: VideoMode | 'retry-with-video' | { mode: VideoMode } | undefined): VideoMode { @@ -430,19 +425,6 @@ function shouldCaptureVideo(videoMode: VideoMode, testInfo: TestInfo) { return (videoMode === 'on' || videoMode === 'retain-on-failure' || (videoMode === 'on-first-retry' && testInfo.retry === 1)); } -function normalizeTraceMode(trace: TraceOption): TraceMode { - if (!trace) - return 'off'; - let traceMode = typeof trace === 'string' ? trace : trace.mode; - if (traceMode === 'retry-with-trace') - traceMode = 'on-first-retry'; - return traceMode; -} - -function shouldCaptureTrace(traceMode: TraceMode, testInfo: TestInfo) { - return traceMode === 'on' || traceMode === 'retain-on-failure' || (traceMode === 'on-first-retry' && testInfo.retry === 1) || (traceMode === 'on-all-retries' && testInfo.retry > 0); -} - function normalizeScreenshotMode(screenshot: ScreenshotOption): ScreenshotMode { if (!screenshot) return 'off'; @@ -467,8 +449,6 @@ function attachConnectedHeaderIfNeeded(testInfo: TestInfo, browser: Browser | nu const kTracingStarted = Symbol('kTracingStarted'); const kIsReusedContext = Symbol('kReusedContext'); -const kStartedContextTearDown = Symbol('kStartedContextTearDown'); -let traceOrdinal = 0; function connectOptionsFromEnv() { const wsEndpoint = process.env.PW_TEST_CONNECT_WS_ENDPOINT; @@ -487,11 +467,7 @@ class ArtifactsRecorder { private _playwright: Playwright; private _artifactsDir: string; private _screenshotMode: ScreenshotMode; - private _traceMode: TraceMode; - private _captureTrace = false; private _screenshotOptions: { mode: ScreenshotMode } & Pick<playwrightLibrary.PageScreenshotOptions, 'fullPage' | 'omitBackground'> | undefined; - private _traceOptions: { screenshots: boolean, snapshots: boolean, sources: boolean, attachments: boolean, _live: boolean, mode?: TraceMode }; - private _temporaryTraceFiles: string[] = []; private _temporaryScreenshots: string[] = []; private _temporaryArtifacts: string[] = []; private _reusedContexts = new Set<BrowserContext>(); @@ -499,14 +475,11 @@ class ArtifactsRecorder { private _screenshottedSymbol: symbol; private _startedCollectingArtifacts: symbol; - constructor(playwright: Playwright, artifactsDir: string, trace: TraceOption, screenshot: ScreenshotOption) { + constructor(playwright: Playwright, artifactsDir: string, screenshot: ScreenshotOption) { this._playwright = playwright; this._artifactsDir = artifactsDir; this._screenshotMode = normalizeScreenshotMode(screenshot); this._screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot; - this._traceMode = normalizeTraceMode(trace); - const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true, _live: false }; - this._traceOptions = typeof trace === 'string' ? defaultTraceOptions : { ...defaultTraceOptions, ...trace, mode: undefined }; this._screenshottedSymbol = Symbol('screenshotted'); this._startedCollectingArtifacts = Symbol('startedCollectingArtifacts'); } @@ -520,9 +493,6 @@ class ArtifactsRecorder { async willStartTest(testInfo: TestInfoImpl) { this._testInfo = testInfo; testInfo._onDidFinishTestFunction = () => this.didFinishTestFunction(); - this._captureTrace = shouldCaptureTrace(this._traceMode, testInfo) && !process.env.PW_TEST_DISABLE_TRACING; - if (this._captureTrace) - this._testInfo._tracing.start(this._createTemporaryArtifact('traces', `${this._testInfo.testId}-test.trace`), this._traceOptions); // Since beforeAll(s), test and afterAll(s) reuse the same TestInfo, make sure we do not // overwrite previous screenshots. @@ -555,7 +525,7 @@ class ArtifactsRecorder { // Do not record empty traces and useless screenshots for them. if (this._reusedContexts.has(context)) return; - await this._stopTracing(context.tracing, (context as any)[kStartedContextTearDown]); + await this._stopTracing(context.tracing); if (this._screenshotMode === 'on' || this._screenshotMode === 'only-on-failure') { // Capture screenshot for now. We'll know whether we have to preserve them // after the test finishes. @@ -570,7 +540,7 @@ class ArtifactsRecorder { async willCloseRequestContext(context: APIRequestContext) { const tracing = (context as any)._tracing as Tracing; - await this._stopTracing(tracing, (context as any)[kStartedContextTearDown]); + await this._stopTracing(tracing); } async didFinishTestFunction() { @@ -591,10 +561,10 @@ class ArtifactsRecorder { // Collect traces/screenshots for remaining contexts. await Promise.all(leftoverContexts.map(async context => { - await this._stopTracing(context.tracing, true); + await this._stopTracing(context.tracing); }).concat(leftoverApiRequests.map(async context => { const tracing = (context as any)._tracing as Tracing; - await this._stopTracing(tracing, true); + await this._stopTracing(tracing); }))); // Attach temporary screenshots for contexts closed before collecting the test trace. @@ -608,32 +578,6 @@ class ArtifactsRecorder { } } } - - // Collect test trace. - if (this._preserveTrace()) { - const tracePath = this._createTemporaryArtifact(createGuid() + '.zip'); - this._temporaryTraceFiles.push(tracePath); - await this._testInfo._tracing.stop(tracePath); - } - - // Either remove or attach temporary traces for contexts closed before the - // test has finished. - if (this._preserveTrace() && this._temporaryTraceFiles.length) { - const tracePath = this._testInfo.outputPath(`trace.zip`); - // This could be: beforeHooks, or beforeHooks + test, etc. - const beforeHooksHadTrace = fs.existsSync(tracePath); - if (beforeHooksHadTrace) { - await fs.promises.rename(tracePath, tracePath + '.tmp'); - this._temporaryTraceFiles.unshift(tracePath + '.tmp'); - } - await mergeTraceFiles(tracePath, this._temporaryTraceFiles); - // Do not add attachment twice. - if (!beforeHooksHadTrace) - this._testInfo.attachments.push({ name: 'trace', path: tracePath, contentType: 'application/zip' }); - } - - for (const file of this._temporaryArtifacts) - await fs.promises.unlink(file).catch(() => {}); } private _createScreenshotAttachmentPath() { @@ -675,15 +619,12 @@ class ArtifactsRecorder { } private async _startTraceChunkOnContextCreation(tracing: Tracing) { - if (this._captureTrace) { - const title = [path.relative(this._testInfo.project.testDir, this._testInfo.file) + ':' + this._testInfo.line, ...this._testInfo.titlePath.slice(1)].join(' › '); - const ordinalSuffix = traceOrdinal ? `-context${traceOrdinal}` : ''; - ++traceOrdinal; - const retrySuffix = this._testInfo.retry ? `-retry${this._testInfo.retry}` : ''; - // Note that trace name must start with testId for live tracing to work. - const name = `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`; + const options = this._testInfo._tracing.traceOptions(); + if (options) { + const title = this._testInfo._tracing.traceTitle(); + const name = this._testInfo._tracing.generateNextTraceRecordingName(); if (!(tracing as any)[kTracingStarted]) { - await tracing.start({ ...this._traceOptions, title, name }); + await tracing.start({ ...options, title, name }); (tracing as any)[kTracingStarted] = true; } else { await tracing.startChunk({ title, name }); @@ -696,26 +637,12 @@ class ArtifactsRecorder { } } - private _preserveTrace() { - const testFailed = this._testInfo.status !== this._testInfo.expectedStatus; - return this._captureTrace && (this._traceMode === 'on' || (testFailed && this._traceMode === 'retain-on-failure') || (this._traceMode === 'on-first-retry' && this._testInfo.retry === 1) || (this._traceMode === 'on-all-retries' && this._testInfo.retry > 0)); - } - - private async _stopTracing(tracing: Tracing, contextTearDownStarted: boolean) { + private async _stopTracing(tracing: Tracing) { if ((tracing as any)[this._startedCollectingArtifacts]) return; (tracing as any)[this._startedCollectingArtifacts] = true; - if (this._captureTrace) { - let tracePath; - // Create a trace file if we know that: - // - it is's going to be used due to the config setting and the test status or - // - we are inside a test or afterEach and the user manually closed the context. - if (this._preserveTrace() || !contextTearDownStarted) { - tracePath = this._createTemporaryArtifact(createGuid() + '.zip'); - this._temporaryTraceFiles.push(tracePath); - } - await tracing.stopChunk({ path: tracePath }); - } + if (this._testInfo._tracing.traceOptions()) + await tracing.stopChunk({ path: this._testInfo._tracing.generateNextTraceRecordingPath() }); } } @@ -743,6 +670,10 @@ function renderApiCall(apiName: string, params: any) { return apiName + paramsText; } +function tracing() { + return (test.info() as TestInfoImpl)._tracing; +} + export const test = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFixtures); export { defineConfig } from './common/configLoader'; diff --git a/playwright/packages/playwright/src/isomorphic/teleReceiver.ts b/playwright/packages/playwright/src/isomorphic/teleReceiver.ts index c5d82d810d..a698f2b119 100644 --- a/playwright/packages/playwright/src/isomorphic/teleReceiver.ts +++ b/playwright/packages/playwright/src/isomorphic/teleReceiver.ts @@ -73,6 +73,7 @@ export type JsonTestCase = { title: string; location: JsonLocation; retries: number; + tags?: string[]; }; export type JsonTestEnd = { @@ -209,8 +210,8 @@ export class TeleReporterReceiver { this._rootSuite.suites.push(projectSuite); projectSuite.parent = this._rootSuite; } - const p = this._parseProject(project); - projectSuite.project = () => p; + // Always update project in watch mode. + projectSuite._project = this._parseProject(project); this._mergeSuitesInto(project.suites, projectSuite); // Remove deleted tests when listing. Empty suites will be auto-filtered @@ -394,6 +395,7 @@ export class TeleReporterReceiver { test.id = payload.testId; test.location = this._absoluteLocation(payload.location); test.retries = payload.retries; + test.tags = payload.tags ?? []; return test; } @@ -428,6 +430,7 @@ export class TeleSuite implements SuitePrivate { _timeout: number | undefined; _retries: number | undefined; _fileId: string | undefined; + _project: TeleFullProject | undefined; _parallelMode: 'none' | 'default' | 'serial' | 'parallel' = 'none'; readonly _type: 'root' | 'project' | 'file' | 'describe'; @@ -459,7 +462,7 @@ export class TeleSuite implements SuitePrivate { } project(): TeleFullProject | undefined { - return undefined; + return this._project ?? this.parent?.project(); } } @@ -474,6 +477,7 @@ export class TeleTestCase implements reporterTypes.TestCase { timeout = 0; annotations: Annotation[] = []; retries = 0; + tags: string[] = []; repeatEachIndex = 0; id: string; diff --git a/playwright/packages/playwright/src/loader/loaderMain.ts b/playwright/packages/playwright/src/loader/loaderMain.ts index 7e865c7e4b..97befeea9d 100644 --- a/playwright/packages/playwright/src/loader/loaderMain.ts +++ b/playwright/packages/playwright/src/loader/loaderMain.ts @@ -15,7 +15,7 @@ */ import type { SerializedConfig } from '../common/ipc'; -import { ConfigLoader } from '../common/configLoader'; +import { deserializeConfig } from '../common/configLoader'; import { ProcessRunner } from '../common/process'; import type { FullConfigInternal } from '../common/config'; import { loadTestFile } from '../common/testLoader'; @@ -36,7 +36,7 @@ export class LoaderMain extends ProcessRunner { private _config(): Promise<FullConfigInternal> { if (!this._configPromise) - this._configPromise = ConfigLoader.deserialize(this._serializedConfig); + this._configPromise = deserializeConfig(this._serializedConfig); return this._configPromise; } diff --git a/playwright/packages/playwright/src/matchers/expect.ts b/playwright/packages/playwright/src/matchers/expect.ts index 8206579453..0b6b3da8ad 100644 --- a/playwright/packages/playwright/src/matchers/expect.ts +++ b/playwright/packages/playwright/src/matchers/expect.ts @@ -267,7 +267,6 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler<any> { params: args[0] ? { expected: args[0] } : undefined, wallTime, infectParentStepsWithError: this._info.isSoft, - laxParent: true, isSoft: this._info.isSoft, }; diff --git a/playwright/packages/playwright/src/matchers/matcherHint.ts b/playwright/packages/playwright/src/matchers/matcherHint.ts index 229ef6ca28..17d893431c 100644 --- a/playwright/packages/playwright/src/matchers/matcherHint.ts +++ b/playwright/packages/playwright/src/matchers/matcherHint.ts @@ -25,7 +25,7 @@ export function matcherHint(state: ExpectMatcherContext, locator: Locator | unde if (timeout) header = colors.red(`Timed out ${timeout}ms waiting for `) + header; if (locator) - header += `Locator: ${locator}\n`; + header += `Locator: ${String(locator)}\n`; return header; } diff --git a/playwright/packages/playwright/src/matchers/matchers.ts b/playwright/packages/playwright/src/matchers/matchers.ts index 91bdc3701b..01d1dfad25 100644 --- a/playwright/packages/playwright/src/matchers/matchers.ts +++ b/playwright/packages/playwright/src/matchers/matchers.ts @@ -25,6 +25,7 @@ import { constructURLBasedOnBaseURL, isRegExp, isTextualMimeType, pollAgainstDea import { currentTestInfo } from '../common/globals'; import { TestInfoImpl } from '../worker/testInfo'; import type { ExpectMatcherContext } from './expect'; +import { takeFirst } from '../common/config'; interface LocatorEx extends Locator { _expect(expression: string, options: Omit<FrameExpectOptions, 'expectedValue'> & { expectedValue?: any }): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }>; @@ -39,7 +40,7 @@ export function toBeAttached( locator: LocatorEx, options?: { attached?: boolean, timeout?: number }, ) { - const attached = !options || options.attached === undefined || options.attached === true; + const attached = !options || options.attached === undefined || options.attached; const expected = attached ? 'attached' : 'detached'; const unexpected = attached ? 'detached' : 'attached'; const arg = attached ? '' : '{ attached: false }'; @@ -53,7 +54,7 @@ export function toBeChecked( locator: LocatorEx, options?: { checked?: boolean, timeout?: number }, ) { - const checked = !options || options.checked === undefined || options.checked === true; + const checked = !options || options.checked === undefined || options.checked; const expected = checked ? 'checked' : 'unchecked'; const unexpected = checked ? 'unchecked' : 'checked'; const arg = checked ? '' : '{ checked: false }'; @@ -77,7 +78,7 @@ export function toBeEditable( locator: LocatorEx, options?: { editable?: boolean, timeout?: number }, ) { - const editable = !options || options.editable === undefined || options.editable === true; + const editable = !options || options.editable === undefined || options.editable; const expected = editable ? 'editable' : 'readOnly'; const unexpected = editable ? 'readOnly' : 'editable'; const arg = editable ? '' : '{ editable: false }'; @@ -101,7 +102,7 @@ export function toBeEnabled( locator: LocatorEx, options?: { enabled?: boolean, timeout?: number }, ) { - const enabled = !options || options.enabled === undefined || options.enabled === true; + const enabled = !options || options.enabled === undefined || options.enabled; const expected = enabled ? 'enabled' : 'disabled'; const unexpected = enabled ? 'disabled' : 'enabled'; const arg = enabled ? '' : '{ enabled: false }'; @@ -135,7 +136,7 @@ export function toBeVisible( locator: LocatorEx, options?: { visible?: boolean, timeout?: number }, ) { - const visible = !options || options.visible === undefined || options.visible === true; + const visible = !options || options.visible === undefined || options.visible; const expected = visible ? 'visible' : 'hidden'; const unexpected = visible ? 'hidden' : 'visible'; const arg = visible ? '' : '{ visible: false }'; @@ -367,7 +368,7 @@ export async function toPass( } = {}, ) { const testInfo = currentTestInfo(); - const timeout = options.timeout !== undefined ? options.timeout : 0; + const timeout = takeFirst(options.timeout, testInfo?._projectInternal.expect?.toPass?.timeout, 0); const { deadline, timeoutMessage } = testInfo ? testInfo._deadlineForMatcher(timeout) : TestInfoImpl._defaultDeadlineForMatcher(timeout); const result = await pollAgainstDeadline<Error|undefined>(async () => { diff --git a/playwright/packages/playwright/src/matchers/toMatchSnapshot.ts b/playwright/packages/playwright/src/matchers/toMatchSnapshot.ts index e551ebf9c1..80e602f5ad 100644 --- a/playwright/packages/playwright/src/matchers/toMatchSnapshot.ts +++ b/playwright/packages/playwright/src/matchers/toMatchSnapshot.ts @@ -15,12 +15,10 @@ */ import type { Locator, Page } from 'playwright-core'; -import type { Page as PageEx } from 'playwright-core/lib/client/page'; -import type { Locator as LocatorEx } from 'playwright-core/lib/client/locator'; +import type { ExpectScreenshotOptions, Page as PageEx } from 'playwright-core/lib/client/page'; import { currentTestInfo, currentExpectTimeout } from '../common/globals'; import type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils'; import { getComparator, sanitizeForFilePath, zones } from 'playwright-core/lib/utils'; -import type { PageScreenshotOptions } from 'playwright-core/types/types'; import { addSuffixToFilePath, trimLongString, callLogText, @@ -32,6 +30,7 @@ import { mime } from 'playwright-core/lib/utilsBundle'; import type { TestInfoImpl } from '../worker/testInfo'; import type { ExpectMatcherContext } from './expect'; import type { MatcherResult } from './matcherHint'; +import type { FullProjectInternal } from '../common/config'; type NameOrSegments = string | string[]; const snapshotNamesSymbol = Symbol('snapshotNames'); @@ -43,7 +42,36 @@ type SnapshotNames = { type ImageMatcherResult = MatcherResult<string, string> & { diff?: string }; -class SnapshotHelper<T extends ImageComparatorOptions> { +type ToHaveScreenshotConfigOptions = NonNullable<NonNullable<FullProjectInternal['expect']>['toHaveScreenshot']> & { + _comparator?: string; +}; + +type ToHaveScreenshotOptions = ToHaveScreenshotConfigOptions & { + clip?: { + x: number; + y: number; + width: number; + height: number; + }; + fullPage?: boolean; + mask?: Array<Locator>; + maskColor?: string; + omitBackground?: boolean; + timeout?: number; +}; + +// Keep in sync with above (begin). +const NonConfigProperties: (keyof ToHaveScreenshotOptions)[] = [ + 'clip', + 'fullPage', + 'mask', + 'maskColor', + 'omitBackground', + 'timeout', +]; +// Keep in sync with above (end). + +class SnapshotHelper { readonly testInfo: TestInfoImpl; readonly snapshotName: string; readonly legacyExpectedPath: string; @@ -54,9 +82,8 @@ class SnapshotHelper<T extends ImageComparatorOptions> { readonly mimeType: string; readonly kind: 'Screenshot'|'Snapshot'; readonly updateSnapshots: 'all' | 'none' | 'missing'; - readonly comparatorOptions: ImageComparatorOptions; readonly comparator: Comparator; - readonly allOptions: T; + readonly options: Omit<ToHaveScreenshotOptions, '_comparator'> & { comparator?: string }; readonly matcherName: string; readonly locator: Locator | undefined; @@ -66,19 +93,18 @@ class SnapshotHelper<T extends ImageComparatorOptions> { locator: Locator | undefined, snapshotPathResolver: (...pathSegments: string[]) => string, anonymousSnapshotExtension: string, - configOptions: ImageComparatorOptions, - nameOrOptions: NameOrSegments | { name?: NameOrSegments } & T, - optOptions: T, + configOptions: ToHaveScreenshotConfigOptions, + nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions, + optOptions: ToHaveScreenshotOptions, ) { - let options: T; let name: NameOrSegments | undefined; if (Array.isArray(nameOrOptions) || typeof nameOrOptions === 'string') { name = nameOrOptions; - options = optOptions; + this.options = { ...optOptions }; } else { name = nameOrOptions.name; - options = { ...nameOrOptions }; - delete (options as any).name; + this.options = { ...nameOrOptions }; + delete (this.options as any).name; } let snapshotNames = (testInfo as any)[snapshotNamesSymbol] as SnapshotNames; @@ -116,15 +142,24 @@ class SnapshotHelper<T extends ImageComparatorOptions> { } } - options = { - ...configOptions, - ...options, + const filteredConfigOptions = { ...configOptions }; + for (const prop of NonConfigProperties) + delete (filteredConfigOptions as any)[prop]; + this.options = { + ...filteredConfigOptions, + ...this.options, }; - if (options.maxDiffPixels !== undefined && options.maxDiffPixels < 0) + // While comparator is not a part of the public API, it is translated here. + if ((this.options as any)._comparator) { + this.options.comparator = (this.options as any)._comparator; + delete (this.options as any)._comparator; + } + + if (this.options.maxDiffPixels !== undefined && this.options.maxDiffPixels < 0) throw new Error('`maxDiffPixels` option value must be non-negative integer'); - if (options.maxDiffPixelRatio !== undefined && (options.maxDiffPixelRatio < 0 || options.maxDiffPixelRatio > 1)) + if (this.options.maxDiffPixelRatio !== undefined && (this.options.maxDiffPixelRatio < 0 || this.options.maxDiffPixelRatio > 1)) throw new Error('`maxDiffPixelRatio` option value must be between 0 and 1'); // sanitizes path if string @@ -141,19 +176,10 @@ class SnapshotHelper<T extends ImageComparatorOptions> { this.locator = locator; this.updateSnapshots = testInfo.config.updateSnapshots; - if (this.updateSnapshots === 'missing' && testInfo.retry < testInfo.project.retries) - this.updateSnapshots = 'none'; this.mimeType = mime.getType(path.basename(this.snapshotPath)) ?? 'application/octet-string'; this.comparator = getComparator(this.mimeType); this.testInfo = testInfo; - this.allOptions = options; - this.comparatorOptions = { - maxDiffPixels: options.maxDiffPixels, - maxDiffPixelRatio: options.maxDiffPixelRatio, - threshold: options.threshold, - _comparator: options._comparator, - }; this.kind = this.mimeType.startsWith('image/') ? 'Screenshot' : 'Snapshot'; } @@ -206,7 +232,7 @@ class SnapshotHelper<T extends ImageComparatorOptions> { return this.createMatcherResult(message, true); } if (this.updateSnapshots === 'missing') { - this.testInfo._failWithError(new Error(message), false /* isHardError */); + this.testInfo._failWithError(new Error(message), false /* isHardError */, false /* retriable */); return this.createMatcherResult('', true); } return this.createMatcherResult(message, false); @@ -286,7 +312,7 @@ export function toMatchSnapshot( if (this.isNot) { if (!fs.existsSync(helper.snapshotPath)) return helper.handleMissingNegated(); - const isDifferent = !!helper.comparator(received, fs.readFileSync(helper.snapshotPath), helper.comparatorOptions); + const isDifferent = !!helper.comparator(received, fs.readFileSync(helper.snapshotPath), helper.options); return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated(); } @@ -294,7 +320,7 @@ export function toMatchSnapshot( return helper.handleMissing(received); const expected = fs.readFileSync(helper.snapshotPath); - const result = helper.comparator(received, expected, helper.comparatorOptions); + const result = helper.comparator(received, expected, helper.options); if (!result) return helper.handleMatching(); @@ -308,11 +334,9 @@ export function toMatchSnapshot( return helper.handleDifferent(received, expected, undefined, result.diff, result.errorMessage, undefined); } -type HaveScreenshotOptions = ImageComparatorOptions & Omit<PageScreenshotOptions, 'type' | 'quality' | 'path' | 'style'> & { stylePath?: string | string[] }; - export function toHaveScreenshotStepTitle( - nameOrOptions: NameOrSegments | { name?: NameOrSegments } & HaveScreenshotOptions = {}, - optOptions: HaveScreenshotOptions = {} + nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {}, + optOptions: ToHaveScreenshotOptions = {} ): string { let name: NameOrSegments | undefined; if (typeof nameOrOptions === 'object' && !Array.isArray(nameOrOptions)) @@ -325,8 +349,8 @@ export function toHaveScreenshotStepTitle( export async function toHaveScreenshot( this: ExpectMatcherContext, pageOrLocator: Page | Locator, - nameOrOptions: NameOrSegments | { name?: NameOrSegments } & HaveScreenshotOptions = {}, - optOptions: HaveScreenshotOptions = {} + nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {}, + optOptions: ToHaveScreenshotOptions = {} ): Promise<MatcherResult<NameOrSegments | { name?: NameOrSegments }, string>> { const testInfo = currentTestInfo(); if (!testInfo) @@ -336,47 +360,45 @@ export async function toHaveScreenshot( return { pass: !this.isNot, message: () => '', name: 'toHaveScreenshot', expected: nameOrOptions }; expectTypes(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot'); - const [page, locator] = pageOrLocator.constructor.name === 'Page' ? [(pageOrLocator as PageEx), undefined] : [(pageOrLocator as Locator).page() as PageEx, pageOrLocator as LocatorEx]; - const config = (testInfo._projectInternal.expect as any)?.toHaveScreenshot; + const [page, locator] = pageOrLocator.constructor.name === 'Page' ? [(pageOrLocator as PageEx), undefined] : [(pageOrLocator as Locator).page() as PageEx, pageOrLocator as Locator]; + const configOptions = testInfo._projectInternal.expect?.toHaveScreenshot || {}; const snapshotPathResolver = testInfo.snapshotPath.bind(testInfo); const helper = new SnapshotHelper( testInfo, 'toHaveScreenshot', locator, snapshotPathResolver, 'png', - { - _comparator: config?._comparator, - maxDiffPixels: config?.maxDiffPixels, - maxDiffPixelRatio: config?.maxDiffPixelRatio, - threshold: config?.threshold, - }, - nameOrOptions, optOptions); + configOptions, nameOrOptions, optOptions); if (!helper.snapshotPath.toLowerCase().endsWith('.png')) throw new Error(`Screenshot name "${path.basename(helper.snapshotPath)}" must have '.png' extension`); expectTypes(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot'); return await zones.preserve(async () => { // Loading from filesystem resets zones. - const style = await loadScreenshotStyles(optOptions.stylePath || config?.stylePath); - return toHaveScreenshotContinuation.call(this, helper, page, locator, config, style); + const style = await loadScreenshotStyles(helper.options.stylePath); + return toHaveScreenshotContinuation.call(this, helper, page, locator, style); }); } async function toHaveScreenshotContinuation( this: ExpectMatcherContext, - helper: SnapshotHelper<HaveScreenshotOptions>, + helper: SnapshotHelper, page: PageEx, - locator: LocatorEx | undefined, - config?: HaveScreenshotOptions, + locator: Locator | undefined, style?: string) { - const screenshotOptions: any = { - animations: config?.animations ?? 'disabled', - scale: config?.scale ?? 'css', - caret: config?.caret ?? 'hide', + const expectScreenshotOptions: ExpectScreenshotOptions = { + locator, + animations: helper.options.animations ?? 'disabled', + caret: helper.options.caret ?? 'hide', + clip: helper.options.clip, + fullPage: helper.options.fullPage, + mask: helper.options.mask, + maskColor: helper.options.maskColor, + omitBackground: helper.options.omitBackground, + scale: helper.options.scale ?? 'css', style, - ...helper.allOptions, - mask: (helper.allOptions.mask || []) as LocatorEx[], - maskColor: helper.allOptions.maskColor, - name: undefined, - threshold: undefined, - maxDiffPixels: undefined, - maxDiffPixelRatio: undefined, + isNot: !!this.isNot, + timeout: currentExpectTimeout(helper.options), + comparator: helper.options.comparator, + maxDiffPixels: helper.options.maxDiffPixels, + maxDiffPixelRatio: helper.options.maxDiffPixelRatio, + threshold: helper.options.threshold, }; const hasSnapshot = fs.existsSync(helper.snapshotPath); @@ -387,17 +409,8 @@ async function toHaveScreenshotContinuation( // Having `errorMessage` means we timed out while waiting // for screenshots not to match, so screenshots // are actually the same in the end. - const isDifferent = !(await page._expectScreenshot({ - expected: await fs.promises.readFile(helper.snapshotPath), - isNot: true, - locator, - comparatorOptions: { - ...helper.comparatorOptions, - comparator: helper.comparatorOptions._comparator, - }, - screenshotOptions, - timeout: currentExpectTimeout(helper.allOptions), - })).errorMessage; + expectScreenshotOptions.expected = await fs.promises.readFile(helper.snapshotPath); + const isDifferent = !(await page._expectScreenshot(expectScreenshotOptions)).errorMessage; return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated(); } @@ -407,15 +420,7 @@ async function toHaveScreenshotContinuation( if (!hasSnapshot) { // Regenerate a new screenshot by waiting until two screenshots are the same. - const timeout = currentExpectTimeout(helper.allOptions); - const { actual, previous, diff, errorMessage, log } = await page._expectScreenshot({ - expected: undefined, - isNot: false, - locator, - comparatorOptions: { ...helper.comparatorOptions, comparator: helper.comparatorOptions._comparator }, - screenshotOptions, - timeout, - }); + const { actual, previous, diff, errorMessage, log } = await page._expectScreenshot(expectScreenshotOptions); // We tried re-generating new snapshot but failed. // This can be due to e.g. spinning animation, so we want to show it as a diff. if (errorMessage) @@ -429,15 +434,8 @@ async function toHaveScreenshotContinuation( // - snapshot exists // - regular matcher (i.e. not a `.not`) // - perhaps an 'all' flag to update non-matching screenshots - const expected = await fs.promises.readFile(helper.snapshotPath); - const { actual, diff, errorMessage, log } = await page._expectScreenshot({ - expected, - isNot: false, - locator, - comparatorOptions: { ...helper.comparatorOptions, comparator: helper.comparatorOptions._comparator }, - screenshotOptions, - timeout: currentExpectTimeout(helper.allOptions), - }); + expectScreenshotOptions.expected = await fs.promises.readFile(helper.snapshotPath); + const { actual, diff, errorMessage, log } = await page._expectScreenshot(expectScreenshotOptions); if (!errorMessage) return helper.handleMatching(); @@ -450,7 +448,7 @@ async function toHaveScreenshotContinuation( return helper.createMatcherResult(helper.snapshotPath + ' running with --update-snapshots, writing actual.', true); } - return helper.handleDifferent(actual, expected, undefined, diff, errorMessage, log); + return helper.handleDifferent(actual, expectScreenshotOptions.expected, undefined, diff, errorMessage, log); } function writeFileSync(aPath: string, content: Buffer | string) { @@ -467,7 +465,7 @@ function determineFileExtension(file: string | Buffer): string { return 'txt'; if (compareMagicBytes(file, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) return 'png'; - if (compareMagicBytes(file, [0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01])) + if (compareMagicBytes(file, [0xff, 0xd8, 0xff])) return 'jpg'; return 'dat'; } diff --git a/playwright/packages/playwright/src/plugins/webServerPlugin.ts b/playwright/packages/playwright/src/plugins/webServerPlugin.ts index 8fb863ba05..86f84ad652 100644 --- a/playwright/packages/playwright/src/plugins/webServerPlugin.ts +++ b/playwright/packages/playwright/src/plugins/webServerPlugin.ts @@ -17,7 +17,7 @@ import path from 'path'; import net from 'net'; import { colors, debug } from 'playwright-core/lib/utilsBundle'; -import { raceAgainstDeadline, launchProcess, httpRequest, monotonicTime } from 'playwright-core/lib/utils'; +import { raceAgainstDeadline, launchProcess, monotonicTime, isURLAvailable } from 'playwright-core/lib/utils'; import type { FullConfig } from '../../types/testReporter'; import type { TestRunnerPlugin } from '.'; @@ -155,37 +155,6 @@ async function isPortUsed(port: number): Promise<boolean> { return await innerIsPortUsed('127.0.0.1') || await innerIsPortUsed('::1'); } -async function isURLAvailable(url: URL, ignoreHTTPSErrors: boolean, onStdErr: ReporterV2['onStdErr']) { - let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onStdErr); - if (statusCode === 404 && url.pathname === '/') { - const indexUrl = new URL(url); - indexUrl.pathname = '/index.html'; - statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onStdErr); - } - return statusCode >= 200 && statusCode < 404; -} - -async function httpStatusCode(url: URL, ignoreHTTPSErrors: boolean, onStdErr: ReporterV2['onStdErr']): Promise<number> { - return new Promise(resolve => { - debugWebServer(`HTTP GET: ${url}`); - httpRequest({ - url: url.toString(), - headers: { Accept: '*/*' }, - rejectUnauthorized: !ignoreHTTPSErrors - }, res => { - res.resume(); - const statusCode = res.statusCode ?? 0; - debugWebServer(`HTTP Status: ${statusCode}`); - resolve(statusCode); - }, error => { - if ((error as NodeJS.ErrnoException).code === 'DEPTH_ZERO_SELF_SIGNED_CERT') - onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`); - debugWebServer(`Error while checking if ${url} is available: ${error.message}`); - resolve(0); - }); - }); -} - async function waitFor(waitFn: () => Promise<boolean>, cancellationToken: { canceled: boolean }) { const logScale = [100, 250, 500]; while (!cancellationToken.canceled) { @@ -201,7 +170,7 @@ async function waitFor(waitFn: () => Promise<boolean>, cancellationToken: { canc function getIsAvailableFunction(url: string, checkPortOnly: boolean, ignoreHTTPSErrors: boolean, onStdErr: ReporterV2['onStdErr']) { const urlObject = new URL(url); if (!checkPortOnly) - return () => isURLAvailable(urlObject, ignoreHTTPSErrors, onStdErr); + return () => isURLAvailable(urlObject, ignoreHTTPSErrors, debugWebServer, onStdErr); const port = urlObject.port; return () => isPortUsed(+port); } diff --git a/playwright/packages/playwright/src/cli.ts b/playwright/packages/playwright/src/program.ts similarity index 76% rename from playwright/packages/playwright/src/cli.ts rename to playwright/packages/playwright/src/program.ts index 34a0b2372c..d24add96d3 100644 --- a/playwright/packages/playwright/src/cli.ts +++ b/playwright/packages/playwright/src/program.ts @@ -21,20 +21,20 @@ import fs from 'fs'; import path from 'path'; import { Runner } from './runner/runner'; import { stopProfiling, startProfiling, gracefullyProcessExitDoNotHang } from 'playwright-core/lib/utils'; -import { fileIsModule, serializeError } from './util'; +import { serializeError } from './util'; import { showHTMLReport } from './reporters/html'; import { createMergedReport } from './reporters/merge'; -import { ConfigLoader, resolveConfigFile } from './common/configLoader'; +import { loadConfigFromFileRestartIfNeeded, loadEmptyConfigForMergeReports } from './common/configLoader'; import type { ConfigCLIOverrides } from './common/ipc'; import type { FullResult, TestError } from '../types/testReporter'; import type { TraceMode } from '../types/test'; import { builtInReporters, defaultReporter, defaultTimeout } from './common/config'; -import type { FullConfigInternal } from './common/config'; -import program from 'playwright-core/lib/cli/program'; +import { program } from 'playwright-core/lib/cli/program'; +export { program } from 'playwright-core/lib/cli/program'; import type { ReporterDescription } from '../types/test'; import { prepareErrorStack } from './reporters/base'; -import { registerESMLoader } from './common/esmLoaderHost'; -import { execArgvWithExperimentalLoaderOptions, execArgvWithoutExperimentalLoaderOptions } from './transform/esmUtils'; +import { cacheDir } from './transform/compilationCache'; +import { runTestServer } from './runner/testServer'; function addTestCommand(program: Command) { const command = program.command('test [test-filter...]'); @@ -64,14 +64,52 @@ function addListFilesCommand(program: Command) { const command = program.command('list-files [file-filter...]', { hidden: true }); command.description('List files with Playwright Test tests'); command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`); - command.option('--project <project-name...>', `Only run tests from the specified list of projects (default: list all projects)`); - command.action(async (args, opts) => { - try { - await listTestFiles(opts); - } catch (e) { - console.error(e); - gracefullyProcessExitDoNotHang(1); + command.option('--project <project-name...>', `Only run tests from the specified list of projects, supports '*' wildcard (default: list all projects)`); + command.action(async (args, opts) => listTestFiles(opts)); +} + +function addClearCacheCommand(program: Command) { + const command = program.command('clear-cache', { hidden: true }); + command.description('clears build and test caches'); + command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`); + command.action(async opts => { + const configInternal = await loadConfigFromFileRestartIfNeeded(opts.config); + if (!configInternal) + return; + const { config, configDir } = configInternal; + const override = (config as any)['@playwright/test']?.['cli']?.['clear-cache']; + if (override) { + await override(config, configDir); + return; } + await removeFolder(cacheDir); + }); +} + +export async function removeFolder(folder: string) { + try { + if (!fs.existsSync(folder)) + return; + console.log(`Removing ${await fs.promises.realpath(folder)}`); + await fs.promises.rm(folder, { recursive: true, force: true }); + } catch { + } +} + +function addFindRelatedTestFilesCommand(program: Command) { + const command = program.command('find-related-test-files [source-files...]', { hidden: true }); + command.description('Returns the list of related tests to the given files'); + command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`); + command.action(async (files, options) => { + await withRunnerAndMutedWrite(options.config, runner => runner.findRelatedTestFiles('in-process', files)); + }); +} + +function addTestServerCommand(program: Command) { + const command = program.command('test-server', { hidden: true }); + command.description('start test server'); + command.action(() => { + void runTestServer(); }); } @@ -114,21 +152,10 @@ Examples: async function runTests(args: string[], opts: { [key: string]: any }) { await startProfiling(); - - // When no --config option is passed, let's look for the config file in the current directory. - const configFileOrDirectory = opts.config ? path.resolve(process.cwd(), opts.config) : process.cwd(); - const resolvedConfigFile = resolveConfigFile(configFileOrDirectory); - if (restartWithExperimentalTsEsm(resolvedConfigFile)) + const config = await loadConfigFromFileRestartIfNeeded(opts.config, overridesFromOptions(opts), opts.deps === false); + if (!config) return; - const overrides = overridesFromOptions(opts); - const configLoader = new ConfigLoader(overrides); - let config: FullConfigInternal; - if (resolvedConfigFile) - config = await configLoader.loadConfigFile(resolvedConfigFile, opts.deps === false); - else - config = await configLoader.loadEmptyConfig(configFileOrDirectory); - config.cliArgs = args; config.cliGrep = opts.grep as string | undefined; config.cliGrepInvert = opts.grepInvert as string | undefined; @@ -149,47 +176,40 @@ async function runTests(args: string[], opts: { [key: string]: any }) { gracefullyProcessExitDoNotHang(exitCode); } -async function listTestFiles(opts: { [key: string]: any }) { +export async function withRunnerAndMutedWrite(configFile: string | undefined, callback: (runner: Runner) => Promise<any>) { // Redefine process.stdout.write in case config decides to pollute stdio. const stdoutWrite = process.stdout.write.bind(process.stdout); - process.stdout.write = (() => {}) as any; - process.stderr.write = (() => {}) as any; - const configFileOrDirectory = opts.config ? path.resolve(process.cwd(), opts.config) : process.cwd(); - const resolvedConfigFile = resolveConfigFile(configFileOrDirectory)!; - if (restartWithExperimentalTsEsm(resolvedConfigFile)) - return; - + process.stdout.write = ((a: any, b: any, c: any) => process.stderr.write(a, b, c)) as any; try { - const configLoader = new ConfigLoader(); - const config = await configLoader.loadConfigFile(resolvedConfigFile); + const config = await loadConfigFromFileRestartIfNeeded(configFile); + if (!config) + return; const runner = new Runner(config); - const report = await runner.listTestFiles(opts.project); - stdoutWrite(JSON.stringify(report), () => { + const result = await callback(runner); + stdoutWrite(JSON.stringify(result, undefined, 2), () => { gracefullyProcessExitDoNotHang(0); }); } catch (e) { const error: TestError = serializeError(e); error.location = prepareErrorStack(e.stack).location; - stdoutWrite(JSON.stringify({ error }), () => { + stdoutWrite(JSON.stringify({ error }, undefined, 2), () => { gracefullyProcessExitDoNotHang(0); }); } } +async function listTestFiles(opts: { [key: string]: any }) { + await withRunnerAndMutedWrite(opts.config, async runner => { + return await runner.listTestFiles(); + }); +} + async function mergeReports(reportDir: string | undefined, opts: { [key: string]: any }) { - let configFile = opts.config; - if (configFile) { - configFile = path.resolve(process.cwd(), configFile); - if (!fs.existsSync(configFile)) - throw new Error(`${configFile} does not exist`); - if (!fs.statSync(configFile).isFile()) - throw new Error(`${configFile} is not a file`); - } - if (restartWithExperimentalTsEsm(configFile)) + const configFile = opts.config; + const config = configFile ? await loadConfigFromFileRestartIfNeeded(configFile) : await loadEmptyConfigForMergeReports(); + if (!config) return; - const configLoader = new ConfigLoader(); - const config = await (configFile ? configLoader.loadConfigFile(configFile) : configLoader.loadEmptyConfig(process.cwd())); const dir = path.resolve(process.cwd(), reportDir || ''); const dirStat = await fs.promises.stat(dir).catch(e => null); if (!dirStat) @@ -270,44 +290,6 @@ function resolveReporter(id: string) { return require.resolve(id, { paths: [process.cwd()] }); } -function restartWithExperimentalTsEsm(configFile: string | null): boolean { - const nodeVersion = +process.versions.node.split('.')[0]; - // New experimental loader is only supported on Node 16+. - if (nodeVersion < 16) - return false; - if (!configFile) - return false; - if (process.env.PW_DISABLE_TS_ESM) - return false; - // Node.js < 20 - if ((globalThis as any).__esmLoaderPortPreV20) { - // clear execArgv after restart, so that childProcess.fork in user code does not inherit our loader. - process.execArgv = execArgvWithoutExperimentalLoaderOptions(); - return false; - } - if (!fileIsModule(configFile)) - return false; - // Node.js < 20 - if (!require('node:module').register) { - const innerProcess = (require('child_process') as typeof import('child_process')).fork(require.resolve('./cli'), process.argv.slice(2), { - env: { - ...process.env, - PW_TS_ESM_LEGACY_LOADER_ON: '1', - }, - execArgv: execArgvWithExperimentalLoaderOptions(), - }); - - innerProcess.on('close', (code: number | null) => { - if (code !== 0 && code !== null) - gracefullyProcessExitDoNotHang(code); - }); - return true; - } - // Nodejs >= 21 - registerESMLoader(); - return false; -} - const kTraceModes: TraceMode[] = ['on', 'off', 'on-first-retry', 'on-all-retries', 'retain-on-failure']; const testOptions: [string, string][] = [ @@ -326,7 +308,7 @@ const testOptions: [string, string][] = [ ['--no-deps', 'Do not run project dependencies'], ['--output <dir>', `Folder for output artifacts (default: "test-results")`], ['--pass-with-no-tests', `Makes test run succeed even if no tests were found`], - ['--project <project-name...>', `Only run tests from the specified list of projects (default: run all projects)`], + ['--project <project-name...>', `Only run tests from the specified list of projects, supports '*' wildcard (default: run all projects)`], ['--quiet', `Suppress stdio`], ['--repeat-each <N>', `Run each test N times (default: 1)`], ['--reporter <reporter>', `Reporter to use, comma-separated, can be ${builtInReporters.map(name => `"${name}"`).join(', ')} (default: "${defaultReporter}")`], @@ -346,5 +328,6 @@ addTestCommand(program); addShowReportCommand(program); addListFilesCommand(program); addMergeReportsCommand(program); - -program.parse(process.argv); +addClearCacheCommand(program); +addFindRelatedTestFilesCommand(program); +addTestServerCommand(program); diff --git a/playwright/packages/playwright/src/reporters/base.ts b/playwright/packages/playwright/src/reporters/base.ts index de5a26640f..91f34faf74 100644 --- a/playwright/packages/playwright/src/reporters/base.ts +++ b/playwright/packages/playwright/src/reporters/base.ts @@ -331,7 +331,7 @@ export function formatFailure(config: FullConfig, test: TestCase, options: {inde const packageManagerCommand = getPackageManagerExecCommand(); resultLines.push(colors.cyan(` Usage:`)); resultLines.push(''); - resultLines.push(colors.cyan(` ${packageManagerCommand} playwright show-trace ${relativePath}`)); + resultLines.push(colors.cyan(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`)); resultLines.push(''); } } else { @@ -373,6 +373,12 @@ export function formatFailure(config: FullConfig, test: TestCase, options: {inde }; } +function quotePathIfNeeded(path: string): string { + if (/\s/.test(path)) + return `"${path}"`; + return path; +} + export function formatResultFailure(test: TestCase, result: TestResult, initialIndent: string, highlightCode: boolean): ErrorDetails[] { const errorDetails: ErrorDetails[] = []; diff --git a/playwright/packages/playwright/src/reporters/html.ts b/playwright/packages/playwright/src/reporters/html.ts index dbebacc176..3f21de3cde 100644 --- a/playwright/packages/playwright/src/reporters/html.ts +++ b/playwright/packages/playwright/src/reporters/html.ts @@ -20,11 +20,10 @@ import fs from 'fs'; import path from 'path'; import type { TransformCallback } from 'stream'; import { Transform } from 'stream'; -import { toPosixPath } from './json'; import { codeFrameColumns } from '../transform/babelBundle'; import type { FullResult, FullConfig, Location, Suite, TestCase as TestCasePublic, TestResult as TestResultPublic, TestStep as TestStepPublic, TestError } from '../../types/testReporter'; import type { SuitePrivate } from '../../types/reporterPrivate'; -import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders, sanitizeForFilePath } from 'playwright-core/lib/utils'; +import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders, sanitizeForFilePath, toPosixPath } from 'playwright-core/lib/utils'; import { colors, formatError, formatResultFailure, stripAnsiEscapes } from './base'; import { resolveReporterOutputPath } from '../util'; import type { Metadata } from '../../types/test'; @@ -182,6 +181,7 @@ export async function showHTMLReport(reportFolder: string | undefined, host: str console.log(colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`)); if (testId) url += `#?testId=${testId}`; + url = url.replace('0.0.0.0', 'localhost'); await open(url, { wait: true }).catch(() => {}); await new Promise(() => {}); } @@ -361,7 +361,9 @@ class HtmlBuilder { botName, location, duration, - annotations: test.annotations, + // Annotations can be pushed directly, with a wrong type. + annotations: test.annotations.map(a => ({ type: a.type, description: a.description ? String(a.description) : a.description })), + tags: test.tags, outcome: test.outcome(), path, results, @@ -374,7 +376,9 @@ class HtmlBuilder { botName, location, duration, - annotations: test.annotations, + // Annotations can be pushed directly, with a wrong type. + annotations: test.annotations.map(a => ({ type: a.type, description: a.description ? String(a.description) : a.description })), + tags: test.tags, outcome: test.outcome(), path, ok: test.outcome() === 'expected' || test.outcome() === 'flaky', diff --git a/playwright/packages/playwright/src/reporters/json.ts b/playwright/packages/playwright/src/reporters/json.ts index d677079ab5..36ba678258 100644 --- a/playwright/packages/playwright/src/reporters/json.ts +++ b/playwright/packages/playwright/src/reporters/json.ts @@ -18,15 +18,10 @@ import fs from 'fs'; import path from 'path'; import type { FullConfig, TestCase, Suite, TestResult, TestError, TestStep, FullResult, Location, JSONReport, JSONReportSuite, JSONReportSpec, JSONReportTest, JSONReportTestResult, JSONReportTestStep, JSONReportError } from '../../types/testReporter'; import { formatError, prepareErrorStack } from './base'; -import { MultiMap } from 'playwright-core/lib/utils'; -import { assert } from 'playwright-core/lib/utils'; +import { MultiMap, assert, toPosixPath } from 'playwright-core/lib/utils'; import { getProjectId } from '../common/config'; import EmptyReporter from './empty'; -export function toPosixPath(aPath: string): string { - return aPath.split(path.sep).join(path.posix.sep); -} - class JSONReporter extends EmptyReporter { config!: FullConfig; suite!: Suite; @@ -174,7 +169,7 @@ class JSONReporter extends EmptyReporter { return { title: test.title, ok: test.ok(), - tags: (test.title.match(/@[\S]+/g) || []).map(t => t.substring(1)), + tags: test.tags.map(tag => tag.substring(1)), // Strip '@'. tests: [this._serializeTest(projectId, projectName, test)], id: test.id, ...this._relativeLocation(test.location), diff --git a/playwright/packages/playwright/src/reporters/merge.ts b/playwright/packages/playwright/src/reporters/merge.ts index f206c592e8..9e81f92e7a 100644 --- a/playwright/packages/playwright/src/reporters/merge.ts +++ b/playwright/packages/playwright/src/reporters/merge.ts @@ -26,12 +26,14 @@ import { Multiplexer } from './multiplexer'; import { ZipFile, calculateSha1 } from 'playwright-core/lib/utils'; import { currentBlobReportVersion, type BlobReportMetadata } from './blob'; import { relativeFilePath } from '../util'; +import type { TestError } from '../../types/testReporter'; type StatusCallback = (message: string) => void; type ReportData = { eventPatchers: JsonEventPatchers; reportFile: string; + metadata: BlobReportMetadata; }; export async function createMergedReport(config: FullConfigInternal, dir: string, reporterDescriptions: ReporterDescription[], rootDirOverride: string | undefined) { @@ -65,11 +67,13 @@ export async function createMergedReport(config: FullConfigInternal, dir: string }; await dispatchEvents(eventData.prologue); - for (const { reportFile, eventPatchers } of eventData.reports) { + for (const { reportFile, eventPatchers, metadata } of eventData.reports) { const reportJsonl = await fs.promises.readFile(reportFile); const events = parseTestEvents(reportJsonl); new JsonStringInternalizer(stringPool).traverse(events); eventPatchers.patchers.push(new AttachmentPathPatcher(dir)); + if (metadata.name) + eventPatchers.patchers.push(new GlobalErrorPatcher(metadata.name)); eventPatchers.patchEvents(events); await dispatchEvents(events); } @@ -213,6 +217,7 @@ async function mergeEvents(dir: string, shardReportFiles: string[], stringPool: reports.push({ eventPatchers, reportFile: localPath, + metadata, }); } @@ -465,6 +470,24 @@ class PathSeparatorPatcher { } } +class GlobalErrorPatcher { + private _prefix: string; + + constructor(botName: string) { + this._prefix = `(${botName}) `; + } + + patchEvent(event: JsonEvent) { + if (event.method !== 'onError') + return; + const error = event.params.error as TestError; + if (error.message !== undefined) + error.message = this._prefix + error.message; + if (error.stack !== undefined) + error.stack = this._prefix + error.stack; + } +} + interface JsonEventPatcher { patchEvent(event: JsonEvent): void; } diff --git a/playwright/packages/playwright/src/reporters/teleEmitter.ts b/playwright/packages/playwright/src/reporters/teleEmitter.ts index 113856ce62..7f4caf6ad5 100644 --- a/playwright/packages/playwright/src/reporters/teleEmitter.ts +++ b/playwright/packages/playwright/src/reporters/teleEmitter.ts @@ -202,6 +202,7 @@ export class TeleReporterEmitter implements ReporterV2 { title: test.title, location: this._relativeLocation(test.location), retries: test.retries, + tags: test.tags, }; } diff --git a/playwright/packages/playwright/src/runner/DEPS.list b/playwright/packages/playwright/src/runner/DEPS.list index eb8f6309e7..828f2307e9 100644 --- a/playwright/packages/playwright/src/runner/DEPS.list +++ b/playwright/packages/playwright/src/runner/DEPS.list @@ -8,3 +8,4 @@ ../util.ts ../utilsBundle.ts ../isomorphic/folders.ts +../fsWatcher.ts diff --git a/playwright/packages/playwright/src/runner/dispatcher.ts b/playwright/packages/playwright/src/runner/dispatcher.ts index 6fb315114e..da2b759aa6 100644 --- a/playwright/packages/playwright/src/runner/dispatcher.ts +++ b/playwright/packages/playwright/src/runner/dispatcher.ts @@ -97,7 +97,7 @@ export class Dispatcher { // 2. Start the worker if it is down. let startError; if (!worker) { - worker = this._createWorker(job, index, serializeConfig(this._config)); + worker = this._createWorker(job, index, serializeConfig(this._config, true)); this._workerSlots[index].worker = worker; worker.on('exit', () => this._workerSlots[index].worker = undefined); startError = await worker.start(); @@ -243,6 +243,7 @@ class JobDispatcher { private _listeners: RegisteredListener[] = []; private _failedTests = new Set<TestCase>(); + private _failedWithNonRetriableError = new Set<TestCase|Suite>(); private _remainingByTestId = new Map<string, TestCase>(); private _dataByTestId = new Map<string, { test: TestCase, result: TestResult, steps: Map<string, TestStep> }>(); private _parallelIndex = 0; @@ -293,10 +294,20 @@ class JobDispatcher { const isFailure = result.status !== 'skipped' && result.status !== test.expectedStatus; if (isFailure) this._failedTests.add(test); + if (params.hasNonRetriableError) + this._addNonretriableTestAndSerialModeParents(test); this._reportTestEnd(test, result); this._currentlyRunning = undefined; } + private _addNonretriableTestAndSerialModeParents(test: TestCase) { + this._failedWithNonRetriableError.add(test); + for (let parent: Suite | undefined = test.parent; parent; parent = parent.parent) { + if (parent._parallelMode === 'serial') + this._failedWithNonRetriableError.add(parent); + } + } + private _onStepBegin(params: StepBeginPayload) { const data = this._dataByTestId.get(params.testId); if (!data) { @@ -435,6 +446,8 @@ class JobDispatcher { const serialSuitesWithFailures = new Set<Suite>(); for (const failedTest of this._failedTests) { + if (this._failedWithNonRetriableError.has(failedTest)) + continue; retryCandidates.add(failedTest); let outermostSerialSuite: Suite | undefined; @@ -442,7 +455,7 @@ class JobDispatcher { if (parent._parallelMode === 'serial') outermostSerialSuite = parent; } - if (outermostSerialSuite) + if (outermostSerialSuite && !this._failedWithNonRetriableError.has(outermostSerialSuite)) serialSuitesWithFailures.add(outermostSerialSuite); } diff --git a/playwright/packages/playwright/src/runner/loadUtils.ts b/playwright/packages/playwright/src/runner/loadUtils.ts index f75aca8fb8..f6ecccb3d6 100644 --- a/playwright/packages/playwright/src/runner/loadUtils.ts +++ b/playwright/packages/playwright/src/runner/loadUtils.ts @@ -217,15 +217,12 @@ function createProjectSuite(project: FullProjectInternal, fileSuites: Suite[]): const grepMatcher = createTitleMatcher(project.project.grep); const grepInvertMatcher = project.project.grepInvert ? createTitleMatcher(project.project.grepInvert) : null; - - const titleMatcher = (test: TestCase) => { - const grepTitle = test.titlePath().join(' '); + filterTestsRemoveEmptySuites(projectSuite, (test: TestCase) => { + const grepTitle = test._grepTitle(); if (grepInvertMatcher?.(grepTitle)) return false; return grepMatcher(grepTitle); - }; - - filterTestsRemoveEmptySuites(projectSuite, titleMatcher); + }); return projectSuite; } @@ -239,10 +236,11 @@ function filterProjectSuite(projectSuite: Suite, options: { cliFileFilters: Test filterByFocusedLine(result, options.cliFileFilters); if (options.testIdMatcher) filterByTestIds(result, options.testIdMatcher); - const titleMatcher = (test: TestCase) => { - return !options.cliTitleMatcher || options.cliTitleMatcher(test.titlePath().join(' ')); - }; - filterTestsRemoveEmptySuites(result, titleMatcher); + filterTestsRemoveEmptySuites(result, (test: TestCase) => { + if (options.cliTitleMatcher && !options.cliTitleMatcher(test._grepTitle())) + return false; + return true; + }); return result; } @@ -318,8 +316,8 @@ export function loadGlobalHook(config: FullConfigInternal, file: string): Promis return requireOrImportDefaultFunction(path.resolve(config.config.rootDir, file), false); } -export function loadReporter(config: FullConfigInternal, file: string): Promise<new (arg?: any) => Reporter> { - return requireOrImportDefaultFunction(path.resolve(config.config.rootDir, file), true); +export function loadReporter(config: FullConfigInternal | undefined, file: string): Promise<new (arg?: any) => Reporter> { + return requireOrImportDefaultFunction(config ? path.resolve(config.config.rootDir, file) : file, true); } function sourceMapSources(file: string, cache: Map<string, string[]>): string[] { diff --git a/playwright/packages/playwright/src/runner/loaderHost.ts b/playwright/packages/playwright/src/runner/loaderHost.ts index 0b2bac5dad..cc311e5f6e 100644 --- a/playwright/packages/playwright/src/runner/loaderHost.ts +++ b/playwright/packages/playwright/src/runner/loaderHost.ts @@ -59,7 +59,7 @@ export class OutOfProcessLoaderHost { } async start(errors: TestError[]) { - const startError = await this._processHost.startRunner(serializeConfig(this._config)); + const startError = await this._processHost.startRunner(serializeConfig(this._config, false)); if (startError) { errors.push({ message: `Test loader process failed to start with code "${startError.code}" and signal "${startError.signal}"`, diff --git a/playwright/packages/playwright/src/runner/projectUtils.ts b/playwright/packages/playwright/src/runner/projectUtils.ts index 4469be3744..49a6c89d84 100644 --- a/playwright/packages/playwright/src/runner/projectUtils.ts +++ b/playwright/packages/playwright/src/runner/projectUtils.ts @@ -16,6 +16,7 @@ import fs from 'fs'; import path from 'path'; +import { escapeRegExp } from 'playwright-core/lib/utils'; import { minimatch } from 'playwright-core/lib/utilsBundle'; import { promisify } from 'util'; import type { FullProjectInternal } from '../common/config'; @@ -24,28 +25,52 @@ import { createFileMatcher } from '../util'; const readFileAsync = promisify(fs.readFile); const readDirAsync = promisify(fs.readdir); +function wildcardPatternToRegExp(pattern: string): RegExp { + return new RegExp('^' + pattern.split('*').map(escapeRegExp).join('.*') + '$', 'ig'); +} + export function filterProjects(projects: FullProjectInternal[], projectNames?: string[]): FullProjectInternal[] { if (!projectNames) return [...projects]; - const projectsToFind = new Set<string>(); - const unknownProjects = new Map<string, string>(); - projectNames.forEach(n => { - const name = n.toLocaleLowerCase(); - projectsToFind.add(name); - unknownProjects.set(name, n); - }); + + const projectNamesToFind = new Set<string>(); + const unmatchedProjectNames = new Map<string, string>(); + const patterns = new Set<RegExp>(); + for (const name of projectNames!) { + const lowerCaseName = name.toLocaleLowerCase(); + if (lowerCaseName.includes('*')) { + patterns.add(wildcardPatternToRegExp(lowerCaseName)); + } else { + projectNamesToFind.add(lowerCaseName); + unmatchedProjectNames.set(lowerCaseName, name); + } + } + const result = projects.filter(project => { - const name = project.project.name.toLocaleLowerCase(); - unknownProjects.delete(name); - return projectsToFind.has(name); + const lowerCaseName = project.project.name.toLocaleLowerCase(); + if (projectNamesToFind.has(lowerCaseName)) { + unmatchedProjectNames.delete(lowerCaseName); + return true; + } + for (const regex of patterns) { + regex.lastIndex = 0; + if (regex.test(lowerCaseName)) + return true; + } + return false; }); - if (unknownProjects.size) { - const names = projects.map(p => p.project.name).filter(name => !!name); - if (!names.length) - throw new Error(`No named projects are specified in the configuration file`); - const unknownProjectNames = Array.from(unknownProjects.values()).map(n => `"${n}"`).join(', '); - throw new Error(`Project(s) ${unknownProjectNames} not found. Available named projects: ${names.map(name => `"${name}"`).join(', ')}`); + + if (unmatchedProjectNames.size) { + const unknownProjectNames = Array.from(unmatchedProjectNames.values()).map(n => `"${n}"`).join(', '); + throw new Error(`Project(s) ${unknownProjectNames} not found. Available projects: ${projects.map(p => `"${p.project.name}"`).join(', ')}`); } + + if (!result.length) { + const allProjects = projects.map(p => `"${p.project.name}"`).join(', '); + throw new Error(`No projects matched. Available projects: ${allProjects}`); + } + + return result; } diff --git a/playwright/packages/playwright/src/runner/reporters.ts b/playwright/packages/playwright/src/runner/reporters.ts index 808f33fe47..2285e9bee2 100644 --- a/playwright/packages/playwright/src/runner/reporters.ts +++ b/playwright/packages/playwright/src/runner/reporters.ts @@ -48,6 +48,8 @@ export async function createReporters(config: FullConfigInternal, mode: 'list' | }; const reporters: ReporterV2[] = []; descriptions ??= config.config.reporter; + if (config.configCLIOverrides.additionalReporters) + descriptions = [...descriptions, ...config.configCLIOverrides.additionalReporters]; for (const r of descriptions) { const [name, arg] = r; const options = { ...arg, configDir: config.configDir }; diff --git a/playwright/packages/playwright/src/runner/runner.ts b/playwright/packages/playwright/src/runner/runner.ts index 3592afb701..142f0958cd 100644 --- a/playwright/packages/playwright/src/runner/runner.ts +++ b/playwright/packages/playwright/src/runner/runner.ts @@ -15,8 +15,9 @@ * limitations under the License. */ +import path from 'path'; import { monotonicTime } from 'playwright-core/lib/utils'; -import type { FullResult } from '../../types/testReporter'; +import type { FullResult, TestError } from '../../types/testReporter'; import { webServerPluginsForConfig } from '../plugins/webServerPlugin'; import { collectFilesForProject, filterProjects } from './projectUtils'; import { createReporters } from './reporters'; @@ -27,6 +28,9 @@ import { runWatchModeLoop } from './watchMode'; import { runUIMode } from './uiMode'; import { InternalReporter } from '../reporters/internalReporter'; import { Multiplexer } from '../reporters/multiplexer'; +import type { Suite } from '../common/test'; +import { wrapReporterAsV2 } from '../reporters/reporterV2'; +import { affectedTestFiles } from '../transform/compilationCache'; type ProjectConfigWithFiles = { name: string; @@ -37,6 +41,13 @@ type ProjectConfigWithFiles = { type ConfigListFilesReport = { projects: ProjectConfigWithFiles[]; + cliEntryPoint?: string; + error?: TestError; +}; + +export type FindRelatedTestFilesReport = { + testFiles: string[]; + errors?: TestError[]; }; export class Runner { @@ -46,10 +57,12 @@ export class Runner { this._config = config; } - async listTestFiles(projectNames: string[] | undefined): Promise<any> { - const projects = filterProjects(this._config.projects, projectNames); + async listTestFiles(): Promise<ConfigListFilesReport> { + const frameworkPackage = (this._config.config as any)['@playwright/test']?.['packageJSON']; + const projects = filterProjects(this._config.projects); const report: ConfigListFilesReport = { - projects: [] + projects: [], + cliEntryPoint: frameworkPackage ? path.join(path.dirname(frameworkPackage), 'cli.js') : undefined, }; for (const project of projects) { report.projects.push({ @@ -104,6 +117,29 @@ export class Runner { return status; } + async loadAllTests(mode: 'in-process' | 'out-of-process' = 'in-process'): Promise<{ status: FullResult['status'], suite?: Suite, errors: TestError[] }> { + const config = this._config; + const errors: TestError[] = []; + const reporter = new InternalReporter(new Multiplexer([wrapReporterAsV2({ + onError(error: TestError) { + errors.push(error); + } + })])); + const taskRunner = createTaskRunnerForList(config, reporter, mode, { failOnLoadErrors: true }); + const testRun = new TestRun(config, reporter); + reporter.onConfigure(config.config); + + const taskStatus = await taskRunner.run(testRun, 0); + let status: FullResult['status'] = testRun.failureTracker.result(); + if (status === 'passed' && taskStatus !== 'passed') + status = taskStatus; + const modifiedResult = await reporter.onEnd({ status }); + if (modifiedResult && modifiedResult.status) + status = modifiedResult.status; + await reporter.onExit(); + return { status, suite: testRun.rootSuite, errors }; + } + async watchAllTests(): Promise<FullResult['status']> { const config = this._config; webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p })); @@ -115,4 +151,16 @@ export class Runner { webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p })); return await runUIMode(config, options); } + + async findRelatedTestFiles(mode: 'in-process' | 'out-of-process', files: string[]): Promise<FindRelatedTestFilesReport> { + const result = await this.loadAllTests(mode); + if (result.status !== 'passed' || !result.suite) + return { errors: result.errors, testFiles: [] }; + + const resolvedFiles = (files as string[]).map(file => path.resolve(process.cwd(), file)); + const override = (this._config.config as any)['@playwright/test']?.['cli']?.['find-related-test-files']; + if (override) + return await override(resolvedFiles, this._config.config, this._config.configDir, result.suite); + return { testFiles: affectedTestFiles(resolvedFiles) }; + } } diff --git a/playwright/packages/playwright/src/runner/tasks.ts b/playwright/packages/playwright/src/runner/tasks.ts index 7f874aef4e..1cd57034e4 100644 --- a/playwright/packages/playwright/src/runner/tasks.ts +++ b/playwright/packages/playwright/src/runner/tasks.ts @@ -29,7 +29,7 @@ import type { FullConfigInternal, FullProjectInternal } from '../common/config'; import { collectProjectsAndTestFiles, createRootSuite, loadFileSuites, loadGlobalHook } from './loadUtils'; import type { Matcher } from '../util'; import type { Suite } from '../common/test'; -import { buildDependentProjects, buildTeardownToSetupsMap } from './projectUtils'; +import { buildDependentProjects, buildTeardownToSetupsMap, filterProjects } from './projectUtils'; import { FailureTracker } from './failureTracker'; const readDirAsync = promisify(fs.readdir); @@ -83,12 +83,21 @@ export function createTaskRunnerForWatch(config: FullConfigInternal, reporter: R return taskRunner; } +export function createTaskRunnerForTestServer(config: FullConfigInternal, reporter: ReporterV2): TaskRunner<TestRun> { + const taskRunner = new TaskRunner<TestRun>(reporter, 0); + addGlobalSetupTasks(taskRunner, config); + taskRunner.addTask('load tests', createLoadTask('out-of-process', { filterOnly: true, failOnLoadErrors: false, doNotRunTestsOutsideProjectFilter: true })); + addRunTasks(taskRunner, config); + return taskRunner; +} + function addGlobalSetupTasks(taskRunner: TaskRunner<TestRun>, config: FullConfigInternal) { + if (!config.configCLIOverrides.preserveOutputDir && !process.env.PW_TEST_NO_REMOVE_OUTPUT_DIRS) + taskRunner.addTask('clear output', createRemoveOutputDirsTask()); for (const plugin of config.plugins) taskRunner.addTask('plugin setup', createPluginSetupTask(plugin)); if (config.config.globalSetup || config.config.globalTeardown) taskRunner.addTask('global setup', createGlobalSetupTask()); - taskRunner.addTask('clear output', createRemoveOutputDirsTask()); } function addRunTasks(taskRunner: TaskRunner<TestRun>, config: FullConfigInternal) { @@ -165,13 +174,9 @@ function createGlobalSetupTask(): Task<TestRun> { function createRemoveOutputDirsTask(): Task<TestRun> { return { setup: async ({ config }) => { - if (process.env.PW_TEST_NO_REMOVE_OUTPUT_DIRS) - return; const outputDirs = new Set<string>(); - for (const p of config.projects) { - if (!config.cliProjectFilter || config.cliProjectFilter.includes(p.project.name)) - outputDirs.add(p.project.outputDir); - } + const projects = filterProjects(config.projects, config.cliProjectFilter); + projects.forEach(p => outputDirs.add(p.project.outputDir)); await Promise.all(Array.from(outputDirs).map(outputDir => removeFolders([outputDir]).then(async ([error]) => { if (!error) diff --git a/playwright/packages/playwright/src/runner/testServer.ts b/playwright/packages/playwright/src/runner/testServer.ts new file mode 100644 index 0000000000..c21733b9d5 --- /dev/null +++ b/playwright/packages/playwright/src/runner/testServer.ts @@ -0,0 +1,234 @@ +/** + * 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 http from 'http'; +import path from 'path'; +import { ManualPromise, createGuid, gracefullyProcessExitDoNotHang } from 'playwright-core/lib/utils'; +import { WSServer } from 'playwright-core/lib/utils'; +import type { WebSocket } from 'playwright-core/lib/utilsBundle'; +import type { FullResult, TestError } from 'playwright/types/testReporter'; +import { loadConfig, restartWithExperimentalTsEsm } from '../common/configLoader'; +import { InternalReporter } from '../reporters/internalReporter'; +import { Multiplexer } from '../reporters/multiplexer'; +import { createReporters } from './reporters'; +import { TestRun, createTaskRunnerForList, createTaskRunnerForTestServer } from './tasks'; +import type { ConfigCLIOverrides } from '../common/ipc'; +import { Runner } from './runner'; +import type { FindRelatedTestFilesReport } from './runner'; +import type { FullConfigInternal } from '../common/config'; +import type { TestServerInterface } from './testServerInterface'; +import { serializeError } from '../util'; +import { prepareErrorStack } from '../reporters/base'; +import { loadReporter } from './loadUtils'; +import { wrapReporterAsV2 } from '../reporters/reporterV2'; + +export async function runTestServer() { + if (restartWithExperimentalTsEsm(undefined, true)) + return null; + process.env.PW_TEST_HTML_REPORT_OPEN = 'never'; + const wss = new WSServer({ + onConnection(request: http.IncomingMessage, url: URL, ws: WebSocket, id: string) { + const dispatcher = new Dispatcher(ws); + ws.on('message', async message => { + const { id, method, params } = JSON.parse(String(message)); + try { + const result = await (dispatcher as any)[method](params); + ws.send(JSON.stringify({ id, result })); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } + }); + return { + async close() {} + }; + }, + }); + const url = await wss.listen(0, 'localhost', '/' + createGuid()); + // eslint-disable-next-line no-console + process.on('exit', () => wss.close().catch(console.error)); + // eslint-disable-next-line no-console + console.log(`Listening on ${url}`); + process.stdin.on('close', () => gracefullyProcessExitDoNotHang(0)); +} + +class Dispatcher implements TestServerInterface { + private _testRun: { run: Promise<FullResult['status']>, stop: ManualPromise<void> } | undefined; + private _ws: WebSocket; + + constructor(ws: WebSocket) { + this._ws = ws; + + process.stdout.write = ((chunk: string | Buffer, cb?: Buffer | Function, cb2?: Function) => { + this._dispatchEvent('stdio', chunkToPayload('stdout', chunk)); + if (typeof cb === 'function') + (cb as any)(); + if (typeof cb2 === 'function') + (cb2 as any)(); + return true; + }) as any; + process.stderr.write = ((chunk: string | Buffer, cb?: Buffer | Function, cb2?: Function) => { + this._dispatchEvent('stdio', chunkToPayload('stderr', chunk)); + if (typeof cb === 'function') + (cb as any)(); + if (typeof cb2 === 'function') + (cb2 as any)(); + return true; + }) as any; + } + + async listFiles(params: { + configFile: string; + }): Promise<{ + projects: { + name: string; + testDir: string; + use: { testIdAttribute?: string }; + files: string[]; + }[]; + cliEntryPoint?: string; + error?: TestError; + }> { + try { + const config = await this._loadConfig(params.configFile); + const runner = new Runner(config); + return runner.listTestFiles(); + } catch (e) { + const error: TestError = serializeError(e); + error.location = prepareErrorStack(e.stack).location; + return { projects: [], error }; + } + } + + async listTests(params: { + configFile: string; + locations: string[]; + reporter: string; + env: NodeJS.ProcessEnv; + }) { + const config = await this._loadConfig(params.configFile); + config.cliArgs = params.locations || []; + const wireReporter = await this._createReporter(params.reporter); + const reporter = new InternalReporter(new Multiplexer([wireReporter])); + const taskRunner = createTaskRunnerForList(config, reporter, 'out-of-process', { failOnLoadErrors: true }); + const testRun = new TestRun(config, reporter); + reporter.onConfigure(config.config); + + const taskStatus = await taskRunner.run(testRun, 0); + let status: FullResult['status'] = testRun.failureTracker.result(); + if (status === 'passed' && taskStatus !== 'passed') + status = taskStatus; + const modifiedResult = await reporter.onEnd({ status }); + if (modifiedResult && modifiedResult.status) + status = modifiedResult.status; + await reporter.onExit(); + } + + async test(params: { + configFile: string; + locations: string[]; + reporter: string; + env: NodeJS.ProcessEnv; + headed?: boolean; + oneWorker?: boolean; + trace?: 'on' | 'off'; + projects?: string[]; + grep?: string; + reuseContext?: boolean; + connectWsEndpoint?: string; + }) { + await this._stopTests(); + + const overrides: ConfigCLIOverrides = { + repeatEach: 1, + retries: 0, + preserveOutputDir: true, + use: { + trace: params.trace, + headless: params.headed ? false : undefined, + _optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined, + _optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : undefined, + }, + workers: params.oneWorker ? 1 : undefined, + }; + + const config = await this._loadConfig(params.configFile, overrides); + config.cliListOnly = false; + config.cliArgs = params.locations || []; + config.cliGrep = params.grep; + config.cliProjectFilter = params.projects?.length ? params.projects : undefined; + + const wireReporter = await this._createReporter(params.reporter); + const configReporters = await createReporters(config, 'run'); + const reporter = new InternalReporter(new Multiplexer([...configReporters, wireReporter])); + const taskRunner = createTaskRunnerForTestServer(config, reporter); + const testRun = new TestRun(config, reporter); + reporter.onConfigure(config.config); + const stop = new ManualPromise(); + const run = taskRunner.run(testRun, 0, stop).then(async status => { + await reporter.onEnd({ status }); + await reporter.onExit(); + this._testRun = undefined; + return status; + }); + this._testRun = { run, stop }; + await run; + } + + async findRelatedTestFiles(params: { + configFile: string; + files: string[]; + }): Promise<FindRelatedTestFilesReport> { + const config = await this._loadConfig(params.configFile); + const runner = new Runner(config); + return runner.findRelatedTestFiles('out-of-process', params.files); + } + + async stop(params: { + configFile: string; + }) { + await this._stopTests(); + } + + async closeGracefully() { + gracefullyProcessExitDoNotHang(0); + } + + private async _stopTests() { + this._testRun?.stop?.resolve(); + await this._testRun?.run; + } + + private _dispatchEvent(method: string, params: any) { + this._ws.send(JSON.stringify({ method, params })); + } + + private async _loadConfig(configFile: string, overrides?: ConfigCLIOverrides): Promise<FullConfigInternal> { + return loadConfig({ resolvedConfigFile: configFile, configDir: path.dirname(configFile) }, overrides); + } + + private async _createReporter(file: string) { + const reporterConstructor = await loadReporter(undefined, file); + const instance = new reporterConstructor((message: any) => this._dispatchEvent('report', message)); + return wrapReporterAsV2(instance); + } +} + +function chunkToPayload(type: 'stdout' | 'stderr', chunk: Buffer | string) { + if (chunk instanceof Buffer) + return { type, buffer: chunk.toString('base64') }; + return { type, text: chunk }; +} diff --git a/playwright/packages/playwright/src/runner/testServerInterface.ts b/playwright/packages/playwright/src/runner/testServerInterface.ts new file mode 100644 index 0000000000..1e844d8123 --- /dev/null +++ b/playwright/packages/playwright/src/runner/testServerInterface.ts @@ -0,0 +1,67 @@ +/** + * 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 { TestError } from '../../types/testReporter'; + +export interface TestServerInterface { + listFiles(params: { + configFile: string; + }): Promise<{ + projects: { + name: string; + testDir: string; + use: { testIdAttribute?: string }; + files: string[]; + }[]; + cliEntryPoint?: string; + error?: TestError; + }>; + + listTests(params: { + configFile: string; + locations: string[]; + reporter: string; + }): Promise<void>; + + test(params: { + configFile: string; + locations: string[]; + reporter: string; + headed?: boolean; + oneWorker?: boolean; + trace?: 'on' | 'off'; + projects?: string[]; + grep?: string; + reuseContext?: boolean; + connectWsEndpoint?: string; + }): Promise<void>; + + findRelatedTestFiles(params: { + configFile: string; + files: string[]; + }): Promise<{ testFiles: string[]; errors?: TestError[]; }>; + + stop(params: { + configFile: string; + }): Promise<void>; + + closeGracefully(): Promise<void>; +} + +export interface TestServerEvents { + on(event: 'report', listener: (params: any) => void): void; + on(event: 'stdio', listener: (params: { type: 'stdout' | 'stderr', text?: string, buffer?: string }) => void): void; +} diff --git a/playwright/packages/playwright/src/runner/uiMode.ts b/playwright/packages/playwright/src/runner/uiMode.ts index 922aeb2968..d0071e14f1 100644 --- a/playwright/packages/playwright/src/runner/uiMode.ts +++ b/playwright/packages/playwright/src/runner/uiMode.ts @@ -17,19 +17,18 @@ import { openTraceViewerApp, openTraceInBrowser, registry } from 'playwright-core/lib/server'; import { isUnderTest, ManualPromise } from 'playwright-core/lib/utils'; import type { FullResult } from '../../types/testReporter'; -import { clearCompilationCache, collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache'; +import { collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache'; import type { FullConfigInternal } from '../common/config'; import { InternalReporter } from '../reporters/internalReporter'; import { TeleReporterEmitter } from '../reporters/teleEmitter'; import { createReporters } from './reporters'; import { TestRun, createTaskRunnerForList, createTaskRunnerForWatch, createTaskRunnerForWatchSetup } from './tasks'; -import { chokidar } from '../utilsBundle'; -import type { FSWatcher } from 'chokidar'; import { open } from 'playwright-core/lib/utilsBundle'; import ListReporter from '../reporters/list'; import type { OpenTraceViewerOptions, Transport } from 'playwright-core/lib/server/trace/viewer/traceViewer'; import { Multiplexer } from '../reporters/multiplexer'; import { SigIntWatcher } from './sigIntWatcher'; +import { Watcher } from '../fsWatcher'; class UIMode { private _config: FullConfigInternal; @@ -173,7 +172,6 @@ class UIMode { this._config.testIdMatcher = undefined; const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process', { failOnLoadErrors: false }); const testRun = new TestRun(this._config, reporter); - clearCompilationCache(); reporter.onConfigure(this._config.config); const status = await taskRunner.run(testRun, 0); await reporter.onEnd({ status }); @@ -201,7 +199,6 @@ class UIMode { const reporter = new InternalReporter(new Multiplexer(reporters)); const taskRunner = createTaskRunnerForWatch(this._config, reporter); const testRun = new TestRun(this._config, reporter); - clearCompilationCache(); reporter.onConfigure(this._config.config); const stop = new ManualPromise(); const run = taskRunner.run(testRun, 0, stop).then(async status => { @@ -258,59 +255,6 @@ function chunkToPayload(type: 'stdout' | 'stderr', chunk: Buffer | string): Stdi return { type, text: chunk }; } -type FSEvent = { event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', file: string }; - -class Watcher { - private _onChange: (events: FSEvent[]) => void; - private _watchedFiles: string[] = []; - private _ignoredFolders: string[] = []; - private _collector: FSEvent[] = []; - private _fsWatcher: FSWatcher | undefined; - private _throttleTimer: NodeJS.Timeout | undefined; - private _mode: 'flat' | 'deep'; - - constructor(mode: 'flat' | 'deep', onChange: (events: FSEvent[]) => void) { - this._mode = mode; - this._onChange = onChange; - } - - update(watchedFiles: string[], ignoredFolders: string[], reportPending: boolean) { - if (JSON.stringify([this._watchedFiles, this._ignoredFolders]) === JSON.stringify(watchedFiles, ignoredFolders)) - return; - - if (reportPending) - this._reportEventsIfAny(); - - this._watchedFiles = watchedFiles; - this._ignoredFolders = ignoredFolders; - void this._fsWatcher?.close(); - this._fsWatcher = undefined; - this._collector.length = 0; - clearTimeout(this._throttleTimer); - this._throttleTimer = undefined; - - if (!this._watchedFiles.length) - return; - - this._fsWatcher = chokidar.watch(watchedFiles, { ignoreInitial: true, ignored: this._ignoredFolders }).on('all', async (event, file) => { - if (this._throttleTimer) - clearTimeout(this._throttleTimer); - if (this._mode === 'flat' && event !== 'add' && event !== 'change') - return; - if (this._mode === 'deep' && event !== 'add' && event !== 'change' && event !== 'unlink' && event !== 'addDir' && event !== 'unlinkDir') - return; - this._collector.push({ event, file }); - this._throttleTimer = setTimeout(() => this._reportEventsIfAny(), 250); - }); - } - - private _reportEventsIfAny() { - if (this._collector.length) - this._onChange(this._collector.slice()); - this._collector.length = 0; - } -} - function hasSomeBrowsers(): boolean { for (const browserName of ['chromium', 'webkit', 'firefox']) { try { diff --git a/playwright/packages/playwright/src/runner/watchMode.ts b/playwright/packages/playwright/src/runner/watchMode.ts index 575653d3e2..5758bf673e 100644 --- a/playwright/packages/playwright/src/runner/watchMode.ts +++ b/playwright/packages/playwright/src/runner/watchMode.ts @@ -22,7 +22,7 @@ import { createFileMatcher, createFileMatcherFromArguments } from '../util'; import type { Matcher } from '../util'; import { TestRun, createTaskRunnerForWatch, createTaskRunnerForWatchSetup } from './tasks'; import { buildProjectsClosure, filterProjects } from './projectUtils'; -import { clearCompilationCache, collectAffectedTestFiles } from '../transform/compilationCache'; +import { collectAffectedTestFiles } from '../transform/compilationCache'; import type { FullResult } from '../../types/testReporter'; import { chokidar } from '../utilsBundle'; import type { FSWatcher as CFSWatcher } from 'chokidar'; @@ -283,7 +283,6 @@ async function runTests(config: FullConfigInternal, failedTestIdCollector: Set<s const reporter = new InternalReporter(new ListReporter()); const taskRunner = createTaskRunnerForWatch(config, reporter, options?.additionalFileMatcher); const testRun = new TestRun(config, reporter); - clearCompilationCache(); reporter.onConfigure(config.config); const taskStatus = await taskRunner.run(testRun, 0); let status: FullResult['status'] = 'passed'; @@ -418,15 +417,20 @@ async function toggleShowBrowser(config: FullConfigInternal, originalWorkers: nu config.config.workers = 1; showBrowserServer = new PlaywrightServer({ mode: 'extension', path: '/' + createGuid(), maxConnections: 1 }); const wsEndpoint = await showBrowserServer.listen(); - process.env.PW_TEST_REUSE_CONTEXT = '1'; - process.env.PW_TEST_CONNECT_WS_ENDPOINT = wsEndpoint; + config.configCLIOverrides.use = { + ...config.configCLIOverrides.use, + _optionContextReuseMode: 'when-possible', + _optionConnectOptions: { wsEndpoint }, + }; process.stdout.write(`${colors.dim('Show & reuse browser:')} ${colors.bold('on')}\n`); } else { config.config.workers = originalWorkers; + if (config.configCLIOverrides.use) { + delete config.configCLIOverrides.use._optionContextReuseMode; + delete config.configCLIOverrides.use._optionConnectOptions; + } await showBrowserServer?.close(); showBrowserServer = undefined; - delete process.env.PW_TEST_REUSE_CONTEXT; - delete process.env.PW_TEST_CONNECT_WS_ENDPOINT; process.stdout.write(`${colors.dim('Show & reuse browser:')} ${colors.bold('off')}\n`); } } diff --git a/playwright/packages/playwright/src/store.ts b/playwright/packages/playwright/src/store.ts deleted file mode 100644 index ceb5bd85c7..0000000000 --- a/playwright/packages/playwright/src/store.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright Microsoft Corporation. All rights reserved. - * - * 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 fs from 'fs'; -import path from 'path'; -import { currentConfig } from './common/globals'; -import { mime } from 'playwright-core/lib/utilsBundle'; -import { isJsonMimeType, isString, isTextualMimeType } from 'playwright-core/lib/utils'; - -class JsonStore { - async delete(name: string) { - const file = this.path(name); - await fs.promises.rm(file, { force: true }); - } - - async get<T>(name: string) { - const file = this.path(name); - try { - const type = contentType(name); - if (type === 'binary') - return await fs.promises.readFile(file) as T; - const text = await fs.promises.readFile(file, 'utf-8'); - if (type === 'json') - return JSON.parse(text) as T; - return text as T; - } catch (e) { - return undefined; - } - } - - path(name: string): string { - return path.join(this.root(), name); - } - - root(): string { - const config = currentConfig(); - if (!config) - throw new Error('Cannot access store before config is loaded'); - return config.storeDir; - } - - async set<T>(name: string, value: T | undefined) { - const file = this.path(name); - if (value === undefined) { - await fs.promises.rm(file, { force: true }); - return; - } - let data: string | Buffer = ''; - switch (contentType(name)) { - case 'json': { - if (Buffer.isBuffer(value)) - throw new Error('JSON value must be an Object'); - data = JSON.stringify(value, undefined, 2); - break; - } - case 'text': { - if (!isString(value)) - throw new Error('Textual value must be a string'); - data = value as string; - break; - } - case 'binary': { - if (!Buffer.isBuffer(value)) - throw new Error('Binary value must be a Buffer'); - data = value; - break; - } - } - await fs.promises.mkdir(path.dirname(file), { recursive: true }); - await fs.promises.writeFile(file, data); - } -} - -function contentType(name: string): 'json'|'text'|'binary' { - const mimeType = mime.getType(path.basename(name)) ?? 'application/octet-string'; - if (isJsonMimeType(mimeType)) - return 'json'; - if (isTextualMimeType(mimeType)) - return 'text'; - return 'binary'; -} - -export const store = new JsonStore(); diff --git a/playwright/packages/playwright/src/third_party/tsconfig-loader.ts b/playwright/packages/playwright/src/third_party/tsconfig-loader.ts index d925d588e9..ffe8af1a00 100644 --- a/playwright/packages/playwright/src/third_party/tsconfig-loader.ts +++ b/playwright/packages/playwright/src/third_party/tsconfig-loader.ts @@ -31,7 +31,7 @@ import { json5 } from '../utilsBundle'; /** * Typing for the parts of tsconfig that we care about */ -interface Tsconfig { +interface TsConfig { extends?: string; compilerOptions?: { baseUrl?: string; @@ -39,55 +39,29 @@ interface Tsconfig { strict?: boolean; allowJs?: boolean; }; - references?: any[]; + references?: { path: string }[]; } -export interface TsConfigLoaderResult { - tsConfigPath: string | undefined; - baseUrl: string | undefined; - paths: { [key: string]: Array<string> } | undefined; - serialized: string | undefined; - allowJs: boolean; +export interface LoadedTsConfig { + tsConfigPath: string; + baseUrl?: string; + paths?: { [key: string]: Array<string> }; + allowJs?: boolean; } export interface TsConfigLoaderParams { cwd: string; } -export function tsConfigLoader({ - cwd, -}: TsConfigLoaderParams): TsConfigLoaderResult { - const loadResult = loadSyncDefault(cwd); - loadResult.serialized = JSON.stringify(loadResult); - return loadResult; -} - -function loadSyncDefault( - cwd: string, -): TsConfigLoaderResult { - // Tsconfig.loadSync uses path.resolve. This is why we can use an absolute path as filename - +export function tsConfigLoader({ cwd, }: TsConfigLoaderParams): LoadedTsConfig[] { const configPath = resolveConfigPath(cwd); - if (!configPath) { - return { - tsConfigPath: undefined, - baseUrl: undefined, - paths: undefined, - serialized: undefined, - allowJs: false, - }; - } - const config = loadTsconfig(configPath); - - return { - tsConfigPath: configPath, - baseUrl: - (config && config.compilerOptions && config.compilerOptions.baseUrl), - paths: config && config.compilerOptions && config.compilerOptions.paths, - serialized: undefined, - allowJs: !!config?.compilerOptions?.allowJs, - }; + if (!configPath) + return []; + + const references: LoadedTsConfig[] = []; + const config = loadTsConfig(configPath, references); + return [config, ...references]; } function resolveConfigPath(cwd: string): string | undefined { @@ -122,79 +96,64 @@ export function walkForTsConfig( return walkForTsConfig(parentDirectory, existsSync); } -function loadTsconfig( +function resolveConfigFile(baseConfigFile: string, referencedConfigFile: string) { + if (!referencedConfigFile.endsWith('.json')) + referencedConfigFile += '.json'; + const currentDir = path.dirname(baseConfigFile); + let resolvedConfigFile = path.resolve(currentDir, referencedConfigFile); + if (referencedConfigFile.indexOf('/') !== -1 && referencedConfigFile.indexOf('.') !== -1 && !fs.existsSync(referencedConfigFile)) + resolvedConfigFile = path.join(currentDir, 'node_modules', referencedConfigFile); + return resolvedConfigFile; +} + +function loadTsConfig( configFilePath: string, -): Tsconfig | undefined { - if (!fs.existsSync(configFilePath)) { - return undefined; - } + references: LoadedTsConfig[], + visited = new Map<string, LoadedTsConfig>(), +): LoadedTsConfig { + if (visited.has(configFilePath)) + return visited.get(configFilePath)!; + + let result: LoadedTsConfig = { + tsConfigPath: configFilePath, + }; + visited.set(configFilePath, result); + + if (!fs.existsSync(configFilePath)) + return result; const configString = fs.readFileSync(configFilePath, 'utf-8'); const cleanedJson = StripBom(configString); - const parsedConfig: Tsconfig = json5.parse(cleanedJson); - - let config: Tsconfig = {}; + const parsedConfig: TsConfig = json5.parse(cleanedJson); const extendsArray = Array.isArray(parsedConfig.extends) ? parsedConfig.extends : (parsedConfig.extends ? [parsedConfig.extends] : []); - for (let extendedConfig of extendsArray) { - if ( - typeof extendedConfig === "string" && - extendedConfig.indexOf(".json") === -1 - ) { - extendedConfig += ".json"; - } - const currentDir = path.dirname(configFilePath); - let extendedConfigPath = path.join(currentDir, extendedConfig); - if ( - extendedConfig.indexOf("/") !== -1 && - extendedConfig.indexOf(".") !== -1 && - !fs.existsSync(extendedConfigPath) - ) { - extendedConfigPath = path.join( - currentDir, - "node_modules", - extendedConfig - ); - } - - const base = - loadTsconfig(extendedConfigPath) || {}; + for (const extendedConfig of extendsArray) { + const extendedConfigPath = resolveConfigFile(configFilePath, extendedConfig); + const base = loadTsConfig(extendedConfigPath, references, visited); // baseUrl should be interpreted as relative to the base tsconfig, // but we need to update it so it is relative to the original tsconfig being loaded - if (base.compilerOptions && base.compilerOptions.baseUrl) { + if (base.baseUrl && base.baseUrl) { const extendsDir = path.dirname(extendedConfig); - base.compilerOptions.baseUrl = path.join( - extendsDir, - base.compilerOptions.baseUrl - ); + base.baseUrl = path.join(extendsDir, base.baseUrl); } - - config = mergeConfigs(config, base); + result = { ...result, ...base, tsConfigPath: configFilePath }; } - config = mergeConfigs(config, parsedConfig); - // The only top-level property that is excluded from inheritance is "references". - // https://www.typescriptlang.org/tsconfig#extends - config.references = parsedConfig.references; + const loadedConfig = Object.fromEntries(Object.entries({ + baseUrl: parsedConfig.compilerOptions?.baseUrl, + paths: parsedConfig.compilerOptions?.paths, + allowJs: parsedConfig?.compilerOptions?.allowJs, + }).filter(([, value]) => value !== undefined)); - if (path.basename(configFilePath) === 'jsconfig.json' && config.compilerOptions?.allowJs === undefined) { - config.compilerOptions = config.compilerOptions || {}; - config.compilerOptions.allowJs = true; - } + result = { ...result, ...loadedConfig }; - return config; -} + for (const ref of parsedConfig.references || []) + references.push(loadTsConfig(resolveConfigFile(configFilePath, ref.path), references, visited)); -function mergeConfigs(base: Tsconfig, override: Tsconfig): Tsconfig { - return { - ...base, - ...override, - compilerOptions: { - ...base.compilerOptions, - ...override.compilerOptions, - }, - }; + if (path.basename(configFilePath) === 'jsconfig.json' && result.allowJs === undefined) + result.allowJs = true; + return result; } function StripBom(string: string) { diff --git a/playwright/packages/playwright/src/transform/compilationCache.ts b/playwright/packages/playwright/src/transform/compilationCache.ts index c1e97bddb7..caa4b08d23 100644 --- a/playwright/packages/playwright/src/transform/compilationCache.ts +++ b/playwright/packages/playwright/src/transform/compilationCache.ts @@ -47,14 +47,14 @@ type SerializedCompilationCache = { // - For workers-only dynamic imports or some cache problems, we will re-transpile files in // each worker anew. -const cacheDir = process.env.PWTEST_CACHE_DIR || (() => { +export const cacheDir = process.env.PWTEST_CACHE_DIR || (() => { if (process.platform === 'win32') return path.join(os.tmpdir(), `playwright-transform-cache`); // Use `geteuid()` instead of more natural `os.userInfo().username` // since `os.userInfo()` is not always available. // Note: `process.geteuid()` is not available on windows. // See https://github.com/microsoft/playwright/issues/22721 - return path.join(os.tmpdir(), `playwright-transform-cache-` + process.geteuid()); + return path.join(os.tmpdir(), `playwright-transform-cache-` + process.geteuid?.()); })(); const sourceMaps: Map<string, string> = new Map(); @@ -92,12 +92,24 @@ export function installSourceMapSupportIfNeeded() { }); } -function _innerAddToCompilationCache(filename: string, entry: MemoryCache) { +function _innerAddToCompilationCacheAndSerialize(filename: string, entry: MemoryCache) { sourceMaps.set(entry.moduleUrl || filename, entry.sourceMapPath); memoryCache.set(filename, entry); + return { + sourceMaps: [[entry.moduleUrl || filename, entry.sourceMapPath]], + memoryCache: [[filename, entry]], + fileDependencies: [], + externalDependencies: [], + }; } -export function getFromCompilationCache(filename: string, hash: string, moduleUrl?: string): { cachedCode?: string, addToCache?: (code: string, map: any | undefined | null, data: Map<string, any>) => void } { +type CompilationCacheLookupResult = { + serializedCache?: any; + cachedCode?: string; + addToCache?: (code: string, map: any | undefined | null, data: Map<string, any>) => { serializedCache?: any }; +}; + +export function getFromCompilationCache(filename: string, hash: string, moduleUrl?: string): CompilationCacheLookupResult { // First check the memory cache by filename, this cache will always work in the worker, // because we just compiled this file in the loader. const cache = memoryCache.get(filename); @@ -116,22 +128,23 @@ export function getFromCompilationCache(filename: string, hash: string, moduleUr const dataPath = cachePath + '.data'; try { const cachedCode = fs.readFileSync(codePath, 'utf8'); - _innerAddToCompilationCache(filename, { codePath, sourceMapPath, dataPath, moduleUrl }); - return { cachedCode }; + const serializedCache = _innerAddToCompilationCacheAndSerialize(filename, { codePath, sourceMapPath, dataPath, moduleUrl }); + return { cachedCode, serializedCache }; } catch { } return { addToCache: (code: string, map: any | undefined | null, data: Map<string, any>) => { if (isWorkerProcess()) - return; + return {}; fs.mkdirSync(path.dirname(cachePath), { recursive: true }); if (map) fs.writeFileSync(sourceMapPath, JSON.stringify(map), 'utf8'); if (data.size) fs.writeFileSync(dataPath, JSON.stringify(Object.fromEntries(data.entries()), undefined, 2), 'utf8'); fs.writeFileSync(codePath, code, 'utf8'); - _innerAddToCompilationCache(filename, { codePath, sourceMapPath, dataPath, moduleUrl }); + const serializedCache = _innerAddToCompilationCacheAndSerialize(filename, { codePath, sourceMapPath, dataPath, moduleUrl }); + return { serializedCache }; } }; } @@ -145,11 +158,6 @@ export function serializeCompilationCache(): SerializedCompilationCache { }; } -export function clearCompilationCache() { - sourceMaps.clear(); - memoryCache.clear(); -} - export function addToCompilationCache(payload: any) { for (const entry of payload.sourceMaps) sourceMaps.set(entry[0], entry[1]); @@ -200,7 +208,8 @@ export function fileDependenciesForTest() { } export function collectAffectedTestFiles(dependency: string, testFileCollector: Set<string>) { - testFileCollector.add(dependency); + if (fileDependencies.has(dependency)) + testFileCollector.add(dependency); for (const [testFile, deps] of fileDependencies) { if (deps.has(dependency)) testFileCollector.add(testFile); @@ -211,6 +220,13 @@ export function collectAffectedTestFiles(dependency: string, testFileCollector: } } +export function affectedTestFiles(changes: string[]): string[] { + const result = new Set<string>(); + for (const change of changes) + collectAffectedTestFiles(change, result); + return [...result]; +} + export function internalDependenciesForTestFile(filename: string): Set<string> | undefined{ return fileDependencies.get(filename); } diff --git a/playwright/packages/playwright/src/transform/esmLoader.ts b/playwright/packages/playwright/src/transform/esmLoader.ts index 4a02b8511e..13c99f1f7f 100644 --- a/playwright/packages/playwright/src/transform/esmLoader.ts +++ b/playwright/packages/playwright/src/transform/esmLoader.ts @@ -30,6 +30,8 @@ async function resolve(specifier: string, context: { parentURL?: string }, defau specifier = url.pathToFileURL(resolved).toString(); } const result = await defaultResolve(specifier, context, defaultResolve); + // Note: we collect dependencies here that will be sent to the main thread + // (and optionally runner process) after the loading finishes. if (result?.url && result.url.startsWith('file://')) currentFileDepsCollector()?.add(url.fileURLToPath(result.url)); @@ -54,14 +56,15 @@ async function load(moduleUrl: string, context: { format?: string }, defaultLoad return defaultLoad(moduleUrl, context, defaultLoad); const code = fs.readFileSync(filename, 'utf-8'); - const source = transformHook(code, filename, moduleUrl); + const transformed = transformHook(code, filename, moduleUrl); - // Flush the source maps to the main thread. - await transport?.send('pushToCompilationCache', { cache: serializeCompilationCache() }); + // Flush the source maps to the main thread, so that errors during import() are source-mapped. + if (transformed.serializedCache) + await transport?.send('pushToCompilationCache', { cache: transformed.serializedCache }); // Output format is always the same as input format, if it was unknown, we always report modules. // shortCircuit is required by Node >= 18.6 to designate no more loaders should be called. - return { format: context.format || 'module', source, shortCircuit: true }; + return { format: context.format || 'module', source: transformed.code, shortCircuit: true }; } let transport: PortTransport | undefined; diff --git a/playwright/packages/playwright/src/transform/portTransport.ts b/playwright/packages/playwright/src/transform/portTransport.ts index 027e6b6e2c..a99bf76c6c 100644 --- a/playwright/packages/playwright/src/transform/portTransport.ts +++ b/playwright/packages/playwright/src/transform/portTransport.ts @@ -21,7 +21,7 @@ export class PortTransport { constructor(port: MessagePort, handler: (method: string, params: any) => Promise<any>) { this._port = port; - port.onmessage = async event => { + port.addEventListener('message', async event => { const message = event.data; const { id, ackId, method, params, result } = message; if (id) { @@ -36,7 +36,7 @@ export class PortTransport { callback?.(result); return; } - }; + }); } async send(method: string, params: any) { diff --git a/playwright/packages/playwright/src/transform/transform.ts b/playwright/packages/playwright/src/transform/transform.ts index 29b8b78a2b..82b5acdda1 100644 --- a/playwright/packages/playwright/src/transform/transform.ts +++ b/playwright/packages/playwright/src/transform/transform.ts @@ -19,7 +19,7 @@ import path from 'path'; import url from 'url'; import { sourceMapSupport, pirates } from '../utilsBundle'; import type { Location } from '../../types/testReporter'; -import type { TsConfigLoaderResult } from '../third_party/tsconfig-loader'; +import type { LoadedTsConfig } from '../third_party/tsconfig-loader'; import { tsConfigLoader } from '../third_party/tsconfig-loader'; import Module from 'module'; import type { BabelPlugin, BabelTransformFunction } from './babelBundle'; @@ -34,7 +34,7 @@ type ParsedTsConfigData = { paths: { key: string, values: string[] }[]; allowJs: boolean; }; -const cachedTSConfigs = new Map<string, ParsedTsConfigData | undefined>(); +const cachedTSConfigs = new Map<string, ParsedTsConfigData[]>(); export type TransformConfig = { babelPlugins: [string, any?][]; @@ -57,9 +57,7 @@ export function transformConfig(): TransformConfig { return _transformConfig; } -function validateTsConfig(tsconfig: TsConfigLoaderResult): ParsedTsConfigData | undefined { - if (!tsconfig.tsConfigPath) - return; +function validateTsConfig(tsconfig: LoadedTsConfig): ParsedTsConfigData { // Make 'baseUrl' absolute, because it is relative to the tsconfig.json, not to cwd. // When no explicit baseUrl is set, resolve paths relative to the tsconfig file. // See https://www.typescriptlang.org/tsconfig#paths @@ -67,21 +65,19 @@ function validateTsConfig(tsconfig: TsConfigLoaderResult): ParsedTsConfigData | // Only add the catch-all mapping when baseUrl is specified const pathsFallback = tsconfig.baseUrl ? [{ key: '*', values: ['*'] }] : []; return { - allowJs: tsconfig.allowJs, + allowJs: !!tsconfig.allowJs, absoluteBaseUrl, paths: Object.entries(tsconfig.paths || {}).map(([key, values]) => ({ key, values })).concat(pathsFallback) }; } -function loadAndValidateTsconfigForFile(file: string): ParsedTsConfigData | undefined { +function loadAndValidateTsconfigsForFile(file: string): ParsedTsConfigData[] { const cwd = path.dirname(file); if (!cachedTSConfigs.has(cwd)) { - const loaded = tsConfigLoader({ - cwd - }); - cachedTSConfigs.set(cwd, validateTsConfig(loaded)); + const loaded = tsConfigLoader({ cwd }); + cachedTSConfigs.set(cwd, loaded.map(validateTsConfig)); } - return cachedTSConfigs.get(cwd); + return cachedTSConfigs.get(cwd)!; } const pathSeparator = process.platform === 'win32' ? ';' : ':'; @@ -97,8 +93,10 @@ export function resolveHook(filename: string, specifier: string): string | undef return resolveImportSpecifierExtension(path.resolve(path.dirname(filename), specifier)); const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx'); - const tsconfig = loadAndValidateTsconfigForFile(filename); - if (tsconfig && (isTypeScript || tsconfig.allowJs)) { + const tsconfigs = loadAndValidateTsconfigsForFile(filename); + for (const tsconfig of tsconfigs) { + if (!isTypeScript && !tsconfig.allowJs) + continue; let longestPrefixLength = -1; let pathMatchedByLongestPrefix: string | undefined; @@ -134,7 +132,7 @@ export function resolveHook(filename: string, specifier: string): string | undef let candidate = value; if (value.includes('*')) candidate = candidate.replace('*', matchedPartOfSpecifier); - candidate = path.resolve(tsconfig.absoluteBaseUrl, candidate.replace(/\//g, path.sep)); + candidate = path.resolve(tsconfig.absoluteBaseUrl, candidate); const existing = resolveImportSpecifierExtension(candidate); if (existing) { longestPrefixLength = keyPrefix.length; @@ -165,7 +163,7 @@ export function setTransformData(pluginName: string, value: any) { transformData.set(pluginName, value); } -export function transformHook(originalCode: string, filename: string, moduleUrl?: string): string { +export function transformHook(originalCode: string, filename: string, moduleUrl?: string): { code: string, serializedCache?: any } { const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx') || filename.endsWith('.mts') || filename.endsWith('.cts'); const hasPreprocessor = process.env.PW_TEST_SOURCE_TRANSFORM && @@ -174,9 +172,9 @@ export function transformHook(originalCode: string, filename: string, moduleUrl? const pluginsPrologue = _transformConfig.babelPlugins; const pluginsEpilogue = hasPreprocessor ? [[process.env.PW_TEST_SOURCE_TRANSFORM!]] as BabelPlugin[] : []; const hash = calculateHash(originalCode, filename, !!moduleUrl, pluginsPrologue, pluginsEpilogue); - const { cachedCode, addToCache } = getFromCompilationCache(filename, hash, moduleUrl); + const { cachedCode, addToCache, serializedCache } = getFromCompilationCache(filename, hash, moduleUrl); if (cachedCode !== undefined) - return cachedCode; + return { code: cachedCode, serializedCache }; // We don't use any browserslist data, but babel checks it anyway. // Silence the annoying warning. @@ -185,9 +183,10 @@ export function transformHook(originalCode: string, filename: string, moduleUrl? const { babelTransform }: { babelTransform: BabelTransformFunction } = require('./babelBundle'); transformData = new Map<string, any>(); const { code, map } = babelTransform(originalCode, filename, isTypeScript, !!moduleUrl, pluginsPrologue, pluginsEpilogue); - if (code) - addToCache!(code, map, transformData); - return code || ''; + if (!code) + return { code: '', serializedCache }; + const added = addToCache!(code, map, transformData); + return { code, serializedCache: added.serializedCache }; } function calculateHash(content: string, filePath: string, isModule: boolean, pluginsPrologue: BabelPlugin[], pluginsEpilogue: BabelPlugin[]): string { @@ -241,7 +240,7 @@ function installTransform(): () => void { const revertPirates = pirates.addHook((code: string, filename: string) => { if (!shouldTransform(filename)) return code; - return transformHook(code, filename); + return transformHook(code, filename).code; }, { exts: ['.ts', '.tsx', '.js', '.jsx', '.mjs'] }); return () => { diff --git a/playwright/packages/playwright/src/util.ts b/playwright/packages/playwright/src/util.ts index 17e406d514..505a59c6b6 100644 --- a/playwright/packages/playwright/src/util.ts +++ b/playwright/packages/playwright/src/util.ts @@ -15,12 +15,11 @@ */ import fs from 'fs'; -import { mime } from 'playwright-core/lib/utilsBundle'; import type { StackFrame } from '@protocol/channels'; import util from 'util'; import path from 'path'; import url from 'url'; -import { debug, minimatch, parseStackTraceLine } from 'playwright-core/lib/utilsBundle'; +import { debug, mime, minimatch, parseStackTraceLine } from 'playwright-core/lib/utilsBundle'; import { formatCallLog } from 'playwright-core/lib/utils'; import type { TestInfoError } from './../types/test'; import type { Location } from './../types/testReporter'; @@ -31,13 +30,14 @@ const PLAYWRIGHT_TEST_PATH = path.join(__dirname, '..'); const PLAYWRIGHT_CORE_PATH = path.dirname(require.resolve('playwright-core/package.json')); export function filterStackTrace(e: Error): { message: string, stack: string } { + const name = e.name ? e.name + ': ' : ''; if (process.env.PWDEBUGIMPL) - return { message: e.name + ': ' + e.message, stack: e.stack || '' }; + return { message: name + e.message, stack: e.stack || '' }; const stackLines = stringifyStackFrames(filteredStackTrace(e.stack?.split('\n') || [])); return { - message: e.name + ': ' + e.message, - stack: `${e.name}: ${e.message}\n${stackLines.join('\n')}` + message: name + e.message, + stack: `${name}${e.message}${stackLines.map(line => '\n' + line).join('')}` }; } @@ -208,7 +208,8 @@ export function addSuffixToFilePath(filePath: string, suffix: string, customExte */ export function getContainedPath(parentPath: string, subPath: string = ''): string | null { const resolvedPath = path.resolve(parentPath, subPath); - if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + path.sep)) return resolvedPath; + if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + path.sep)) + return resolvedPath; return null; } diff --git a/playwright/packages/playwright/src/worker/fixtureRunner.ts b/playwright/packages/playwright/src/worker/fixtureRunner.ts index 520921722d..85c2dddab9 100644 --- a/playwright/packages/playwright/src/worker/fixtureRunner.ts +++ b/playwright/packages/playwright/src/worker/fixtureRunner.ts @@ -15,9 +15,9 @@ */ import { formatLocation, debugTest, filterStackFile } from '../util'; -import { ManualPromise, zones } from 'playwright-core/lib/utils'; -import type { TestInfoImpl, TestStepInternal } from './testInfo'; -import type { FixtureDescription, TimeoutManager } from './timeoutManager'; +import { ManualPromise } from 'playwright-core/lib/utils'; +import type { TestInfoImpl } from './testInfo'; +import type { FixtureDescription } from './timeoutManager'; import { fixtureParameterNames, type FixturePool, type FixtureRegistration, type FixtureScope } from '../common/fixtures'; import type { WorkerInfo } from '../../types/test'; import type { Location } from '../../types/testReporter'; @@ -28,10 +28,13 @@ class Fixture { value: any; failed = false; - _useFuncFinished: ManualPromise<void> | undefined; - _selfTeardownComplete: Promise<void> | undefined; - _teardownWithDepsComplete: Promise<void> | undefined; - _runnableDescription: FixtureDescription; + private _useFuncFinished: ManualPromise<void> | undefined; + private _selfTeardownComplete: Promise<void> | undefined; + private _teardownWithDepsComplete: Promise<void> | undefined; + private _setupDescription: FixtureDescription; + private _teardownDescription: FixtureDescription; + private _shouldGenerateStep = false; + private _isInternalFixture = false; _deps = new Set<Fixture>(); _usages = new Set<Fixture>(); @@ -40,7 +43,7 @@ class Fixture { this.registration = registration; this.value = null; const title = this.registration.customTitle || this.registration.name; - this._runnableDescription = { + this._setupDescription = { title, phase: 'setup', location: registration.location, @@ -49,18 +52,46 @@ class Fixture { elapsed: 0, } }; + this._teardownDescription = { ...this._setupDescription, phase: 'teardown' }; + this._shouldGenerateStep = !this.registration.hideStep && !this.registration.name.startsWith('_') && !this.registration.option; + this._isInternalFixture = this.registration.location && filterStackFile(this.registration.location.file); } async setup(testInfo: TestInfoImpl) { + this.runner.instanceForId.set(this.registration.id, this); + if (typeof this.registration.fn !== 'function') { this.value = this.registration.fn; return; } + testInfo._timeoutManager.setCurrentFixture(this._setupDescription); + const beforeStep = this._shouldGenerateStep ? testInfo._addStep({ + title: `fixture: ${this.registration.name}`, + category: 'fixture', + location: this._isInternalFixture ? this.registration.location : undefined, + wallTime: Date.now(), + }) : undefined; + try { + await this._setupInternal(testInfo); + beforeStep?.complete({}); + } catch (error) { + beforeStep?.complete({ error }); + throw error; + } finally { + testInfo._timeoutManager.setCurrentFixture(undefined); + } + } + + private async _setupInternal(testInfo: TestInfoImpl) { const params: { [key: string]: any } = {}; for (const name of this.registration.deps) { - const registration = this.runner.pool!.resolveDependency(this.registration, name)!; - const dep = await this.runner.setupFixtureForRegistration(registration, testInfo); + const registration = this.runner.pool!.resolve(name, this.registration)!; + const dep = this.runner.instanceForId.get(registration.id); + if (!dep) { + this.failed = true; + return; + } // Fixture teardown is root => leafs, when we need to teardown a fixture, // it recursively tears down its usages first. dep._usages.add(this); @@ -74,13 +105,6 @@ class Fixture { } } - // Break the registration function into before/after steps. Create these before/after stacks - // w/o scopes, and create single mutable step that will be converted into the after step. - const shouldGenerateStep = !this.registration.hideStep && !this.registration.name.startsWith('_') && !this.registration.option; - const isInternalFixture = this.registration.location && filterStackFile(this.registration.location.file); - let mutableStepOnStack: TestStepInternal | undefined; - let afterStep: TestStepInternal | undefined; - let called = false; const useFuncStarted = new ManualPromise<void>(); debugTest(`setup ${this.registration.name}`); @@ -92,99 +116,58 @@ class Fixture { this._useFuncFinished = new ManualPromise<void>(); useFuncStarted.resolve(); await this._useFuncFinished; - - if (shouldGenerateStep) { - afterStep = testInfo._addStep({ - wallTime: Date.now(), - title: `fixture: ${this.registration.name}`, - category: 'fixture', - location: isInternalFixture ? this.registration.location : undefined, - }, testInfo._afterHooksStep); - mutableStepOnStack!.stepId = afterStep.stepId; - } }; const workerInfo: WorkerInfo = { config: testInfo.config, parallelIndex: testInfo.parallelIndex, workerIndex: testInfo.workerIndex, project: testInfo.project }; const info = this.registration.scope === 'worker' ? workerInfo : testInfo; - testInfo._timeoutManager.setCurrentFixture(this._runnableDescription); - - const handleError = (e: any) => { - this.failed = true; - if (!useFuncStarted.isDone()) - useFuncStarted.reject(e); - else - throw e; - }; - try { - const result = zones.preserve(async () => { - if (!shouldGenerateStep) - return await this.registration.fn(params, useFunc, info); - - await testInfo._runAsStep({ - title: `fixture: ${this.registration.name}`, - category: 'fixture', - location: isInternalFixture ? this.registration.location : undefined, - }, async step => { - mutableStepOnStack = step; - return await this.registration.fn(params, useFunc, info); - }); - }); - - if (result instanceof Promise) - this._selfTeardownComplete = result.catch(handleError); - else - this._selfTeardownComplete = Promise.resolve(); - } catch (e) { - handleError(e); - } + this._selfTeardownComplete = (async () => { + try { + await this.registration.fn(params, useFunc, info); + } catch (error) { + this.failed = true; + if (!useFuncStarted.isDone()) + useFuncStarted.reject(error); + else + throw error; + } + })(); await useFuncStarted; - if (shouldGenerateStep) { - mutableStepOnStack?.complete({}); - this._selfTeardownComplete?.then(() => { - afterStep?.complete({}); - }).catch(e => { - afterStep?.complete({ error: e }); - }); - } - testInfo._timeoutManager.setCurrentFixture(undefined); } - async teardown(timeoutManager: TimeoutManager) { - if (this._teardownWithDepsComplete) { - // When we are waiting for the teardown for the second time, - // most likely after the first time did timeout, annotate current fixture - // for better error messages. - this._setTeardownDescription(timeoutManager); + async teardown(testInfo: TestInfoImpl) { + const afterStep = this._shouldGenerateStep ? testInfo?._addStep({ + wallTime: Date.now(), + title: `fixture: ${this.registration.name}`, + category: 'fixture', + location: this._isInternalFixture ? this.registration.location : undefined, + }) : undefined; + testInfo._timeoutManager.setCurrentFixture(this._teardownDescription); + try { + if (!this._teardownWithDepsComplete) + this._teardownWithDepsComplete = this._teardownInternal(); await this._teardownWithDepsComplete; - timeoutManager.setCurrentFixture(undefined); - return; + afterStep?.complete({}); + } catch (error) { + afterStep?.complete({ error }); + throw error; + } finally { + testInfo._timeoutManager.setCurrentFixture(undefined); } - this._teardownWithDepsComplete = this._teardownInternal(timeoutManager); - await this._teardownWithDepsComplete; - } - - private _setTeardownDescription(timeoutManager: TimeoutManager) { - this._runnableDescription.phase = 'teardown'; - timeoutManager.setCurrentFixture(this._runnableDescription); } - private async _teardownInternal(timeoutManager: TimeoutManager) { + private async _teardownInternal() { if (typeof this.registration.fn !== 'function') return; try { - for (const fixture of this._usages) - await fixture.teardown(timeoutManager); if (this._usages.size !== 0) { // TODO: replace with assert. - console.error('Internal error: fixture integrity at', this._runnableDescription.title); // eslint-disable-line no-console + console.error('Internal error: fixture integrity at', this._teardownDescription.title); // eslint-disable-line no-console this._usages.clear(); } if (this._useFuncFinished) { debugTest(`teardown ${this.registration.name}`); - this._setTeardownDescription(timeoutManager); this._useFuncFinished.resolve(); await this._selfTeardownComplete; - timeoutManager.setCurrentFixture(undefined); } } finally { for (const dep of this._deps) @@ -192,6 +175,14 @@ class Fixture { this.runner.instanceForId.delete(this.registration.id); } } + + _collectFixturesInTeardownOrder(scope: FixtureScope, collector: Set<Fixture>) { + if (this.registration.scope !== scope) + return; + for (const fixture of this._usages) + fixture._collectFixturesInTeardownOrder(scope, collector); + collector.add(this); + } } export class FixtureRunner { @@ -213,32 +204,34 @@ export class FixtureRunner { this.pool = pool; } - async teardownScope(scope: FixtureScope, timeoutManager: TimeoutManager) { - let error: Error | undefined; + private _collectFixturesInSetupOrder(registration: FixtureRegistration, collector: Set<FixtureRegistration>) { + if (collector.has(registration)) + return; + for (const name of registration.deps) { + const dep = this.pool!.resolve(name, registration)!; + this._collectFixturesInSetupOrder(dep, collector); + } + collector.add(registration); + } + + async teardownScope(scope: FixtureScope, testInfo: TestInfoImpl, onFixtureError: (error: Error) => void) { // Teardown fixtures in the reverse order. const fixtures = Array.from(this.instanceForId.values()).reverse(); - for (const fixture of fixtures) { - if (fixture.registration.scope === scope) { - try { - await fixture.teardown(timeoutManager); - } catch (e) { - if (error === undefined) - error = e; - } - } - } + const collector = new Set<Fixture>(); + for (const fixture of fixtures) + fixture._collectFixturesInTeardownOrder(scope, collector); + for (const fixture of collector) + await fixture.teardown(testInfo).catch(onFixtureError); if (scope === 'test') this.testScopeClean = true; - if (error !== undefined) - throw error; } async resolveParametersForFunction(fn: Function, testInfo: TestInfoImpl, autoFixtures: 'worker' | 'test' | 'all-hooks-only'): Promise<object | null> { - // Install automatic fixtures. + const collector = new Set<FixtureRegistration>(); + + // Collect automatic fixtures. const auto: FixtureRegistration[] = []; - for (const registration of this.pool!.registrations.values()) { - if (registration.auto === false) - continue; + for (const registration of this.pool!.autoFixtures()) { let shouldRun = true; if (autoFixtures === 'all-hooks-only') shouldRun = registration.scope === 'worker' || registration.auto === 'all-hooks-included'; @@ -248,19 +241,27 @@ export class FixtureRunner { auto.push(registration); } auto.sort((r1, r2) => (r1.scope === 'worker' ? 0 : 1) - (r2.scope === 'worker' ? 0 : 1)); - for (const registration of auto) { - const fixture = await this.setupFixtureForRegistration(registration, testInfo); + for (const registration of auto) + this._collectFixturesInSetupOrder(registration, collector); + + // Collect used fixtures. + const names = getRequiredFixtureNames(fn); + for (const name of names) + this._collectFixturesInSetupOrder(this.pool!.resolve(name)!, collector); + + // Setup fixtures. + for (const registration of collector) { + const fixture = await this._setupFixtureForRegistration(registration, testInfo); if (fixture.failed) return null; } - // Install used fixtures. - const names = getRequiredFixtureNames(fn); + // Create params object. const params: { [key: string]: any } = {}; for (const name of names) { - const registration = this.pool!.registrations.get(name)!; - const fixture = await this.setupFixtureForRegistration(registration, testInfo); - if (fixture.failed) + const registration = this.pool!.resolve(name)!; + const fixture = this.instanceForId.get(registration.id)!; + if (!fixture || fixture.failed) return null; params[name] = fixture.value; } @@ -276,7 +277,7 @@ export class FixtureRunner { return fn(params, testInfo); } - async setupFixtureForRegistration(registration: FixtureRegistration, testInfo: TestInfoImpl): Promise<Fixture> { + private async _setupFixtureForRegistration(registration: FixtureRegistration, testInfo: TestInfoImpl): Promise<Fixture> { if (registration.scope === 'test') this.testScopeClean = false; @@ -285,7 +286,6 @@ export class FixtureRunner { return fixture; fixture = new Fixture(this, registration); - this.instanceForId.set(registration.id, fixture); await fixture.setup(testInfo); return fixture; } @@ -293,7 +293,7 @@ export class FixtureRunner { dependsOnWorkerFixturesOnly(fn: Function, location: Location): boolean { const names = getRequiredFixtureNames(fn, location); for (const name of names) { - const registration = this.pool!.registrations.get(name)!; + const registration = this.pool!.resolve(name)!; if (registration.scope !== 'worker') return false; } diff --git a/playwright/packages/playwright/src/worker/testInfo.ts b/playwright/packages/playwright/src/worker/testInfo.ts index c4777355d7..e79bd61529 100644 --- a/playwright/packages/playwright/src/worker/testInfo.ts +++ b/playwright/packages/playwright/src/worker/testInfo.ts @@ -33,12 +33,11 @@ export interface TestStepInternal { complete(result: { error?: Error, attachments?: Attachment[] }): void; stepId: string; title: string; - category: string; + category: 'hook' | 'fixture' | 'test.step' | string; wallTime: number; location?: Location; boxedStack?: StackFrame[]; steps: TestStepInternal[]; - laxParent?: boolean; endWallTime?: number; apiName?: string; params?: Record<string, any>; @@ -46,29 +45,30 @@ export interface TestStepInternal { infectParentStepsWithError?: boolean; box?: boolean; isSoft?: boolean; + forceNoParent?: boolean; } export class TestInfoImpl implements TestInfo { private _onStepBegin: (payload: StepBeginPayload) => void; private _onStepEnd: (payload: StepEndPayload) => void; private _onAttach: (payload: AttachmentPayload) => void; - readonly _test: TestCase; readonly _timeoutManager: TimeoutManager; readonly _startTime: number; readonly _startWallTime: number; private _hasHardError: boolean = false; - readonly _tracing = new TestTracing(); + readonly _tracing: TestTracing; _didTimeout = false; _wasInterrupted = false; _lastStepId = 0; + private readonly _requireFile: string; readonly _projectInternal: FullProjectInternal; readonly _configInternal: FullConfigInternal; readonly _steps: TestStepInternal[] = []; - _beforeHooksStep: TestStepInternal | undefined; - _afterHooksStep: TestStepInternal | undefined; _onDidFinishTestFunction: (() => Promise<void>) | undefined; + _hasNonRetriableError = false; + // ------------ TestInfo fields ------------ readonly testId: string; readonly repeatEachIndex: number; @@ -88,8 +88,6 @@ export class TestInfoImpl implements TestInfo { readonly annotations: Annotation[] = []; readonly attachments: TestInfo['attachments'] = []; status: TestStatus = 'passed'; - readonly stdout: TestInfo['stdout'] = []; - readonly stderr: TestInfo['stderr'] = []; snapshotSuffix: string = ''; readonly outputDir: string; readonly snapshotDir: string; @@ -131,19 +129,19 @@ export class TestInfoImpl implements TestInfo { configInternal: FullConfigInternal, projectInternal: FullProjectInternal, workerParams: WorkerInitParams, - test: TestCase, + test: TestCase | undefined, retry: number, onStepBegin: (payload: StepBeginPayload) => void, onStepEnd: (payload: StepEndPayload) => void, onAttach: (payload: AttachmentPayload) => void, ) { - this._test = test; - this.testId = test.id; + this.testId = test?.id ?? ''; this._onStepBegin = onStepBegin; this._onStepEnd = onStepEnd; this._onAttach = onAttach; this._startTime = monotonicTime(); this._startWallTime = Date.now(); + this._requireFile = test?._requireFile ?? ''; this.repeatEachIndex = workerParams.repeatEachIndex; this.retry = retry; @@ -153,20 +151,20 @@ export class TestInfoImpl implements TestInfo { this.project = projectInternal.project; this._configInternal = configInternal; this.config = configInternal.config; - this.title = test.title; - this.titlePath = test.titlePath(); - this.file = test.location.file; - this.line = test.location.line; - this.column = test.location.column; - this.fn = test.fn; - this.expectedStatus = test.expectedStatus; + this.title = test?.title ?? ''; + this.titlePath = test?.titlePath() ?? []; + this.file = test?.location.file ?? ''; + this.line = test?.location.line ?? 0; + this.column = test?.location.column ?? 0; + this.fn = test?.fn ?? (() => {}); + this.expectedStatus = test?.expectedStatus ?? 'skipped'; this._timeoutManager = new TimeoutManager(this.project.timeout); this.outputDir = (() => { - const relativeTestFilePath = path.relative(this.project.testDir, test._requireFile.replace(/\.(spec|test)\.(js|ts|mjs)$/, '')); + const relativeTestFilePath = path.relative(this.project.testDir, this._requireFile.replace(/\.(spec|test)\.(js|ts|mjs)$/, '')); const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === 'win32' ? new RegExp('\\\\', 'g') : new RegExp('/', 'g'), '-'); - const fullTitleWithoutSpec = test.titlePath().slice(1).join(' '); + const fullTitleWithoutSpec = this.titlePath.slice(1).join(' '); let testOutputDir = trimLongString(sanitizedRelativePath + '-' + sanitizeForFilePath(fullTitleWithoutSpec)); if (projectInternal.id) @@ -179,7 +177,7 @@ export class TestInfoImpl implements TestInfo { })(); this.snapshotDir = (() => { - const relativeTestFilePath = path.relative(this.project.testDir, test._requireFile); + const relativeTestFilePath = path.relative(this.project.testDir, this._requireFile); return path.join(this.project.snapshotDir, relativeTestFilePath + '-snapshots'); })(); @@ -189,6 +187,8 @@ export class TestInfoImpl implements TestInfo { this._attach(a.name, a); return this.attachments.length; }; + + this._tracing = new TestTracing(this, workerParams.artifactsDir); } private _modifier(type: 'skip' | 'fail' | 'fixme' | 'slow', modifierArgs: [arg?: any, description?: string]) { @@ -224,7 +224,9 @@ export class TestInfoImpl implements TestInfo { // consider it a timeout. if (!this._wasInterrupted && timeoutError && !this._didTimeout) { this._didTimeout = true; - this.errors.push(timeoutError); + const serialized = serializeError(timeoutError); + this.errors.push(serialized); + this._tracing.appendForError(serialized); // Do not overwrite existing failure upon hook/teardown timeout. if (this.status === 'passed' || this.status === 'skipped') this.status = 'timedOut'; @@ -240,30 +242,50 @@ export class TestInfoImpl implements TestInfo { if (this.status === 'passed') this.status = 'skipped'; } else { - this._failWithError(error, true /* isHardError */); + this._failWithError(error, true /* isHardError */, true /* retriable */); return error; } } } - _addStep(data: Omit<TestStepInternal, 'complete' | 'stepId' | 'steps'>, parentStep?: TestStepInternal): TestStepInternal { + private _findLastNonFinishedStep(filter: (step: TestStepInternal) => boolean) { + let result: TestStepInternal | undefined; + const visit = (step: TestStepInternal) => { + if (!step.endWallTime && filter(step)) + result = step; + step.steps.forEach(visit); + }; + this._steps.forEach(visit); + return result; + } + + _addStep(data: Omit<TestStepInternal, 'complete' | 'stepId' | 'steps'>): TestStepInternal { const stepId = `${data.category}@${++this._lastStepId}`; const rawStack = captureRawStack(); - if (!parentStep) - parentStep = zones.zoneData<TestStepInternal>('stepZone', rawStack!) || undefined; - // For out-of-stack calls, locate the enclosing step. - let isLaxParent = false; - if (!parentStep && data.laxParent) { - const visit = (step: TestStepInternal) => { - // Do not nest chains of route.continue. - const shouldNest = step.title !== data.title; - if (!step.endWallTime && shouldNest) - parentStep = step; - step.steps.forEach(visit); - }; - this._steps.forEach(visit); - isLaxParent = !!parentStep; + let parentStep: TestStepInternal | undefined; + if (data.category === 'hook' || data.category === 'fixture') { + // Predefined steps form a fixed hierarchy - find the last non-finished one. + parentStep = this._findLastNonFinishedStep(step => step.category === 'fixture' || step.category === 'hook'); + } else { + parentStep = zones.zoneData<TestStepInternal>('stepZone', rawStack!) || undefined; + if (parentStep?.category === 'hook' || parentStep?.category === 'fixture') { + // Prefer last non-finished predefined step over the on-stack one, because + // some predefined steps may be missing on the stack. + parentStep = this._findLastNonFinishedStep(step => step.category === 'fixture' || step.category === 'hook'); + } else if (!parentStep) { + if (data.category === 'test.step') { + // Nest test.step without a good stack in the last non-finished predefined step like a hook. + parentStep = this._findLastNonFinishedStep(step => step.category === 'fixture' || step.category === 'hook'); + } else { + // Do not nest chains of route.continue. + parentStep = this._findLastNonFinishedStep(step => step.title !== data.title); + } + } + } + if (data.forceNoParent) { + // This is used to reset step hierarchy after test timeout. + parentStep = undefined; } const filteredStack = filteredStackTrace(rawStack); @@ -277,7 +299,6 @@ export class TestInfoImpl implements TestInfo { const step: TestStepInternal = { stepId, ...data, - laxParent: isLaxParent, steps: [], complete: result => { if (step.endWallTime) @@ -307,7 +328,7 @@ export class TestInfoImpl implements TestInfo { } const payload: StepEndPayload = { - testId: this._test.id, + testId: this.testId, stepId, wallTime: step.endWallTime, error: step.error, @@ -317,13 +338,13 @@ export class TestInfoImpl implements TestInfo { this._tracing.appendAfterActionForStep(stepId, errorForTrace, result.attachments); if (step.isSoft && result.error) - this._failWithError(result.error, false /* isHardError */); + this._failWithError(result.error, false /* isHardError */, true /* retriable */); } }; const parentStepList = parentStep ? parentStep.steps : this._steps; parentStepList.push(step); const payload: StepBeginPayload = { - testId: this._test.id, + testId: this.testId, stepId, parentStepId: parentStep ? parentStep.stepId : undefined, title: data.title, @@ -345,7 +366,9 @@ export class TestInfoImpl implements TestInfo { this.status = 'interrupted'; } - _failWithError(error: Error, isHardError: boolean) { + _failWithError(error: Error, isHardError: boolean, retriable: boolean) { + if (!retriable) + this._hasNonRetriableError = true; // Do not overwrite any previous hard errors. // Some (but not all) scenarios include: // - expect() that fails after uncaught exception. @@ -361,6 +384,7 @@ export class TestInfoImpl implements TestInfo { if (step && step.boxedStack) serialized.stack = `${error.name}: ${error.message}\n${stringifyStackFrames(step.boxedStack).join('\n')}`; this.errors.push(serialized); + this._tracing.appendForError(serialized); } async _runAsStepWithRunnable<T>( @@ -407,11 +431,10 @@ export class TestInfoImpl implements TestInfo { title: `attach "${name}"`, category: 'attach', wallTime: Date.now(), - laxParent: true, }); this._attachmentsPush(attachment); this._onAttach({ - testId: this._test.id, + testId: this.testId, name: attachment.name, contentType: attachment.contentType, path: attachment.path, @@ -442,7 +465,7 @@ export class TestInfoImpl implements TestInfo { snapshotPath(...pathSegments: string[]) { const subPath = path.join(...pathSegments); const parsedSubPath = path.parse(subPath); - const relativeTestFilePath = path.relative(this.project.testDir, this._test._requireFile); + const relativeTestFilePath = path.relative(this.project.testDir, this._requireFile); const parsedRelativeTestFilePath = path.parse(relativeTestFilePath); const projectNamePathSegment = sanitizeForFilePath(this.project.name); diff --git a/playwright/packages/playwright/src/worker/testTracing.ts b/playwright/packages/playwright/src/worker/testTracing.ts index 89a6e99d44..579bf8bc87 100644 --- a/playwright/packages/playwright/src/worker/testTracing.ts +++ b/playwright/packages/playwright/src/worker/testTracing.ts @@ -19,31 +19,104 @@ import type * as trace from '@trace/trace'; import type EventEmitter from 'events'; import fs from 'fs'; import path from 'path'; -import { ManualPromise, calculateSha1, monotonicTime } from 'playwright-core/lib/utils'; +import { ManualPromise, calculateSha1, monotonicTime, createGuid } from 'playwright-core/lib/utils'; import { yauzl, yazl } from 'playwright-core/lib/zipBundle'; import type { TestInfo, TestInfoError } from '../../types/test'; import { filteredStackTrace } from '../util'; - +import type { TraceMode, PlaywrightWorkerOptions } from '../../types/test'; +import type { TestInfoImpl } from './testInfo'; export type Attachment = TestInfo['attachments'][0]; export const testTraceEntryName = 'test.trace'; +let traceOrdinal = 0; + +type TraceFixtureValue = PlaywrightWorkerOptions['trace'] | undefined; +type TraceOptions = { screenshots: boolean, snapshots: boolean, sources: boolean, attachments: boolean, _live: boolean, mode: TraceMode }; export class TestTracing { + private _testInfo: TestInfoImpl; + private _options: TraceOptions | undefined; private _liveTraceFile: string | undefined; private _traceEvents: trace.TraceEvent[] = []; - private _options: { sources: boolean; attachments: boolean; _live: boolean; } | undefined; + private _temporaryTraceFiles: string[] = []; + private _artifactsDir: string; + private _tracesDir: string; + + constructor(testInfo: TestInfoImpl, artifactsDir: string) { + this._testInfo = testInfo; + this._artifactsDir = artifactsDir; + this._tracesDir = path.join(this._artifactsDir, 'traces'); + } + + async startIfNeeded(value: TraceFixtureValue) { + const defaultTraceOptions: TraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true, _live: false, mode: 'off' }; + if (!value) { + this._options = defaultTraceOptions; + } else if (typeof value === 'string') { + this._options = { ...defaultTraceOptions, mode: value === 'retry-with-trace' ? 'on-first-retry' : value as TraceMode }; + } else { + const mode = value.mode || 'off'; + this._options = { ...defaultTraceOptions, ...value, mode: (mode as string) === 'retry-with-trace' ? 'on-first-retry' : mode }; + } - start(liveFileName: string, options: { sources: boolean, attachments: boolean, _live: boolean }) { - this._options = options; - if (!this._liveTraceFile && options._live) { - this._liveTraceFile = liveFileName; - fs.mkdirSync(path.dirname(this._liveTraceFile), { recursive: true }); + let shouldCaptureTrace = this._options.mode === 'on' || this._options.mode === 'retain-on-failure' || (this._options.mode === 'on-first-retry' && this._testInfo.retry === 1) || (this._options.mode === 'on-all-retries' && this._testInfo.retry > 0); + shouldCaptureTrace = shouldCaptureTrace && !process.env.PW_TEST_DISABLE_TRACING; + if (!shouldCaptureTrace) { + this._options = undefined; + return; + } + + if (!this._liveTraceFile && this._options._live) { + // Note that trace name must start with testId for live tracing to work. + this._liveTraceFile = path.join(this._tracesDir, `${this._testInfo.testId}-test.trace`); + await fs.promises.mkdir(path.dirname(this._liveTraceFile), { recursive: true }); const data = this._traceEvents.map(e => JSON.stringify(e)).join('\n') + '\n'; - fs.writeFileSync(this._liveTraceFile, data); + await fs.promises.writeFile(this._liveTraceFile, data); } } - async stop(fileName: string) { + artifactsDir() { + return this._artifactsDir; + } + + tracesDir() { + return this._tracesDir; + } + + traceTitle() { + return [path.relative(this._testInfo.project.testDir, this._testInfo.file) + ':' + this._testInfo.line, ...this._testInfo.titlePath.slice(1)].join(' › '); + } + + generateNextTraceRecordingName() { + const ordinalSuffix = traceOrdinal ? `-recording${traceOrdinal}` : ''; + ++traceOrdinal; + const retrySuffix = this._testInfo.retry ? `-retry${this._testInfo.retry}` : ''; + // Note that trace name must start with testId for live tracing to work. + return `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`; + } + + generateNextTraceRecordingPath() { + const file = path.join(this._artifactsDir, createGuid() + '.zip'); + this._temporaryTraceFiles.push(file); + return file; + } + + traceOptions() { + return this._options; + } + + async stopIfNeeded() { + if (!this._options) + return; + + const testFailed = this._testInfo.status !== this._testInfo.expectedStatus; + const shouldAbandonTrace = !testFailed && this._options.mode === 'retain-on-failure'; + if (shouldAbandonTrace) { + for (const file of this._temporaryTraceFiles) + await fs.promises.unlink(file).catch(() => {}); + return; + } + const zipFile = new yazl.ZipFile(); if (!this._options?.attachments) { @@ -97,9 +170,13 @@ export class TestTracing { await new Promise(f => { zipFile.end(undefined, () => { - zipFile.outputStream.pipe(fs.createWriteStream(fileName)).on('close', f); + zipFile.outputStream.pipe(fs.createWriteStream(this.generateNextTraceRecordingPath())).on('close', f); }); }); + + const tracePath = this._testInfo.outputPath('trace.zip'); + await mergeTraceFiles(tracePath, this._temporaryTraceFiles); + this._testInfo.attachments.push({ name: 'trace', path: tracePath, contentType: 'application/zip' }); } appendForError(error: TestInfoError) { @@ -185,7 +262,7 @@ function generatePreview(value: any, visited = new Set<any>()): string { return String(value); } -export async function mergeTraceFiles(fileName: string, temporaryTraceFiles: string[]) { +async function mergeTraceFiles(fileName: string, temporaryTraceFiles: string[]) { if (temporaryTraceFiles.length === 1) { await fs.promises.rename(temporaryTraceFiles[0], fileName); return; diff --git a/playwright/packages/playwright/src/worker/timeoutManager.ts b/playwright/packages/playwright/src/worker/timeoutManager.ts index 56b0272eba..94e182f682 100644 --- a/playwright/packages/playwright/src/worker/timeoutManager.ts +++ b/playwright/packages/playwright/src/worker/timeoutManager.ts @@ -16,7 +16,6 @@ import { colors } from 'playwright-core/lib/utilsBundle'; import { TimeoutRunner, TimeoutRunnerError } from 'playwright-core/lib/utils'; -import type { TestInfoError } from '../../types/test'; import type { Location } from '../../types/testReporter'; export type TimeSlot = { @@ -86,7 +85,7 @@ export class TimeoutManager { this._timeoutRunner.updateTimeout(slot.timeout); } - async runWithTimeout(cb: () => Promise<any>): Promise<TestInfoError | undefined> { + async runWithTimeout(cb: () => Promise<any>): Promise<Error | undefined> { try { await this._timeoutRunner.run(cb); } catch (error) { @@ -127,7 +126,7 @@ export class TimeoutManager { this._timeoutRunner.updateTimeout(slot.timeout, slot.elapsed); } - private _createTimeoutError(): TestInfoError { + private _createTimeoutError(): Error { let message = ''; const timeout = this._currentSlot().timeout; switch (this._runnable.type) { @@ -174,10 +173,10 @@ export class TimeoutManager { message = `Fixture "${fixtureWithSlot.title}" timeout of ${timeout}ms exceeded during ${fixtureWithSlot.phase}.`; message = colors.red(message); const location = (fixtureWithSlot || this._runnable).location; - return { - message, - // Include location for hooks, modifiers and fixtures to distinguish between them. - stack: location ? message + `\n at ${location.file}:${location.line}:${location.column}` : undefined - }; + const error = new Error(message); + error.name = ''; + // Include location for hooks, modifiers and fixtures to distinguish between them. + error.stack = message + (location ? `\n at ${location.file}:${location.line}:${location.column}` : ''); + return error; } } diff --git a/playwright/packages/playwright/src/worker/workerMain.ts b/playwright/packages/playwright/src/worker/workerMain.ts index bd529bbfd8..d678e485aa 100644 --- a/playwright/packages/playwright/src/worker/workerMain.ts +++ b/playwright/packages/playwright/src/worker/workerMain.ts @@ -18,18 +18,20 @@ import { colors } from 'playwright-core/lib/utilsBundle'; import { debugTest, formatLocation, relativeFilePath, serializeError } from '../util'; import { type TestBeginPayload, type TestEndPayload, type RunPayload, type DonePayload, type WorkerInitParams, type TeardownErrorsPayload, stdioChunkToParams } from '../common/ipc'; import { setCurrentTestInfo, setIsWorkerProcess } from '../common/globals'; -import { ConfigLoader } from '../common/configLoader'; +import { deserializeConfig } from '../common/configLoader'; import type { Suite, TestCase } from '../common/test'; import type { Annotation, FullConfigInternal, FullProjectInternal } from '../common/config'; import { FixtureRunner } from './fixtureRunner'; import { ManualPromise, gracefullyCloseAll, removeFolders } from 'playwright-core/lib/utils'; import { TestInfoImpl } from './testInfo'; -import { TimeoutManager, type TimeSlot } from './timeoutManager'; import { ProcessRunner } from '../common/process'; import { loadTestFile } from '../common/testLoader'; import { applyRepeatEachIndex, bindFileSuiteToProject, filterTestsRemoveEmptySuites } from '../common/suiteUtils'; import { PoolBuilder } from '../common/poolBuilder'; import type { TestInfoError } from '../../types/test'; +import type { Location } from '../../types/testReporter'; +import type { FixtureScope } from '../common/fixtures'; +import { inheritFixutreNames } from '../common/fixtures'; export class WorkerMain extends ProcessRunner { private _params: WorkerInitParams; @@ -50,19 +52,17 @@ export class WorkerMain extends ProcessRunner { // This promise resolves once the single "run test group" call finishes. private _runFinished = new ManualPromise<void>(); private _currentTest: TestInfoImpl | null = null; - private _lastRunningTests: TestInfoImpl[] = []; + private _lastRunningTests: TestCase[] = []; private _totalRunningTests = 0; - // Dynamic annotations originated by modifiers with a callback, e.g. `test.skip(() => true)`. - private _extraSuiteAnnotations = new Map<Suite, Annotation[]>(); // Suites that had their beforeAll hooks, but not afterAll hooks executed. // These suites still need afterAll hooks to be executed for the proper cleanup. - private _activeSuites = new Set<Suite>(); + // Contains dynamic annotations originated by modifiers with a callback, e.g. `test.skip(() => true)`. + private _activeSuites = new Map<Suite, Annotation[]>(); constructor(params: WorkerInitParams) { super(); process.env.TEST_WORKER_INDEX = String(params.workerIndex); process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex); - process.env.TEST_ARTIFACTS_DIR = params.artifactsDir; setIsWorkerProcess(); this._params = params; @@ -128,7 +128,7 @@ export class WorkerMain extends ProcessRunner { '', '', colors.red(`Failed worker ran ${count}${lastMessage}:`), - ...this._lastRunningTests.map(testInfo => formatTestTitle(testInfo._test, testInfo.project.name)), + ...this._lastRunningTests.map(test => formatTestTitle(test, this._project.project.name)), ].join('\n'); if (error.message) { if (error.stack) { @@ -144,17 +144,32 @@ export class WorkerMain extends ProcessRunner { } } + private async _teardownScope(scope: FixtureScope, testInfo: TestInfoImpl) { + const error = await this._teardownScopeAndReturnFirstError(scope, testInfo); + if (error) + throw error; + } + + private async _teardownScopeAndReturnFirstError(scope: FixtureScope, testInfo: TestInfoImpl): Promise<Error | undefined> { + let error: Error | undefined; + await this._fixtureRunner.teardownScope(scope, testInfo, e => { + testInfo._failWithError(e, true, false); + if (error === undefined) + error = e; + }); + return error; + } + private async _teardownScopes() { // TODO: separate timeout for teardown? - const timeoutManager = new TimeoutManager(this._project.project.timeout); - await timeoutManager.withRunnable({ type: 'teardown' }, async () => { - const timeoutError = await timeoutManager.runWithTimeout(async () => { - await this._fixtureRunner.teardownScope('test', timeoutManager); - await this._fixtureRunner.teardownScope('worker', timeoutManager); + const fakeTestInfo = new TestInfoImpl(this._config, this._project, this._params, undefined, 0, () => {}, () => {}, () => {}); + await fakeTestInfo._timeoutManager.withRunnable({ type: 'teardown' }, async () => { + await fakeTestInfo._runWithTimeout(async () => { + await this._teardownScopeAndReturnFirstError('test', fakeTestInfo); + await this._teardownScopeAndReturnFirstError('worker', fakeTestInfo); }); - if (timeoutError) - this._fatalErrors.push(timeoutError); }); + this._fatalErrors.push(...fakeTestInfo.errors); } unhandledError(error: Error | any) { @@ -170,7 +185,7 @@ export class WorkerMain extends ProcessRunner { // and unhandled errors - both lead to the test failing. This is good for regular tests, // so that you can, e.g. expect() from inside an event handler. The test fails, // and we restart the worker. - this._currentTest._failWithError(error, true /* isHardError */); + this._currentTest._failWithError(error, true /* isHardError */, true /* retriable */); // For tests marked with test.fail(), this might be a problem when unhandled error // is not coming from the user test code (legit failure), but from fixtures or test runner. @@ -190,7 +205,7 @@ export class WorkerMain extends ProcessRunner { if (this._config) return; - this._config = await ConfigLoader.deserialize(this._params.config); + this._config = await deserializeConfig(this._params.config); this._project = this._config.projects.find(p => p.id === this._params.projectId)!; this._poolBuilder = PoolBuilder.createForWorker(this._project); } @@ -208,8 +223,7 @@ export class WorkerMain extends ProcessRunner { const hasEntries = filterTestsRemoveEmptySuites(suite, test => entries.has(test.id)); if (hasEntries) { this._poolBuilder.buildPools(suite); - this._extraSuiteAnnotations = new Map(); - this._activeSuites = new Set(); + this._activeSuites = new Map(); this._didRunFullCleanup = false; const tests = suite.allTests(); for (let i = 0; i < tests.length; i++) { @@ -280,12 +294,12 @@ export class WorkerMain extends ProcessRunner { const nextSuites = new Set(getSuites(nextTest)); testInfo._timeoutManager.setTimeout(test.timeout); - for (const annotation of test._staticAnnotations) + for (const annotation of test.annotations) processAnnotation(annotation); // Process existing annotations dynamically set for parent suites. for (const suite of suites) { - const extraAnnotations = this._extraSuiteAnnotations.get(suite) || []; + const extraAnnotations = this._activeSuites.get(suite) || []; for (const annotation of extraAnnotations) processAnnotation(annotation); } @@ -306,13 +320,28 @@ export class WorkerMain extends ProcessRunner { } this._totalRunningTests++; - this._lastRunningTests.push(testInfo); + this._lastRunningTests.push(test); if (this._lastRunningTests.length > 10) this._lastRunningTests.shift(); let didFailBeforeAllForSuite: Suite | undefined; let shouldRunAfterEachHooks = false; await testInfo._runWithTimeout(async () => { + const traceError = await testInfo._runAndFailOnError(async () => { + // Ideally, "trace" would be an config-level option belonging to the + // test runner instead of a fixture belonging to Playwright. + // However, for backwards compatibility, we have to read it from a fixture today. + // We decided to not introduce the config-level option just yet. + const traceFixtureRegistration = test._pool!.resolve('trace'); + if (!traceFixtureRegistration) + return; + if (typeof traceFixtureRegistration.fn === 'function') + throw new Error(`"trace" option cannot be a function`); + await testInfo._tracing.startIfNeeded(traceFixtureRegistration.fn); + }); + if (traceError) + return; + if (this._isStopped || isSkipped) { // Two reasons to get here: // - Last test is skipped, so we should not run the test, but run the cleanup. @@ -327,48 +356,41 @@ export class WorkerMain extends ProcessRunner { let testFunctionParams: object | null = null; await testInfo._runAsStep({ category: 'hook', title: 'Before Hooks' }, async step => { - testInfo._beforeHooksStep = step; - // Note: wrap all preparation steps together, because failure/skip in any of them - // prevents further setup and/or test from running. - const beforeHooksError = await testInfo._runAndFailOnError(async () => { - // Run "beforeAll" modifiers on parent suites, unless already run during previous tests. - for (const suite of suites) { - if (this._extraSuiteAnnotations.has(suite)) - continue; - const extraAnnotations: Annotation[] = []; - this._extraSuiteAnnotations.set(suite, extraAnnotations); - didFailBeforeAllForSuite = suite; // Assume failure, unless reset below. - // Separate timeout for each "beforeAll" modifier. - const timeSlot = { timeout: this._project.project.timeout, elapsed: 0 }; - await this._runModifiersForSuite(suite, testInfo, 'worker', timeSlot, extraAnnotations); - } - - // Run "beforeAll" hooks, unless already run during previous tests. - for (const suite of suites) { - didFailBeforeAllForSuite = suite; // Assume failure, unless reset below. - await this._runBeforeAllHooksForSuite(suite, testInfo); + // Run "beforeAll" hooks, unless already run during previous tests. + for (const suite of suites) { + didFailBeforeAllForSuite = suite; // Assume failure, unless reset below. + const beforeAllError = await this._runBeforeAllHooksForSuite(suite, testInfo); + if (beforeAllError) { + step.complete({ error: beforeAllError }); + return; } - - // Running "beforeAll" succeeded for all suites! didFailBeforeAllForSuite = undefined; + if (testInfo.expectedStatus === 'skipped') + return; + } - // Run "beforeEach" modifiers. - for (const suite of suites) - await this._runModifiersForSuite(suite, testInfo, 'test', undefined); - + const beforeEachError = await testInfo._runAndFailOnError(async () => { // Run "beforeEach" hooks. Once started with "beforeEach", we must run all "afterEach" hooks as well. shouldRunAfterEachHooks = true; await this._runEachHooksForSuites(suites, 'beforeEach', testInfo); + }, 'allowSkips'); + if (beforeEachError) { + step.complete({ error: beforeEachError }); + return; + } + if (testInfo.expectedStatus === 'skipped') + return; + const fixturesError = await testInfo._runAndFailOnError(async () => { // Setup fixtures required by the test. testFunctionParams = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, 'test'); }, 'allowSkips'); - if (beforeHooksError) - step.complete({ error: beforeHooksError }); + if (fixturesError) + step.complete({ error: fixturesError }); }); if (testFunctionParams === null) { - // Fixture setup failed, we should not run the test now. + // Fixture setup failed or was skipped, we should not run the test now. return; } @@ -379,9 +401,6 @@ export class WorkerMain extends ProcessRunner { await fn(testFunctionParams, testInfo); debugTest(`test function finished`); }, 'allowSkips'); - - for (const error of testInfo.errors) - testInfo._tracing.appendForError(error); }); if (didFailBeforeAllForSuite) { @@ -393,8 +412,7 @@ export class WorkerMain extends ProcessRunner { // A timed-out test gets a full additional timeout to run after hooks. const afterHooksSlot = testInfo._didTimeout ? { timeout: this._project.project.timeout, elapsed: 0 } : undefined; - await testInfo._runAsStepWithRunnable({ category: 'hook', title: 'After Hooks', runnableType: 'afterHooks', runnableSlot: afterHooksSlot }, async step => { - testInfo._afterHooksStep = step; + await testInfo._runAsStepWithRunnable({ category: 'hook', title: 'After Hooks', runnableType: 'afterHooks', runnableSlot: afterHooksSlot, forceNoParent: true }, async step => { let firstAfterHooksError: Error | undefined; await testInfo._runWithTimeout(async () => { // Note: do not wrap all teardown steps together, because failure in any of them @@ -415,9 +433,7 @@ export class WorkerMain extends ProcessRunner { // Teardown test-scoped fixtures. Attribute to 'test' so that users understand // they should probably increase the test timeout to fix this issue. debugTest(`tearing down test scope started`); - const testScopeError = await testInfo._runAndFailOnError(() => { - return this._fixtureRunner.teardownScope('test', testInfo._timeoutManager); - }); + const testScopeError = await this._teardownScopeAndReturnFirstError('test', testInfo); debugTest(`tearing down test scope finished`); firstAfterHooksError = firstAfterHooksError || testScopeError; @@ -448,9 +464,7 @@ export class WorkerMain extends ProcessRunner { await testInfo._timeoutManager.withRunnable({ type: 'teardown', slot: teardownSlot }, async () => { // Attribute to 'test' so that users understand they should probably increate the test timeout to fix this issue. debugTest(`tearing down test scope started`); - const testScopeError = await testInfo._runAndFailOnError(() => { - return this._fixtureRunner.teardownScope('test', testInfo._timeoutManager); - }); + const testScopeError = await this._teardownScopeAndReturnFirstError('test', testInfo); debugTest(`tearing down test scope finished`); firstAfterHooksError = firstAfterHooksError || testScopeError; @@ -461,9 +475,7 @@ export class WorkerMain extends ProcessRunner { // Attribute to 'teardown' because worker fixtures are not perceived as a part of a test. debugTest(`tearing down worker scope started`); - const workerScopeError = await testInfo._runAndFailOnError(() => { - return this._fixtureRunner.teardownScope('worker', testInfo._timeoutManager); - }); + const workerScopeError = await this._teardownScopeAndReturnFirstError('worker', testInfo); debugTest(`tearing down worker scope finished`); firstAfterHooksError = firstAfterHooksError || workerScopeError; }); @@ -474,6 +486,10 @@ export class WorkerMain extends ProcessRunner { step.complete({ error: firstAfterHooksError }); }); + await testInfo._runAndFailOnError(async () => { + await testInfo._tracing.stopIfNeeded(); + }); + this._currentTest = null; setCurrentTestInfo(null); this.dispatchEvent('testEnd', buildTestEndPayload(testInfo)); @@ -484,107 +500,94 @@ export class WorkerMain extends ProcessRunner { await removeFolders([testInfo.outputDir]); } - private async _runModifiersForSuite(suite: Suite, testInfo: TestInfoImpl, scope: 'worker' | 'test', timeSlot: TimeSlot | undefined, extraAnnotations?: Annotation[]) { + private _collectHooksAndModifiers(suite: Suite, type: 'beforeAll' | 'beforeEach' | 'afterAll' | 'afterEach', testInfo: TestInfoImpl) { + type Runnable = { type: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll' | 'fixme' | 'skip' | 'slow' | 'fail', fn: Function, title: string, location: Location }; + const runnables: Runnable[] = []; for (const modifier of suite._modifiers) { - const actualScope = this._fixtureRunner.dependsOnWorkerFixturesOnly(modifier.fn, modifier.location) ? 'worker' : 'test'; - if (actualScope !== scope) + const modifierType = this._fixtureRunner.dependsOnWorkerFixturesOnly(modifier.fn, modifier.location) ? 'beforeAll' : 'beforeEach'; + if (modifierType !== type) continue; - debugTest(`modifier at "${formatLocation(modifier.location)}" started`); - const result = await testInfo._runAsStepWithRunnable({ - category: 'hook', + const fn = async (fixtures: any) => { + const result = await modifier.fn(fixtures); + testInfo[modifier.type](!!result, modifier.description); + }; + inheritFixutreNames(modifier.fn, fn); + runnables.push({ title: `${modifier.type} modifier`, location: modifier.location, - runnableType: modifier.type, - runnableSlot: timeSlot, - }, () => this._fixtureRunner.resolveParametersAndRunFunction(modifier.fn, testInfo, scope)); - debugTest(`modifier at "${formatLocation(modifier.location)}" finished`); - if (result && extraAnnotations) - extraAnnotations.push({ type: modifier.type, description: modifier.description }); - testInfo[modifier.type](!!result, modifier.description); + type: modifier.type, + fn, + }); } + // Modifiers first, then hooks. + runnables.push(...suite._hooks.filter(hook => hook.type === type)); + return runnables; } private async _runBeforeAllHooksForSuite(suite: Suite, testInfo: TestInfoImpl) { if (this._activeSuites.has(suite)) return; - this._activeSuites.add(suite); - let beforeAllError: Error | undefined; - for (const hook of suite._hooks) { - if (hook.type !== 'beforeAll') - continue; + const extraAnnotations: Annotation[] = []; + this._activeSuites.set(suite, extraAnnotations); + return await this._runAllHooksForSuite(suite, testInfo, 'beforeAll', extraAnnotations); + } + + private async _runAllHooksForSuite(suite: Suite, testInfo: TestInfoImpl, type: 'beforeAll' | 'afterAll', extraAnnotations?: Annotation[]) { + const allowSkips = type === 'beforeAll'; + let firstError: Error | undefined; + for (const hook of this._collectHooksAndModifiers(suite, type, testInfo)) { debugTest(`${hook.type} hook at "${formatLocation(hook.location)}" started`); - try { - // Separate time slot for each "beforeAll" hook. + const error = await testInfo._runAndFailOnError(async () => { + // Separate time slot for each beforeAll/afterAll hook. const timeSlot = { timeout: this._project.project.timeout, elapsed: 0 }; await testInfo._runAsStepWithRunnable({ category: 'hook', - title: `${hook.title}`, + title: hook.title, location: hook.location, - runnableType: 'beforeAll', + runnableType: hook.type, runnableSlot: timeSlot, }, async () => { + const existingAnnotations = new Set(testInfo.annotations); try { await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, 'all-hooks-only'); } finally { - // Each beforeAll hook has its own scope for test fixtures. Attribute to the same runnable and timeSlot. - // Note: we must teardown even after beforeAll fails, because we'll run more beforeAlls. - await this._fixtureRunner.teardownScope('test', testInfo._timeoutManager); + if (extraAnnotations) { + // Inherit all annotations defined in the beforeAll/modifer to all tests in the suite. + const newAnnotations = testInfo.annotations.filter(a => !existingAnnotations.has(a)); + extraAnnotations.push(...newAnnotations); + } + // Each beforeAll/afterAll hook has its own scope for test fixtures. Attribute to the same runnable and timeSlot. + // Note: we must teardown even after hook fails, because we'll run more hooks. + await this._teardownScope('test', testInfo); } }); - } catch (e) { - // Always run all the hooks, and capture the first error. - beforeAllError = beforeAllError || e; - } + }, allowSkips ? 'allowSkips' : undefined); + firstError = firstError || error; debugTest(`${hook.type} hook at "${formatLocation(hook.location)}" finished`); + // Skip inside a beforeAll hook/modifier prevents others from running. + if (allowSkips && testInfo.expectedStatus === 'skipped') + break; } - if (beforeAllError) - throw beforeAllError; + return firstError; } private async _runAfterAllHooksForSuite(suite: Suite, testInfo: TestInfoImpl): Promise<Error | undefined> { if (!this._activeSuites.has(suite)) return; this._activeSuites.delete(suite); - let firstError: Error | undefined; - for (const hook of suite._hooks) { - if (hook.type !== 'afterAll') - continue; - debugTest(`${hook.type} hook at "${formatLocation(hook.location)}" started`); - const afterAllError = await testInfo._runAndFailOnError(async () => { - // Separate time slot for each "afterAll" hook. - const timeSlot = { timeout: this._project.project.timeout, elapsed: 0 }; - await testInfo._runAsStepWithRunnable({ - category: 'hook', - title: `${hook.title}`, - location: hook.location, - runnableType: 'afterAll', - runnableSlot: timeSlot, - }, async () => { - try { - await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, 'all-hooks-only'); - } finally { - // Each afterAll hook has its own scope for test fixtures. Attribute to the same runnable and timeSlot. - // Note: we must teardown even after afterAll fails, because we'll run more afterAlls. - await this._fixtureRunner.teardownScope('test', testInfo._timeoutManager); - } - }); - }); - firstError = firstError || afterAllError; - debugTest(`${hook.type} hook at "${formatLocation(hook.location)}" finished`); - } - return firstError; + return await this._runAllHooksForSuite(suite, testInfo, 'afterAll'); } private async _runEachHooksForSuites(suites: Suite[], type: 'beforeEach' | 'afterEach', testInfo: TestInfoImpl) { - const hooks = suites.map(suite => suite._hooks.filter(hook => hook.type === type)).flat(); + const hooks = suites.map(suite => this._collectHooksAndModifiers(suite, type, testInfo)).flat(); let error: Error | undefined; for (const hook of hooks) { try { await testInfo._runAsStepWithRunnable({ category: 'hook', - title: `${hook.title}`, + title: hook.title, location: hook.location, - runnableType: type, + runnableType: hook.type, }, () => this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, 'test')); } catch (e) { // Always run all the hooks, and capture the first error. @@ -598,17 +601,18 @@ export class WorkerMain extends ProcessRunner { function buildTestBeginPayload(testInfo: TestInfoImpl): TestBeginPayload { return { - testId: testInfo._test.id, + testId: testInfo.testId, startWallTime: testInfo._startWallTime, }; } function buildTestEndPayload(testInfo: TestInfoImpl): TestEndPayload { return { - testId: testInfo._test.id, + testId: testInfo.testId, duration: testInfo.duration, status: testInfo.status!, errors: testInfo.errors, + hasNonRetriableError: testInfo._hasNonRetriableError, expectedStatus: testInfo.expectedStatus, annotations: testInfo.annotations, timeout: testInfo.timeout, diff --git a/playwright/packages/playwright/types/test.d.ts b/playwright/packages/playwright/types/test.d.ts index bbbcdb4c53..7e329ae595 100644 --- a/playwright/packages/playwright/types/test.d.ts +++ b/playwright/packages/playwright/types/test.d.ts @@ -693,12 +693,23 @@ interface TestConfig { */ maxDiffPixelRatio?: number; }; + + /** + * Configuration for the [expect(value).toPass()](https://playwright.dev/docs/test-assertions#expecttopass) method. + */ + toPass?: { + /** + * timeout for toPass method in milliseconds. + */ + timeout?: number; + }; }; /** * Whether to exit with an error if any tests or groups are marked as - * [test.only(title, testFunction)](https://playwright.dev/docs/api/class-test#test-only) or - * [test.describe.only(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-only). Useful on CI. + * [test.only(title[, details, body])](https://playwright.dev/docs/api/class-test#test-only) or + * [test.describe.only([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-only). + * Useful on CI. * * **Usage** * @@ -1453,8 +1464,9 @@ export type Metadata = { [key: string]: any }; export interface FullConfig<TestArgs = {}, WorkerArgs = {}> { /** * Whether to exit with an error if any tests or groups are marked as - * [test.only(title, testFunction)](https://playwright.dev/docs/api/class-test#test-only) or - * [test.describe.only(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-only). Useful on CI. + * [test.only(title[, details, body])](https://playwright.dev/docs/api/class-test#test-only) or + * [test.describe.only([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-only). + * Useful on CI. * * **Usage** * @@ -1906,12 +1918,12 @@ export interface WorkerInfo { /** * `TestInfo` contains information about currently running test. It is available to test functions, - * [test.beforeEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-each-1), - * [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1), - * [test.beforeAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-all-1) and - * [test.afterAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-all-1) hooks, and test-scoped - * fixtures. `TestInfo` provides utilities to control test execution: attach files, update test timeout, determine - * which test is currently running and whether it was retried, etc. + * [test.beforeEach([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-before-each), + * [test.afterEach([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-each), + * [test.beforeAll([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-before-all) and + * [test.afterAll([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-all) hooks, and + * test-scoped fixtures. `TestInfo` provides utilities to control test execution: attach files, update test timeout, + * determine which test is currently running and whether it was retried, etc. * * ```js * import { test, expect } from '@playwright/test'; @@ -1988,13 +2000,14 @@ export interface TestInfo { /** * Marks the currently running test as "should fail". Playwright Test runs this test and ensures that it is actually * failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is - * fixed. This is similar to [test.fail()](https://playwright.dev/docs/api/class-test#test-fail-1). + * fixed. This is similar to + * [test.fail([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fail). */ fail(): void; /** * Conditionally mark the currently running test as "should fail" with an optional description. This is similar to - * [test.fail(condition[, description])](https://playwright.dev/docs/api/class-test#test-fail-2). + * [test.fail([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fail). * @param condition Test is marked as "should fail" when the condition is `true`. * @param description Optional description that will be reflected in a test report. */ @@ -2002,13 +2015,13 @@ export interface TestInfo { /** * Mark a test as "fixme", with the intention to fix it. Test is immediately aborted. This is similar to - * [test.fixme()](https://playwright.dev/docs/api/class-test#test-fixme-2). + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme). */ fixme(): void; /** * Conditionally mark the currently running test as "fixme" with an optional description. This is similar to - * [test.fixme(condition[, description])](https://playwright.dev/docs/api/class-test#test-fixme-3). + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme). * @param condition Test is marked as "fixme" when the condition is `true`. * @param description Optional description that will be reflected in a test report. */ @@ -2060,13 +2073,13 @@ export interface TestInfo { /** * Unconditionally skip the currently running test. Test is immediately aborted. This is similar to - * [test.skip()](https://playwright.dev/docs/api/class-test#test-skip-2). + * [test.skip([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-skip). */ skip(): void; /** * Conditionally skips the currently running test with an optional description. This is similar to - * [test.skip(condition[, description])](https://playwright.dev/docs/api/class-test#test-skip-3). + * [test.skip([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-skip). * @param condition A skip condition. Test is skipped when the condition is `true`. * @param description Optional description that will be reflected in a test report. */ @@ -2074,14 +2087,14 @@ export interface TestInfo { /** * Marks the currently running test as "slow", giving it triple the default timeout. This is similar to - * [test.slow()](https://playwright.dev/docs/api/class-test#test-slow-1). + * [test.slow([condition, callback, description])](https://playwright.dev/docs/api/class-test#test-slow). */ slow(): void; /** * Conditionally mark the currently running test as "slow" with an optional description, giving it triple the default * timeout. This is similar to - * [test.slow(condition[, description])](https://playwright.dev/docs/api/class-test#test-slow-2). + * [test.slow([condition, callback, description])](https://playwright.dev/docs/api/class-test#test-slow). * @param condition Test is marked as "slow" when the condition is `true`. * @param description Optional description that will be reflected in a test report. */ @@ -2102,8 +2115,8 @@ export interface TestInfo { /** * The list of annotations applicable to the current test. Includes annotations from the test, annotations from all - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) groups the test - * belongs to and file-level annotations for the test file. + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) groups the + * test belongs to and file-level annotations for the test file. * * Learn more about [test annotations](https://playwright.dev/docs/test-annotations). */ @@ -2156,8 +2169,8 @@ export interface TestInfo { /** * The number of milliseconds the test took to finish. Always zero before the test finishes, either successfully or - * not. Can be used in [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1) - * hook. + * not. Can be used in + * [test.afterEach([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-each) hook. */ duration: number; @@ -2174,9 +2187,10 @@ export interface TestInfo { /** * Expected status for the currently running test. This is usually `'passed'`, except for a few cases: - * - `'skipped'` for skipped tests, e.g. with [test.skip()](https://playwright.dev/docs/api/class-test#test-skip-2); + * - `'skipped'` for skipped tests, e.g. with + * [test.skip([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-skip); * - `'failed'` for tests marked as failed with - * [test.fail()](https://playwright.dev/docs/api/class-test#test-fail-1). + * [test.fail([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fail). * * Expected status is usually compared with the actual * [testInfo.status](https://playwright.dev/docs/api/class-testinfo#test-info-status): @@ -2280,7 +2294,8 @@ export interface TestInfo { /** * Actual status for the currently running test. Available after the test has finished in - * [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1) hook and fixtures. + * [test.afterEach([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-each) hook and + * fixtures. * * Status is usually compared with the * [testInfo.expectedStatus](https://playwright.dev/docs/api/class-testinfo#test-info-expected-status): @@ -2297,16 +2312,6 @@ export interface TestInfo { */ status?: "passed"|"failed"|"timedOut"|"skipped"|"interrupted"; - /** - * Output written to `process.stderr` or `console.error` during the test execution. - */ - stderr: Array<string|Buffer>; - - /** - * Output written to `process.stdout` or `console.log` during the test execution. - */ - stdout: Array<string|Buffer>; - /** * Test id matching the test case id in the reporter API. */ @@ -2350,12 +2355,28 @@ export interface TestInfo { workerIndex: number; } +type TestDetailsAnnotation = { + type: string; + description?: string; +}; + +export type TestDetails = { + tag?: string | string[]; + annotation?: TestDetailsAnnotation | TestDetailsAnnotation[]; +} + interface SuiteFunction { /** * Declares a group of tests. + * - `test.describe(title, callback)` + * - `test.describe(callback)` + * - `test.describe(title, details, callback)` * * **Usage** * + * You can declare a group of tests with a title. The title will be visible in the test report as a part of each + * test's title. + * * ```js * test.describe('two tests', () => { * test('one', async ({ page }) => { @@ -2368,17 +2389,10 @@ interface SuiteFunction { * }); * ``` * - * @param title Group title. - * @param callback A callback that is run immediately when calling - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1). Any tests added in - * this callback will belong to the group. - */ - (title: string, callback: () => void): void; - /** - * Declares an anonymous group of tests. This is convenient to give a group of tests a common option with - * [test.use(options)](https://playwright.dev/docs/api/class-test#test-use). + * **Anonymous group** * - * **Usage** + * You can also declare a test group without a title. This is convenient to give a group of tests a common option with + * [test.use(options)](https://playwright.dev/docs/api/class-test#test-use). * * ```js * test.describe(() => { @@ -2394,54 +2408,41 @@ interface SuiteFunction { * }); * ``` * - * @param callback A callback that is run immediately when calling - * [test.describe(callback)](https://playwright.dev/docs/api/class-test#test-describe-2). Any tests added in this - * callback will belong to the group. - */ - (callback: () => void): void; -} - -interface TestFunction<TestArgs> { - (title: string, testFunction: (args: TestArgs, testInfo: TestInfo) => Promise<void> | void): void; -} - -/** - * Playwright Test provides a `test` function to declare tests and `expect` function to write assertions. - * - * ```js - * import { test, expect } from '@playwright/test'; - * - * test('basic test', async ({ page }) => { - * await page.goto('https://playwright.dev/'); - * const name = await page.innerText('.navbar__title'); - * expect(name).toBe('Playwright'); - * }); - * ``` - * - */ -export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue> extends TestFunction<TestArgs & WorkerArgs> { - /** - * Declares a focused test. If there are some focused tests or suites, all of them will be run but nothing else. + * **Tags** * - * **Usage** + * You can tag all tests in a group by providing additional details. Note that each tag must start with `@` symbol. * * ```js - * test.only('focus this test', async ({ page }) => { - * // Run only focused tests in the entire project. + * import { test, expect } from '@playwright/test'; + * + * test.describe('two tagged tests', { + * tag: '@smoke', + * }, () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); * }); * ``` * - * @param title Test title. - * @param testFunction Test function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. - */ - only: TestFunction<TestArgs & WorkerArgs>; - /** - * Declares a group of tests. + * Learn more about [tagging](https://playwright.dev/docs/test-annotations#tag-tests). * - * **Usage** + * **Annotations** + * + * You can annotate all tests in a group by providing additional details. * * ```js - * test.describe('two tests', () => { + * import { test, expect } from '@playwright/test'; + * + * test.describe('two annotated tests', { + * annotation: { + * type: 'issue', + * description: 'https://github.com/microsoft/playwright/issues/23180', + * }, + * }, () => { * test('one', async ({ page }) => { * // ... * }); @@ -2452,510 +2453,2105 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * }); * ``` * + * Learn more about [test annotations](https://playwright.dev/docs/test-annotations). * @param title Group title. + * @param details Additional details for all tests in the group. * @param callback A callback that is run immediately when calling - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1). Any tests added in - * this callback will belong to the group. + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe). Any tests + * declared in this callback will belong to the group. */ - describe: SuiteFunction & { - /** - * Declares a focused group of tests. If there are some focused tests or suites, all of them will be run but nothing - * else. + (title: string, callback: () => void): void; + /** + * Declares a group of tests. + * - `test.describe(title, callback)` + * - `test.describe(callback)` + * - `test.describe(title, details, callback)` * * **Usage** * + * You can declare a group of tests with a title. The title will be visible in the test report as a part of each + * test's title. + * * ```js - * test.describe.only('focused group', () => { - * test('in the focused group', async ({ page }) => { - * // This test will run + * test.describe('two tests', () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... * }); * }); - * test('not in the focused group', async ({ page }) => { - * // This test will not run + * ``` + * + * **Anonymous group** + * + * You can also declare a test group without a title. This is convenient to give a group of tests a common option with + * [test.use(options)](https://playwright.dev/docs/api/class-test#test-use). + * + * ```js + * test.describe(() => { + * test.use({ colorScheme: 'dark' }); + * + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); * }); * ``` * - * @param title Group title. - * @param callback A callback that is run immediately when calling - * [test.describe.only(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-only). Any tests - * added in this callback will belong to the group. - */ - only: SuiteFunction; - /** - * Declares a skipped test group, similarly to - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1). Tests in the skipped - * group are never run. + * **Tags** * - * **Usage** + * You can tag all tests in a group by providing additional details. Note that each tag must start with `@` symbol. * * ```js - * test.describe.skip('skipped group', () => { - * test('example', async ({ page }) => { - * // This test will not run + * import { test, expect } from '@playwright/test'; + * + * test.describe('two tagged tests', { + * tag: '@smoke', + * }, () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... * }); * }); * ``` * - * @param title Group title. - * @param callback A callback that is run immediately when calling - * [test.describe.skip(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-skip). Any tests - * added in this callback will belong to the group, and will not be run. - */ - skip: SuiteFunction; - /** - * Declares a test group similarly to - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1). Tests in this group - * are marked as "fixme" and will not be executed. + * Learn more about [tagging](https://playwright.dev/docs/test-annotations#tag-tests). * - * **Usage** + * **Annotations** + * + * You can annotate all tests in a group by providing additional details. * * ```js - * test.describe.fixme('broken tests', () => { - * test('example', async ({ page }) => { - * // This test will not run + * import { test, expect } from '@playwright/test'; + * + * test.describe('two annotated tests', { + * annotation: { + * type: 'issue', + * description: 'https://github.com/microsoft/playwright/issues/23180', + * }, + * }, () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... * }); * }); * ``` * + * Learn more about [test annotations](https://playwright.dev/docs/test-annotations). * @param title Group title. + * @param details Additional details for all tests in the group. * @param callback A callback that is run immediately when calling - * [test.describe.fixme(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-fixme). Any tests - * added in this callback will belong to the group, and will not be run. + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe). Any tests + * declared in this callback will belong to the group. */ - fixme: SuiteFunction; - /** - * **NOTE** See [test.describe.configure([options])](https://playwright.dev/docs/api/class-test#test-describe-configure) for - * the preferred way of configuring the execution mode. - * - * Declares a group of tests that should always be run serially. If one of the tests fails, all subsequent tests are - * skipped. All tests in a group are retried together. - * - * **NOTE** Using serial is not recommended. It is usually better to make your tests isolated, so they can be run - * independently. + (callback: () => void): void; + /** + * Declares a group of tests. + * - `test.describe(title, callback)` + * - `test.describe(callback)` + * - `test.describe(title, details, callback)` * * **Usage** * + * You can declare a group of tests with a title. The title will be visible in the test report as a part of each + * test's title. + * * ```js - * test.describe.serial('group', () => { - * test('runs first', async ({ page }) => {}); - * test('runs second', async ({ page }) => {}); + * test.describe('two tests', () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); * }); * ``` * - * @param title Group title. - * @param callback A callback that is run immediately when calling - * [test.describe.serial(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-serial). Any tests - * added in this callback will belong to the group. - */ - serial: SuiteFunction & { - /** - * **NOTE** See [test.describe.configure([options])](https://playwright.dev/docs/api/class-test#test-describe-configure) for - * the preferred way of configuring the execution mode. + * **Anonymous group** * - * Declares a focused group of tests that should always be run serially. If one of the tests fails, all subsequent - * tests are skipped. All tests in a group are retried together. If there are some focused tests or suites, all of - * them will be run but nothing else. + * You can also declare a test group without a title. This is convenient to give a group of tests a common option with + * [test.use(options)](https://playwright.dev/docs/api/class-test#test-use). * - * **NOTE** Using serial is not recommended. It is usually better to make your tests isolated, so they can be run - * independently. + * ```js + * test.describe(() => { + * test.use({ colorScheme: 'dark' }); * - * **Usage** + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); + * }); + * ``` + * + * **Tags** + * + * You can tag all tests in a group by providing additional details. Note that each tag must start with `@` symbol. * * ```js - * test.describe.serial.only('group', () => { - * test('runs first', async ({ page }) => { + * import { test, expect } from '@playwright/test'; + * + * test.describe('two tagged tests', { + * tag: '@smoke', + * }, () => { + * test('one', async ({ page }) => { + * // ... * }); - * test('runs second', async ({ page }) => { + * + * test('two', async ({ page }) => { + * // ... * }); * }); * ``` * - * @param title Group title. - * @param callback A callback that is run immediately when calling - * [test.describe.serial.only(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-serial-only). - * Any tests added in this callback will belong to the group. - */ - only: SuiteFunction; - }; - /** - * **NOTE** See [test.describe.configure([options])](https://playwright.dev/docs/api/class-test#test-describe-configure) for - * the preferred way of configuring the execution mode. + * Learn more about [tagging](https://playwright.dev/docs/test-annotations#tag-tests). * - * Declares a group of tests that could be run in parallel. By default, tests in a single test file run one after - * another, but using - * [test.describe.parallel(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-parallel) allows - * them to run in parallel. + * **Annotations** * - * **Usage** + * You can annotate all tests in a group by providing additional details. * * ```js - * test.describe.parallel('group', () => { - * test('runs in parallel 1', async ({ page }) => {}); - * test('runs in parallel 2', async ({ page }) => {}); + * import { test, expect } from '@playwright/test'; + * + * test.describe('two annotated tests', { + * annotation: { + * type: 'issue', + * description: 'https://github.com/microsoft/playwright/issues/23180', + * }, + * }, () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); * }); * ``` * - * Note that parallel tests are executed in separate processes and cannot share any state or global variables. Each of - * the parallel tests executes all relevant hooks. + * Learn more about [test annotations](https://playwright.dev/docs/test-annotations). * @param title Group title. + * @param details Additional details for all tests in the group. * @param callback A callback that is run immediately when calling - * [test.describe.parallel(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-parallel). Any - * tests added in this callback will belong to the group. + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe). Any tests + * declared in this callback will belong to the group. */ - parallel: SuiteFunction & { - /** - * **NOTE** See [test.describe.configure([options])](https://playwright.dev/docs/api/class-test#test-describe-configure) for - * the preferred way of configuring the execution mode. - * - * Declares a focused group of tests that could be run in parallel. This is similar to - * [test.describe.parallel(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-parallel), but - * focuses the group. If there are some focused tests or suites, all of them will be run but nothing else. + (title: string, details: TestDetails, callback: () => void): void; +} + +interface TestFunction<TestArgs> { + /** + * Declares a test. + * - `test(title, body)` + * - `test(title, details, body)` * * **Usage** * * ```js - * test.describe.parallel.only('group', () => { - * test('runs in parallel 1', async ({ page }) => {}); - * test('runs in parallel 2', async ({ page }) => {}); + * import { test, expect } from '@playwright/test'; + * + * test('basic test', async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * // ... + * }); + * ``` + * + * **Tags** + * + * You can tag tests by providing additional test details. Alternatively, you can include tags in the test title. Note + * that each tag must start with `@` symbol. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('basic test', { + * tag: '@smoke', + * }, async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * // ... + * }); + * + * test('another test @smoke', async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * // ... + * }); + * ``` + * + * Test tags are displayed in the test report, and are available to a custom reporter via `TestCase.tags` property. + * + * You can also filter tests by their tags during test execution: + * - in the [command line](https://playwright.dev/docs/test-cli#reference); + * - in the config with [testConfig.grep](https://playwright.dev/docs/api/class-testconfig#test-config-grep) and + * [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep); + * + * Learn more about [tagging](https://playwright.dev/docs/test-annotations#tag-tests). + * + * **Annotations** + * + * You can annotate tests by providing additional test details. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('basic test', { + * annotation: { + * type: 'issue', + * description: 'https://github.com/microsoft/playwright/issues/23180', + * }, + * }, async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * // ... + * }); + * ``` + * + * Test annotations are displayed in the test report, and are available to a custom reporter via + * `TestCase.annotations` property. + * + * You can also add annotations during runtime by manipulating + * [testInfo.annotations](https://playwright.dev/docs/api/class-testinfo#test-info-annotations). + * + * 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}. + */ + (title: string, body: (args: TestArgs, testInfo: TestInfo) => Promise<void> | void): void; + /** + * Declares a test. + * - `test(title, body)` + * - `test(title, details, body)` + * + * **Usage** + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('basic test', async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * // ... + * }); + * ``` + * + * **Tags** + * + * You can tag tests by providing additional test details. Alternatively, you can include tags in the test title. Note + * that each tag must start with `@` symbol. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('basic test', { + * tag: '@smoke', + * }, async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * // ... + * }); + * + * test('another test @smoke', async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * // ... + * }); + * ``` + * + * Test tags are displayed in the test report, and are available to a custom reporter via `TestCase.tags` property. + * + * You can also filter tests by their tags during test execution: + * - in the [command line](https://playwright.dev/docs/test-cli#reference); + * - in the config with [testConfig.grep](https://playwright.dev/docs/api/class-testconfig#test-config-grep) and + * [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep); + * + * Learn more about [tagging](https://playwright.dev/docs/test-annotations#tag-tests). + * + * **Annotations** + * + * You can annotate tests by providing additional test details. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('basic test', { + * annotation: { + * type: 'issue', + * description: 'https://github.com/microsoft/playwright/issues/23180', + * }, + * }, async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * // ... + * }); + * ``` + * + * Test annotations are displayed in the test report, and are available to a custom reporter via + * `TestCase.annotations` property. + * + * You can also add annotations during runtime by manipulating + * [testInfo.annotations](https://playwright.dev/docs/api/class-testinfo#test-info-annotations). + * + * 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}. + */ + (title: string, details: TestDetails, body: (args: TestArgs, testInfo: TestInfo) => Promise<void> | void): void; +} + +/** + * Playwright Test provides a `test` function to declare tests and `expect` function to write assertions. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('basic test', async ({ page }) => { + * await page.goto('https://playwright.dev/'); + * const name = await page.innerText('.navbar__title'); + * expect(name).toBe('Playwright'); + * }); + * ``` + * + */ +export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue> extends TestFunction<TestArgs & WorkerArgs> { + /** + * Declares a focused test. If there are some focused tests or suites, all of them will be run but nothing else. + * - `test.only(title, body)` + * - `test.only(title, details, body)` + * + * **Usage** + * + * ```js + * test.only('focus this test', async ({ page }) => { + * // Run only focused tests in the entire project. + * }); + * ``` + * + * @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}. + */ + only: TestFunction<TestArgs & WorkerArgs>; + /** + * Declares a group of tests. + * - `test.describe(title, callback)` + * - `test.describe(callback)` + * - `test.describe(title, details, callback)` + * + * **Usage** + * + * You can declare a group of tests with a title. The title will be visible in the test report as a part of each + * test's title. + * + * ```js + * test.describe('two tests', () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); + * }); + * ``` + * + * **Anonymous group** + * + * You can also declare a test group without a title. This is convenient to give a group of tests a common option with + * [test.use(options)](https://playwright.dev/docs/api/class-test#test-use). + * + * ```js + * test.describe(() => { + * test.use({ colorScheme: 'dark' }); + * + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); + * }); + * ``` + * + * **Tags** + * + * You can tag all tests in a group by providing additional details. Note that each tag must start with `@` symbol. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.describe('two tagged tests', { + * tag: '@smoke', + * }, () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); + * }); + * ``` + * + * Learn more about [tagging](https://playwright.dev/docs/test-annotations#tag-tests). + * + * **Annotations** + * + * You can annotate all tests in a group by providing additional details. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.describe('two annotated tests', { + * annotation: { + * type: 'issue', + * description: 'https://github.com/microsoft/playwright/issues/23180', + * }, + * }, () => { + * test('one', async ({ page }) => { + * // ... + * }); + * + * test('two', async ({ page }) => { + * // ... + * }); + * }); + * ``` + * + * Learn more about [test annotations](https://playwright.dev/docs/test-annotations). + * @param title Group title. + * @param details Additional details for all tests in the group. + * @param callback A callback that is run immediately when calling + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe). Any tests + * declared in this callback will belong to the group. + */ + describe: SuiteFunction & { + /** + * Declares a focused group of tests. If there are some focused tests or suites, all of them will be run but nothing + * else. + * - `test.describe.only(title, callback)` + * - `test.describe.only(callback)` + * - `test.describe.only(title, details, callback)` + * + * **Usage** + * + * ```js + * test.describe.only('focused group', () => { + * test('in the focused group', async ({ page }) => { + * // This test will run + * }); + * }); + * test('not in the focused group', async ({ page }) => { + * // This test will not run + * }); + * ``` + * + * You can also omit the title. + * + * ```js + * test.describe.only(() => { + * // ... * }); * ``` * * @param title Group title. + * @param details See [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) for + * details description. * @param callback A callback that is run immediately when calling - * [test.describe.parallel.only(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-parallel-only). + * [test.describe.only([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-only). * Any tests added in this callback will belong to the group. */ - only: SuiteFunction; - }; - /** - * Configures the enclosing scope. Can be executed either on the top level or inside a describe. Configuration applies - * to the entire scope, regardless of whether it run before or after the test declaration. + only: SuiteFunction; + /** + * Declares a skipped test group, similarly to + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe). Tests in the + * skipped group are never run. + * - `test.describe.skip(title, callback)` + * - `test.describe.skip(title)` + * - `test.describe.skip(title, details, callback)` + * + * **Usage** + * + * ```js + * test.describe.skip('skipped group', () => { + * test('example', async ({ page }) => { + * // This test will not run + * }); + * }); + * ``` + * + * You can also omit the title. + * + * ```js + * test.describe.skip(() => { + * // ... + * }); + * ``` + * + * @param title Group title. + * @param details See [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) for + * details description. + * @param callback A callback that is run immediately when calling + * [test.describe.skip(title[, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-skip). + * Any tests added in this callback will belong to the group, and will not be run. + */ + skip: SuiteFunction; + /** + * Declares a test group similarly to + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe). Tests in + * this group are marked as "fixme" and will not be executed. + * - `test.describe.fixme(title, callback)` + * - `test.describe.fixme(callback)` + * - `test.describe.fixme(title, details, callback)` + * + * **Usage** + * + * ```js + * test.describe.fixme('broken tests that should be fixed', () => { + * test('example', async ({ page }) => { + * // This test will not run + * }); + * }); + * ``` + * + * You can also omit the title. + * + * ```js + * test.describe.fixme(() => { + * // ... + * }); + * ``` + * + * @param title Group title. + * @param details See [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) for + * details description. + * @param callback A callback that is run immediately when calling + * [test.describe.fixme([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-fixme). + * Any tests added in this callback will belong to the group, and will not be run. + */ + fixme: SuiteFunction; + /** + * **NOTE** See [test.describe.configure([options])](https://playwright.dev/docs/api/class-test#test-describe-configure) for + * the preferred way of configuring the execution mode. + * + * Declares a group of tests that should always be run serially. If one of the tests fails, all subsequent tests are + * skipped. All tests in a group are retried together. + * + * **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)` + * + * **Usage** + * + * ```js + * test.describe.serial('group', () => { + * test('runs first', async ({ page }) => {}); + * test('runs second', async ({ page }) => {}); + * }); + * ``` + * + * You can also omit the title. + * + * ```js + * test.describe.serial(() => { + * // ... + * }); + * ``` + * + * @param title Group title. + * @param details See [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) for + * details description. + * @param callback A callback that is run immediately when calling + * [test.describe.serial([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-serial). + * Any tests added in this callback will belong to the group. + */ + serial: SuiteFunction & { + /** + * **NOTE** See [test.describe.configure([options])](https://playwright.dev/docs/api/class-test#test-describe-configure) for + * the preferred way of configuring the execution mode. + * + * Declares a focused group of tests that should always be run serially. If one of the tests fails, all subsequent + * tests are skipped. All tests in a group are retried together. If there are some focused tests or suites, all of + * them will be run but nothing else. + * + * **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)` + * + * **Usage** + * + * ```js + * test.describe.serial.only('group', () => { + * test('runs first', async ({ page }) => { + * }); + * test('runs second', async ({ page }) => { + * }); + * }); + * ``` + * + * You can also omit the title. + * + * ```js + * test.describe.serial.only(() => { + * // ... + * }); + * ``` + * + * @param title Group title. + * @param details See [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) for + * details description. + * @param callback A callback that is run immediately when calling + * [test.describe.serial.only(title[, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-serial-only). + * Any tests added in this callback will belong to the group. + */ + only: SuiteFunction; + }; + /** + * **NOTE** See [test.describe.configure([options])](https://playwright.dev/docs/api/class-test#test-describe-configure) for + * the preferred way of configuring the execution mode. + * + * Declares a group of tests that could be run in parallel. By default, tests in a single test file run one after + * another, but using + * [test.describe.parallel([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-parallel) + * allows them to run in parallel. + * - `test.describe.parallel(title, callback)` + * - `test.describe.parallel(callback)` + * - `test.describe.parallel(title, details, callback)` + * + * **Usage** + * + * ```js + * test.describe.parallel('group', () => { + * test('runs in parallel 1', async ({ page }) => {}); + * test('runs in parallel 2', async ({ page }) => {}); + * }); + * ``` + * + * Note that parallel tests are executed in separate processes and cannot share any state or global variables. Each of + * the parallel tests executes all relevant hooks. + * + * You can also omit the title. + * + * ```js + * test.describe.parallel(() => { + * // ... + * }); + * ``` + * + * @param title Group title. + * @param details See [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) for + * details description. + * @param callback A callback that is run immediately when calling + * [test.describe.parallel([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-parallel). + * Any tests added in this callback will belong to the group. + */ + parallel: SuiteFunction & { + /** + * **NOTE** See [test.describe.configure([options])](https://playwright.dev/docs/api/class-test#test-describe-configure) for + * the preferred way of configuring the execution mode. + * + * Declares a focused group of tests that could be run in parallel. This is similar to + * [test.describe.parallel([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-parallel), + * but focuses the group. If there are some focused tests or suites, all of them will be run but nothing else. + * - `test.describe.parallel.only(title, callback)` + * - `test.describe.parallel.only(callback)` + * - `test.describe.parallel.only(title, details, callback)` + * + * **Usage** + * + * ```js + * test.describe.parallel.only('group', () => { + * test('runs in parallel 1', async ({ page }) => {}); + * test('runs in parallel 2', async ({ page }) => {}); + * }); + * ``` + * + * You can also omit the title. + * + * ```js + * test.describe.parallel.only(() => { + * // ... + * }); + * ``` + * + * @param title Group title. + * @param details See [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) for + * details description. + * @param callback A callback that is run immediately when calling + * [test.describe.parallel.only([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe-parallel-only). + * Any tests added in this callback will belong to the group. + */ + only: SuiteFunction; + }; + /** + * Configures the enclosing scope. Can be executed either on the top level or inside a describe. Configuration applies + * to the entire scope, regardless of whether it run before or after the test declaration. + * + * Learn more about the execution modes [here](https://playwright.dev/docs/test-parallel). + * + * **Usage** + * - Running tests in parallel. + * + * ```js + * // Run all the tests in the file concurrently using parallel workers. + * test.describe.configure({ mode: 'parallel' }); + * test('runs in parallel 1', async ({ page }) => {}); + * test('runs in parallel 2', async ({ page }) => {}); + * ``` + * + * - Running tests serially, retrying from the start. + * + * **NOTE** Running serially is not recommended. It is usually better to make your tests isolated, so they can be + * run independently. + * + * ```js + * // Annotate tests as inter-dependent. + * test.describe.configure({ mode: 'serial' }); + * test('runs first', async ({ page }) => {}); + * test('runs second', async ({ page }) => {}); + * ``` + * + * - Configuring retries and timeout for each test. + * + * ```js + * // Each test in the file will be retried twice and have a timeout of 20 seconds. + * test.describe.configure({ retries: 2, timeout: 20_000 }); + * test('runs first', async ({ page }) => {}); + * test('runs second', async ({ page }) => {}); + * ``` + * + * - Run multiple describes in parallel, but tests inside each describe in order. + * + * ```js + * test.describe.configure({ mode: 'parallel' }); + * + * test.describe('A, runs in parallel with B', () => { + * test.describe.configure({ mode: 'default' }); + * test('in order A1', async ({ page }) => {}); + * test('in order A2', async ({ page }) => {}); + * }); + * + * test.describe('B, runs in parallel with A', () => { + * test.describe.configure({ mode: 'default' }); + * test('in order B1', async ({ page }) => {}); + * test('in order B2', async ({ page }) => {}); + * }); + * ``` + * + * @param options + */ + configure: (options: { mode?: 'default' | 'parallel' | 'serial', retries?: number, timeout?: number }) => void; + }; + /** + * Skip a test. Playwright will not run the test past the `test.skip()` call. + * + * Skipped tests are not supposed to be ever run. If you intent to fix the test, use + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme) + * instead. + * + * To declare a skipped test: + * - `test.skip(title, body)` + * - `test.skip(title, details, body)` + * + * To skip a test at runtime: + * - `test.skip(condition, description)` + * - `test.skip(callback, description)` + * - `test.skip()` + * + * **Usage** + * + * You can declare a skipped test, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip('never run', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be skipped in some configurations, but not all, you can skip the test inside the test body + * based on some condition. We recommend passing a `description` argument in this case. Playwright will run the test, + * but abort it immediately after the `test.skip` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('Safari-only test', async ({ page, browserName }) => { + * test.skip(browserName !== 'webkit', 'This feature is Safari-only'); + * // ... + * }); + * ``` + * + * You can skip all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group based + * on some condition with a single `test.skip(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip(({ browserName }) => browserName !== 'webkit', 'Safari-only'); + * + * test('Safari-only test 1', async ({ page }) => { + * // ... + * }); + * test('Safari-only test 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.skip()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.skip(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.skip(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + skip(title: string, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<void> | void): void; + /** + * Skip a test. Playwright will not run the test past the `test.skip()` call. + * + * Skipped tests are not supposed to be ever run. If you intent to fix the test, use + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme) + * instead. + * + * To declare a skipped test: + * - `test.skip(title, body)` + * - `test.skip(title, details, body)` + * + * To skip a test at runtime: + * - `test.skip(condition, description)` + * - `test.skip(callback, description)` + * - `test.skip()` + * + * **Usage** + * + * You can declare a skipped test, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip('never run', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be skipped in some configurations, but not all, you can skip the test inside the test body + * based on some condition. We recommend passing a `description` argument in this case. Playwright will run the test, + * but abort it immediately after the `test.skip` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('Safari-only test', async ({ page, browserName }) => { + * test.skip(browserName !== 'webkit', 'This feature is Safari-only'); + * // ... + * }); + * ``` + * + * You can skip all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group based + * on some condition with a single `test.skip(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip(({ browserName }) => browserName !== 'webkit', 'Safari-only'); + * + * test('Safari-only test 1', async ({ page }) => { + * // ... + * }); + * test('Safari-only test 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.skip()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.skip(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.skip(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + skip(title: string, details: TestDetails, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<void> | void): void; + /** + * Skip a test. Playwright will not run the test past the `test.skip()` call. + * + * Skipped tests are not supposed to be ever run. If you intent to fix the test, use + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme) + * instead. + * + * To declare a skipped test: + * - `test.skip(title, body)` + * - `test.skip(title, details, body)` + * + * To skip a test at runtime: + * - `test.skip(condition, description)` + * - `test.skip(callback, description)` + * - `test.skip()` + * + * **Usage** + * + * You can declare a skipped test, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip('never run', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be skipped in some configurations, but not all, you can skip the test inside the test body + * based on some condition. We recommend passing a `description` argument in this case. Playwright will run the test, + * but abort it immediately after the `test.skip` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('Safari-only test', async ({ page, browserName }) => { + * test.skip(browserName !== 'webkit', 'This feature is Safari-only'); + * // ... + * }); + * ``` + * + * You can skip all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group based + * on some condition with a single `test.skip(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip(({ browserName }) => browserName !== 'webkit', 'Safari-only'); + * + * test('Safari-only test 1', async ({ page }) => { + * // ... + * }); + * test('Safari-only test 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.skip()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.skip(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.skip(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + skip(): void; + /** + * Skip a test. Playwright will not run the test past the `test.skip()` call. + * + * Skipped tests are not supposed to be ever run. If you intent to fix the test, use + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme) + * instead. + * + * To declare a skipped test: + * - `test.skip(title, body)` + * - `test.skip(title, details, body)` + * + * To skip a test at runtime: + * - `test.skip(condition, description)` + * - `test.skip(callback, description)` + * - `test.skip()` + * + * **Usage** + * + * You can declare a skipped test, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip('never run', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be skipped in some configurations, but not all, you can skip the test inside the test body + * based on some condition. We recommend passing a `description` argument in this case. Playwright will run the test, + * but abort it immediately after the `test.skip` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('Safari-only test', async ({ page, browserName }) => { + * test.skip(browserName !== 'webkit', 'This feature is Safari-only'); + * // ... + * }); + * ``` + * + * You can skip all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group based + * on some condition with a single `test.skip(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip(({ browserName }) => browserName !== 'webkit', 'Safari-only'); + * + * test('Safari-only test 1', async ({ page }) => { + * // ... + * }); + * test('Safari-only test 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.skip()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.skip(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.skip(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + skip(condition: boolean, description?: string): void; + /** + * Skip a test. Playwright will not run the test past the `test.skip()` call. + * + * Skipped tests are not supposed to be ever run. If you intent to fix the test, use + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme) + * instead. + * + * To declare a skipped test: + * - `test.skip(title, body)` + * - `test.skip(title, details, body)` + * + * To skip a test at runtime: + * - `test.skip(condition, description)` + * - `test.skip(callback, description)` + * - `test.skip()` + * + * **Usage** + * + * You can declare a skipped test, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip('never run', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be skipped in some configurations, but not all, you can skip the test inside the test body + * based on some condition. We recommend passing a `description` argument in this case. Playwright will run the test, + * but abort it immediately after the `test.skip` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('Safari-only test', async ({ page, browserName }) => { + * test.skip(browserName !== 'webkit', 'This feature is Safari-only'); + * // ... + * }); + * ``` + * + * You can skip all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group based + * on some condition with a single `test.skip(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.skip(({ browserName }) => browserName !== 'webkit', 'Safari-only'); + * + * test('Safari-only test 1', async ({ page }) => { + * // ... + * }); + * test('Safari-only test 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.skip()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.skip(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.skip(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + skip(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; + /** + * Mark a test as "fixme", with the intention to fix it. Playwright will not run the test past the `test.fixme()` + * call. + * + * To declare a "fixme" test: + * - `test.fixme(title, body)` + * - `test.fixme(title, details, body)` + * + * To annotate test as "fixme" at runtime: + * - `test.fixme(condition, description)` + * - `test.fixme(callback, description)` + * - `test.fixme()` + * + * **Usage** + * + * You can declare a test as to be fixed, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fixme('to be fixed', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be fixed in some configurations, but not all, you can mark the test as "fixme" inside the test + * body based on some condition. We recommend passing a `description` argument in this case. Playwright will run the + * test, but abort it immediately after the `test.fixme` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('to be fixed in Safari', async ({ page, browserName }) => { + * test.fixme(browserName === 'webkit', 'This feature breaks in Safari for some reason'); + * // ... + * }); + * ``` + * + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "fixme" based on some condition with a single `test.fixme(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fixme(({ browserName }) => browserName === 'webkit', 'Should figure out the issue'); + * + * test('to be fixed in Safari 1', async ({ page }) => { + * // ... + * }); + * test('to be fixed in Safari 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.fixme()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.fixme(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.fixme(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + fixme(title: string, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<void> | void): void; + /** + * Mark a test as "fixme", with the intention to fix it. Playwright will not run the test past the `test.fixme()` + * call. + * + * To declare a "fixme" test: + * - `test.fixme(title, body)` + * - `test.fixme(title, details, body)` + * + * To annotate test as "fixme" at runtime: + * - `test.fixme(condition, description)` + * - `test.fixme(callback, description)` + * - `test.fixme()` + * + * **Usage** + * + * You can declare a test as to be fixed, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fixme('to be fixed', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be fixed in some configurations, but not all, you can mark the test as "fixme" inside the test + * body based on some condition. We recommend passing a `description` argument in this case. Playwright will run the + * test, but abort it immediately after the `test.fixme` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('to be fixed in Safari', async ({ page, browserName }) => { + * test.fixme(browserName === 'webkit', 'This feature breaks in Safari for some reason'); + * // ... + * }); + * ``` + * + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "fixme" based on some condition with a single `test.fixme(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fixme(({ browserName }) => browserName === 'webkit', 'Should figure out the issue'); + * + * test('to be fixed in Safari 1', async ({ page }) => { + * // ... + * }); + * test('to be fixed in Safari 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.fixme()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.fixme(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.fixme(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + fixme(title: string, details: TestDetails, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<void> | void): void; + /** + * Mark a test as "fixme", with the intention to fix it. Playwright will not run the test past the `test.fixme()` + * call. + * + * To declare a "fixme" test: + * - `test.fixme(title, body)` + * - `test.fixme(title, details, body)` + * + * To annotate test as "fixme" at runtime: + * - `test.fixme(condition, description)` + * - `test.fixme(callback, description)` + * - `test.fixme()` + * + * **Usage** + * + * You can declare a test as to be fixed, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fixme('to be fixed', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be fixed in some configurations, but not all, you can mark the test as "fixme" inside the test + * body based on some condition. We recommend passing a `description` argument in this case. Playwright will run the + * test, but abort it immediately after the `test.fixme` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('to be fixed in Safari', async ({ page, browserName }) => { + * test.fixme(browserName === 'webkit', 'This feature breaks in Safari for some reason'); + * // ... + * }); + * ``` + * + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "fixme" based on some condition with a single `test.fixme(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fixme(({ browserName }) => browserName === 'webkit', 'Should figure out the issue'); + * + * test('to be fixed in Safari 1', async ({ page }) => { + * // ... + * }); + * test('to be fixed in Safari 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.fixme()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.fixme(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.fixme(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + fixme(): void; + /** + * Mark a test as "fixme", with the intention to fix it. Playwright will not run the test past the `test.fixme()` + * call. + * + * To declare a "fixme" test: + * - `test.fixme(title, body)` + * - `test.fixme(title, details, body)` + * + * To annotate test as "fixme" at runtime: + * - `test.fixme(condition, description)` + * - `test.fixme(callback, description)` + * - `test.fixme()` + * + * **Usage** + * + * You can declare a test as to be fixed, and Playwright will not run it. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fixme('to be fixed', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test should be fixed in some configurations, but not all, you can mark the test as "fixme" inside the test + * body based on some condition. We recommend passing a `description` argument in this case. Playwright will run the + * test, but abort it immediately after the `test.fixme` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('to be fixed in Safari', async ({ page, browserName }) => { + * test.fixme(browserName === 'webkit', 'This feature breaks in Safari for some reason'); + * // ... + * }); + * ``` + * + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "fixme" based on some condition with a single `test.fixme(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fixme(({ browserName }) => browserName === 'webkit', 'Should figure out the issue'); + * + * test('to be fixed in Safari 1', async ({ page }) => { + * // ... + * }); + * test('to be fixed in Safari 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.fixme()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.fixme(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.fixme(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + fixme(condition: boolean, description?: string): void; + /** + * Mark a test as "fixme", with the intention to fix it. Playwright will not run the test past the `test.fixme()` + * call. * - * Learn more about the execution modes [here](https://playwright.dev/docs/test-parallel). + * To declare a "fixme" test: + * - `test.fixme(title, body)` + * - `test.fixme(title, details, body)` + * + * To annotate test as "fixme" at runtime: + * - `test.fixme(condition, description)` + * - `test.fixme(callback, description)` + * - `test.fixme()` * * **Usage** - * - Running tests in parallel. * - * ```js - * // Run all the tests in the file concurrently using parallel workers. - * test.describe.configure({ mode: 'parallel' }); - * test('runs in parallel 1', async ({ page }) => {}); - * test('runs in parallel 2', async ({ page }) => {}); - * ``` + * You can declare a test as to be fixed, and Playwright will not run it. * - * - Running tests serially, retrying from the start. + * ```js + * import { test, expect } from '@playwright/test'; * - * **NOTE** Running serially is not recommended. It is usually better to make your tests isolated, so they can be - * run independently. + * test.fixme('to be fixed', async ({ page }) => { + * // ... + * }); + * ``` * - * ```js - * // Annotate tests as inter-dependent. - * test.describe.configure({ mode: 'serial' }); - * test('runs first', async ({ page }) => {}); - * test('runs second', async ({ page }) => {}); - * ``` + * If your test should be fixed in some configurations, but not all, you can mark the test as "fixme" inside the test + * body based on some condition. We recommend passing a `description` argument in this case. Playwright will run the + * test, but abort it immediately after the `test.fixme` call. * - * - Configuring retries and timeout for each test. + * ```js + * import { test, expect } from '@playwright/test'; * - * ```js - * // Each test in the file will be retried twice and have a timeout of 20 seconds. - * test.describe.configure({ retries: 2, timeout: 20_000 }); - * test('runs first', async ({ page }) => {}); - * test('runs second', async ({ page }) => {}); - * ``` + * test('to be fixed in Safari', async ({ page, browserName }) => { + * test.fixme(browserName === 'webkit', 'This feature breaks in Safari for some reason'); + * // ... + * }); + * ``` * - * - Run multiple describes in parallel, but tests inside each describe in order. + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "fixme" based on some condition with a single `test.fixme(callback, description)` call. * - * ```js - * test.describe.configure({ mode: 'parallel' }); + * ```js + * import { test, expect } from '@playwright/test'; * - * test.describe('A, runs in parallel with B', () => { - * test.describe.configure({ mode: 'default' }); - * test('in order A1', async ({ page }) => {}); - * test('in order A2', async ({ page }) => {}); - * }); + * test.fixme(({ browserName }) => browserName === 'webkit', 'Should figure out the issue'); * - * test.describe('B, runs in parallel with A', () => { - * test.describe.configure({ mode: 'default' }); - * test('in order B1', async ({ page }) => {}); - * test('in order B2', async ({ page }) => {}); - * }); - * ``` + * test('to be fixed in Safari 1', async ({ page }) => { + * // ... + * }); + * test('to be fixed in Safari 2', async ({ page }) => { + * // ... + * }); + * ``` * - * @param options + * You can also call `test.fixme()` without arguments inside the test body to always mark the test as failed. We + * recommend using `test.fixme(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.fixme(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. */ - configure: (options: { mode?: 'default' | 'parallel' | 'serial', retries?: number, timeout?: number }) => void; - }; + fixme(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; + /** + * Marks a test as "should fail". Playwright runs this test and ensures that it is actually failing. This is useful + * for documentation purposes to acknowledge that some functionality is broken until it is fixed. + * + * To declare a "failing" test: + * - `test.fail(title, body)` + * - `test.fail(title, details, body)` + * + * To annotate test as "failing" at runtime: + * - `test.fail(condition, description)` + * - `test.fail(callback, description)` + * - `test.fail()` + * + * **Usage** + * + * You can declare a test as failing, so that Playwright ensures it actually fails. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fail('not yet ready', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test fails in some configurations, but not all, you can mark the test as failing inside the test body based + * on some condition. We recommend passing a `description` argument in this case. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('fail in WebKit', async ({ page, browserName }) => { + * test.fail(browserName === 'webkit', 'This feature is not implemented for Mac yet'); + * // ... + * }); + * ``` + * + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "should fail" based on some condition with a single `test.fail(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fail(({ browserName }) => browserName === 'webkit', 'not implemented yet'); + * + * test('fail in WebKit 1', async ({ page }) => { + * // ... + * }); + * test('fail in WebKit 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.fail()` without arguments inside the test body to always mark the test as failed. We + * recommend declaring a failing test with `test.fail(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.fail(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + fail(title: string, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<void> | void): void; /** - * Declares a skipped test, similarly to - * [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call). Skipped test is never - * run. + * Marks a test as "should fail". Playwright runs this test and ensures that it is actually failing. This is useful + * for documentation purposes to acknowledge that some functionality is broken until it is fixed. + * + * To declare a "failing" test: + * - `test.fail(title, body)` + * - `test.fail(title, details, body)` + * + * To annotate test as "failing" at runtime: + * - `test.fail(condition, description)` + * - `test.fail(callback, description)` + * - `test.fail()` * * **Usage** * + * You can declare a test as failing, so that Playwright ensures it actually fails. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fail('not yet ready', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test fails in some configurations, but not all, you can mark the test as failing inside the test body based + * on some condition. We recommend passing a `description` argument in this case. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('fail in WebKit', async ({ page, browserName }) => { + * test.fail(browserName === 'webkit', 'This feature is not implemented for Mac yet'); + * // ... + * }); + * ``` + * + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "should fail" based on some condition with a single `test.fail(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fail(({ browserName }) => browserName === 'webkit', 'not implemented yet'); + * + * test('fail in WebKit 1', async ({ page }) => { + * // ... + * }); + * test('fail in WebKit 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.fail()` without arguments inside the test body to always mark the test as failed. We + * recommend declaring a failing test with `test.fail(title, body)` instead. + * * ```js * import { test, expect } from '@playwright/test'; * - * test.skip('broken test', async ({ page }) => { + * test('less readable', async ({ page }) => { + * test.fail(); * // ... * }); * ``` * * @param title Test title. - * @param testFunction Test function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @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 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`. + * @param description Optional description that will be reflected in a test report. */ - skip(title: string, testFunction: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<void> | void): void; + fail(title: string, details: TestDetails, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<void> | void): void; /** - * Unconditionally skip a test. Test is immediately aborted when you call - * [test.skip()](https://playwright.dev/docs/api/class-test#test-skip-2). + * Marks a test as "should fail". Playwright runs this test and ensures that it is actually failing. This is useful + * for documentation purposes to acknowledge that some functionality is broken until it is fixed. + * + * To declare a "failing" test: + * - `test.fail(title, body)` + * - `test.fail(title, details, body)` + * + * To annotate test as "failing" at runtime: + * - `test.fail(condition, description)` + * - `test.fail(callback, description)` + * - `test.fail()` * * **Usage** * + * You can declare a test as failing, so that Playwright ensures it actually fails. + * * ```js * import { test, expect } from '@playwright/test'; * - * test('skipped test', async ({ page }) => { - * test.skip(); + * test.fail('not yet ready', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test fails in some configurations, but not all, you can mark the test as failing inside the test body based + * on some condition. We recommend passing a `description` argument in this case. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('fail in WebKit', async ({ page, browserName }) => { + * test.fail(browserName === 'webkit', 'This feature is not implemented for Mac yet'); + * // ... + * }); + * ``` + * + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "should fail" based on some condition with a single `test.fail(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fail(({ browserName }) => browserName === 'webkit', 'not implemented yet'); + * + * test('fail in WebKit 1', async ({ page }) => { + * // ... + * }); + * test('fail in WebKit 2', async ({ page }) => { + * // ... + * }); + * ``` + * + * You can also call `test.fail()` without arguments inside the test body to always mark the test as failed. We + * recommend declaring a failing test with `test.fail(title, body)` instead. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('less readable', async ({ page }) => { + * test.fail(); + * // ... + * }); + * ``` + * + * @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 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`. + * @param description Optional description that will be reflected in a test report. + */ + fail(condition: boolean, description?: string): void; + /** + * Marks a test as "should fail". Playwright runs this test and ensures that it is actually failing. This is useful + * for documentation purposes to acknowledge that some functionality is broken until it is fixed. + * + * To declare a "failing" test: + * - `test.fail(title, body)` + * - `test.fail(title, details, body)` + * + * To annotate test as "failing" at runtime: + * - `test.fail(condition, description)` + * - `test.fail(callback, description)` + * - `test.fail()` + * + * **Usage** + * + * You can declare a test as failing, so that Playwright ensures it actually fails. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fail('not yet ready', async ({ page }) => { + * // ... + * }); + * ``` + * + * If your test fails in some configurations, but not all, you can mark the test as failing inside the test body based + * on some condition. We recommend passing a `description` argument in this case. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test('fail in WebKit', async ({ page, browserName }) => { + * test.fail(browserName === 'webkit', 'This feature is not implemented for Mac yet'); + * // ... + * }); + * ``` + * + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "should fail" based on some condition with a single `test.fail(callback, description)` call. + * + * ```js + * import { test, expect } from '@playwright/test'; + * + * test.fail(({ browserName }) => browserName === 'webkit', 'not implemented yet'); + * + * test('fail in WebKit 1', async ({ page }) => { + * // ... + * }); + * test('fail in WebKit 2', async ({ page }) => { * // ... * }); * ``` * - * Unconditionally skip all tests in a file or - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group: + * You can also call `test.fail()` without arguments inside the test body to always mark the test as failed. We + * recommend declaring a failing test with `test.fail(title, body)` instead. * * ```js * import { test, expect } from '@playwright/test'; * - * test.skip(); - * - * test('skipped test 1', async ({ page }) => { - * // ... - * }); - * test('skipped test 2', async ({ page }) => { + * test('less readable', async ({ page }) => { + * test.fail(); * // ... * }); * ``` * + * @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 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`. + * @param description Optional description that will be reflected in a test report. */ - skip(): void; + fail(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; /** - * Conditionally skip a test with an optional description. + * Marks a test as "should fail". Playwright runs this test and ensures that it is actually failing. This is useful + * for documentation purposes to acknowledge that some functionality is broken until it is fixed. + * + * To declare a "failing" test: + * - `test.fail(title, body)` + * - `test.fail(title, details, body)` + * + * To annotate test as "failing" at runtime: + * - `test.fail(condition, description)` + * - `test.fail(callback, description)` + * - `test.fail()` * * **Usage** * + * You can declare a test as failing, so that Playwright ensures it actually fails. + * * ```js * import { test, expect } from '@playwright/test'; * - * test('skip in WebKit', async ({ page, browserName }) => { - * test.skip(browserName === 'webkit', 'This feature is not implemented for Mac'); + * test.fail('not yet ready', async ({ page }) => { * // ... * }); * ``` * - * Skip from [test.beforeEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-each-1) hook: + * If your test fails in some configurations, but not all, you can mark the test as failing inside the test body based + * on some condition. We recommend passing a `description` argument in this case. * * ```js * import { test, expect } from '@playwright/test'; * - * test.beforeEach(async ({ page }) => { - * test.skip(process.env.APP_VERSION === 'v1', 'There are no settings in v1'); - * await page.goto('/settings'); + * test('fail in WebKit', async ({ page, browserName }) => { + * test.fail(browserName === 'webkit', 'This feature is not implemented for Mac yet'); + * // ... * }); * ``` * - * @param condition A skip condition. Test is skipped when the condition is `true`. - * @param description Optional description that will be reflected in a test report. - */ - skip(condition: boolean, description?: string): void; - /** - * Conditionally skips all tests in a file or - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group. - * - * **Usage** + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "should fail" based on some condition with a single `test.fail(callback, description)` call. * * ```js * import { test, expect } from '@playwright/test'; * - * test.skip(({ browserName }) => browserName === 'webkit'); + * test.fail(({ browserName }) => browserName === 'webkit', 'not implemented yet'); * - * test('skip in WebKit 1', async ({ page }) => { + * test('fail in WebKit 1', async ({ page }) => { * // ... * }); - * test('skip in WebKit 2', async ({ page }) => { + * test('fail in WebKit 2', async ({ page }) => { * // ... * }); * ``` * - * @param callback A function that returns whether to skip, based on test fixtures. Test or tests are skipped when the return value is - * `true`. - * @param description Optional description that will be reflected in a test report. - */ - skip(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; - /** - * Declares a test to be fixed, similarly to - * [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call). This test will not be - * run. - * - * **Usage** + * You can also call `test.fail()` without arguments inside the test body to always mark the test as failed. We + * recommend declaring a failing test with `test.fail(title, body)` instead. * * ```js * import { test, expect } from '@playwright/test'; * - * test.fixme('test to be fixed', async ({ page }) => { + * test('less readable', async ({ page }) => { + * test.fail(); * // ... * }); * ``` * * @param title Test title. - * @param testFunction Test function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. + * @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 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`. + * @param description Optional description that will be reflected in a test report. */ - fixme(title: string, testFunction: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<void> | void): void; + fail(): void; /** - * Mark a test as "fixme", with the intention to fix it. Test is immediately aborted when you call - * [test.fixme()](https://playwright.dev/docs/api/class-test#test-fixme-2). - * - * **Usage** + * Marks a test as "slow". Slow test will be given triple the default timeout. * - * ```js - * import { test, expect } from '@playwright/test'; + * Note that [test.slow([condition, callback, description])](https://playwright.dev/docs/api/class-test#test-slow) + * cannot be used in a `beforeAll` or `afterAll` hook. Use + * [test.setTimeout(timeout)](https://playwright.dev/docs/api/class-test#test-set-timeout) instead. + * - `test.slow()` + * - `test.slow(condition, description)` + * - `test.slow(callback, description)` * - * test('test to be fixed', async ({ page }) => { - * test.fixme(); - * // ... - * }); - * ``` + * **Usage** * - * Mark all tests in a file or - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group as "fixme". + * You can mark a test as slow by calling `test.slow()` inside the test body. * * ```js * import { test, expect } from '@playwright/test'; * - * test.fixme(); - * - * test('test to be fixed 1', async ({ page }) => { - * // ... - * }); - * test('test to be fixed 2', async ({ page }) => { + * test('slow test', async ({ page }) => { + * test.slow(); * // ... * }); * ``` * - */ - fixme(): void; - /** - * Conditionally mark a test as "fixme" with an optional description. - * - * **Usage** + * If your test is slow in some configurations, but not all, you can mark it as slow based on a condition. We + * recommend passing a `description` argument in this case. * * ```js * import { test, expect } from '@playwright/test'; * - * test('broken in WebKit', async ({ page, browserName }) => { - * test.fixme(browserName === 'webkit', 'This feature is not implemented on Mac yet'); + * test('slow in Safari', async ({ page, browserName }) => { + * test.slow(browserName === 'webkit', 'This feature is slow in Safari'); * // ... * }); * ``` * - * @param condition Test is marked as "fixme" when the condition is `true`. - * @param description Optional description that will be reflected in a test report. - */ - fixme(condition: boolean, description?: string): void; - /** - * Conditionally mark all tests in a file or - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group as "fixme". - * - * **Usage** + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "slow" based on some condition by passing a callback. * * ```js * import { test, expect } from '@playwright/test'; * - * test.fixme(({ browserName }) => browserName === 'webkit'); + * test.slow(({ browserName }) => browserName === 'webkit', 'all tests are slow in Safari'); * - * test('broken in WebKit 1', async ({ page }) => { + * test('slow in Safari 1', async ({ page }) => { * // ... * }); - * test('broken in WebKit 2', async ({ page }) => { + * test('fail in Safari 2', async ({ page }) => { * // ... * }); * ``` * - * @param callback A function that returns whether to mark as "fixme", based on test fixtures. Test or tests are marked as "fixme" - * when the return value is `true`. + * @param condition Test is marked as "slow" when the condition is `true`. + * @param callback A function that returns whether to mark as "slow", based on test fixtures. Test or tests are marked as "slow" when + * the return value is `true`. * @param description Optional description that will be reflected in a test report. */ - fixme(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; + slow(): void; /** - * Unconditionally marks a test as "should fail". Playwright Test runs this test and ensures that it is actually - * failing. This is useful for documentation purposes to acknowledge that some functionality is broken until it is - * fixed. + * Marks a test as "slow". Slow test will be given triple the default timeout. + * + * Note that [test.slow([condition, callback, description])](https://playwright.dev/docs/api/class-test#test-slow) + * cannot be used in a `beforeAll` or `afterAll` hook. Use + * [test.setTimeout(timeout)](https://playwright.dev/docs/api/class-test#test-set-timeout) instead. + * - `test.slow()` + * - `test.slow(condition, description)` + * - `test.slow(callback, description)` * * **Usage** * + * You can mark a test as slow by calling `test.slow()` inside the test body. + * * ```js * import { test, expect } from '@playwright/test'; * - * test('not yet ready', async ({ page }) => { - * test.fail(); + * test('slow test', async ({ page }) => { + * test.slow(); * // ... * }); * ``` * - */ - fail(): void; - /** - * Conditionally mark a test as "should fail" with an optional description. - * - * **Usage** + * If your test is slow in some configurations, but not all, you can mark it as slow based on a condition. We + * recommend passing a `description` argument in this case. * * ```js * import { test, expect } from '@playwright/test'; * - * test('fail in WebKit', async ({ page, browserName }) => { - * test.fail(browserName === 'webkit', 'This feature is not implemented for Mac yet'); + * test('slow in Safari', async ({ page, browserName }) => { + * test.slow(browserName === 'webkit', 'This feature is slow in Safari'); * // ... * }); * ``` * - * @param condition Test is marked as "should fail" when the condition is `true`. - * @param description Optional description that will be reflected in a test report. - */ - fail(condition: boolean, description?: string): void; - /** - * Conditionally mark all tests in a file or - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group as "should - * fail". - * - * **Usage** + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "slow" based on some condition by passing a callback. * * ```js * import { test, expect } from '@playwright/test'; * - * test.fail(({ browserName }) => browserName === 'webkit'); + * test.slow(({ browserName }) => browserName === 'webkit', 'all tests are slow in Safari'); * - * test('fail in WebKit 1', async ({ page }) => { + * test('slow in Safari 1', async ({ page }) => { * // ... * }); - * test('fail in WebKit 2', async ({ page }) => { + * test('fail in Safari 2', async ({ page }) => { * // ... * }); * ``` * - * @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`. + * @param condition Test is marked as "slow" when the condition is `true`. + * @param callback A function that returns whether to mark as "slow", based on test fixtures. Test or tests are marked as "slow" when + * the return value is `true`. * @param description Optional description that will be reflected in a test report. */ - fail(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; + slow(condition: boolean, description?: string): void; /** - * Unconditionally marks a test as "slow". Slow test will be given triple the default timeout. - * - * **Details** + * Marks a test as "slow". Slow test will be given triple the default timeout. * - * [test.slow()](https://playwright.dev/docs/api/class-test#test-slow-1) cannot be used in a `beforeAll` or `afterAll` - * hook. Use [test.setTimeout(timeout)](https://playwright.dev/docs/api/class-test#test-set-timeout) instead. + * Note that [test.slow([condition, callback, description])](https://playwright.dev/docs/api/class-test#test-slow) + * cannot be used in a `beforeAll` or `afterAll` hook. Use + * [test.setTimeout(timeout)](https://playwright.dev/docs/api/class-test#test-set-timeout) instead. + * - `test.slow()` + * - `test.slow(condition, description)` + * - `test.slow(callback, description)` * * **Usage** * + * You can mark a test as slow by calling `test.slow()` inside the test body. + * * ```js * import { test, expect } from '@playwright/test'; * @@ -2965,47 +4561,36 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * }); * ``` * - */ - slow(): void; - /** - * Conditionally mark a test as "slow" with an optional description. Slow test will be given triple the default - * timeout. - * - * **Usage** + * If your test is slow in some configurations, but not all, you can mark it as slow based on a condition. We + * recommend passing a `description` argument in this case. * * ```js * import { test, expect } from '@playwright/test'; * - * test('slow in WebKit', async ({ page, browserName }) => { - * test.slow(browserName === 'webkit', 'This feature is slow on Mac'); + * test('slow in Safari', async ({ page, browserName }) => { + * test.slow(browserName === 'webkit', 'This feature is slow in Safari'); * // ... * }); * ``` * - * @param condition Test is marked as "slow" when the condition is `true`. - * @param description Optional description that will be reflected in a test report. - */ - slow(condition: boolean, description?: string): void; - /** - * Conditionally mark all tests in a file or - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group as "slow". Slow - * tests will be given triple the default timeout. - * - * **Usage** + * You can mark all tests in a file or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group as + * "slow" based on some condition by passing a callback. * * ```js * import { test, expect } from '@playwright/test'; * - * test.slow(({ browserName }) => browserName === 'webkit'); + * test.slow(({ browserName }) => browserName === 'webkit', 'all tests are slow in Safari'); * - * test('slow in WebKit 1', async ({ page }) => { + * test('slow in Safari 1', async ({ page }) => { * // ... * }); - * test('fail in WebKit 2', async ({ page }) => { + * test('fail in Safari 2', async ({ page }) => { * // ... * }); * ``` * + * @param condition Test is marked as "slow" when the condition is `true`. * @param callback A function that returns whether to mark as "slow", based on test fixtures. Test or tests are marked as "slow" when * the return value is `true`. * @param description Optional description that will be reflected in a test report. @@ -3048,7 +4633,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * ``` * * - Changing timeout for all tests in a - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group. + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group. * * ```js * test.describe('group', () => { @@ -3067,20 +4652,23 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue /** * Declares a `beforeEach` hook that is executed before each test. * - * **Details** - * * When called in the scope of a test file, runs before each test in the file. When called inside a - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group, runs before - * each test in the group. If multiple `beforeEach` hooks are added, they will run in the order of their - * registration. + * [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 function itself, and also the {@link TestInfo} object that + * 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. * - * Playwright will continue running all applicable hooks even if some of them have failed. - * - * You can use [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1) to + * 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`. + * - `test.beforeEach(hookFunction)` + * - `test.beforeEach(title, hookFunction)` + * + * **Details** + * + * When multiple `beforeEach` hooks are added, they will run in the order of their registration. + * + * Playwright will continue running all applicable hooks even if some of them have failed. * * **Usage** * @@ -3088,8 +4676,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * // example.spec.ts * import { test, expect } from '@playwright/test'; * - * test.beforeEach(async ({ page }, testInfo) => { - * console.log(`Running ${testInfo.title}`); + * test.beforeEach(async ({ page }) => { + * console.log(`Running ${test.info().title}`); * await page.goto('https://my.start.url/'); * }); * @@ -3098,15 +4686,40 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * }); * ``` * + * Alternatively, you can declare a hook **with a title**. + * + * ```js + * // example.spec.ts + * test.beforeEach('Open start URL', async ({ page }) => { + * console.log(`Running ${test.info().title}`); + * await page.goto('https://my.start.url/'); + * }); + * ``` + * + * @param title Hook title. * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. */ beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** - * Declares a `beforeEach` hook with a title that is executed before each test. + * Declares a `beforeEach` hook that is executed before each test. + * + * When called in the scope of a test file, runs before each test in the file. When called inside a + * [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 use [test.afterEach([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-each) to + * teardown any resources set up in `beforeEach`. + * - `test.beforeEach(hookFunction)` + * - `test.beforeEach(title, hookFunction)` * * **Details** * - * See [test.beforeEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-each-1). + * When multiple `beforeEach` hooks are added, they will run in the order of their registration. + * + * Playwright will continue running all applicable hooks even if some of them have failed. * * **Usage** * @@ -3114,8 +4727,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * // example.spec.ts * import { test, expect } from '@playwright/test'; * - * test.beforeEach('Open start URL', async ({ page }, testInfo) => { - * console.log(`Running ${testInfo.title}`); + * test.beforeEach(async ({ page }) => { + * console.log(`Running ${test.info().title}`); * await page.goto('https://my.start.url/'); * }); * @@ -3124,6 +4737,16 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * }); * ``` * + * Alternatively, you can declare a hook **with a title**. + * + * ```js + * // example.spec.ts + * test.beforeEach('Open start URL', async ({ page }) => { + * console.log(`Running ${test.info().title}`); + * await page.goto('https://my.start.url/'); + * }); + * ``` + * * @param title Hook title. * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. */ @@ -3131,14 +4754,18 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue /** * Declares an `afterEach` hook that is executed after each test. * - * **Details** - * * When called in the scope of a test file, runs after each test in the file. When called inside a - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group, runs after each - * test in the group. If multiple `afterEach` hooks are added, they will run in the order of their registration. + * [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 function itself, and also the {@link TestInfo} object that + * 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. + * - `test.afterEach(hookFunction)` + * - `test.afterEach(title, hookFunction)` + * + * **Details** + * + * When multiple `afterEach` hooks are added, they will run in the order of their registration. * * Playwright will continue running all applicable hooks even if some of them have failed. * @@ -3148,10 +4775,10 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * // example.spec.ts * import { test, expect } from '@playwright/test'; * - * test.afterEach(async ({ page }, testInfo) => { - * console.log(`Finished ${testInfo.title} with status ${testInfo.status}`); + * test.afterEach(async ({ page }) => { + * console.log(`Finished ${test.info().title} with status ${test.info().status}`); * - * if (testInfo.status !== testInfo.expectedStatus) + * if (test.info().status !== test.info().expectedStatus) * console.log(`Did not run as expected, ended up at ${page.url()}`); * }); * @@ -3160,15 +4787,37 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * }); * ``` * + * Alternatively, you can delcare a hook **with a title**. + * + * ```js + * // example.spec.ts + * test.afterEach('Status check', async ({ page }) => { + * if (test.info().status !== test.info().expectedStatus) + * console.log(`Did not run as expected, ended up at ${page.url()}`); + * }); + * ``` + * + * @param title Hook title. * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. */ afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** - * Declares an `afterEach` hook with a title that is executed after each test. + * Declares an `afterEach` hook that is executed after each test. + * + * When called in the scope of a test file, runs after each test in the file. When called inside a + * [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. + * - `test.afterEach(hookFunction)` + * - `test.afterEach(title, hookFunction)` * * **Details** * - * See [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1). + * When multiple `afterEach` hooks are added, they will run in the order of their registration. + * + * Playwright will continue running all applicable hooks even if some of them have failed. * * **Usage** * @@ -3176,10 +4825,10 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * // example.spec.ts * import { test, expect } from '@playwright/test'; * - * test.afterEach('Status check', async ({ page }, testInfo) => { - * console.log(`Finished ${testInfo.title} with status ${testInfo.status}`); + * test.afterEach(async ({ page }) => { + * console.log(`Finished ${test.info().title} with status ${test.info().status}`); * - * if (testInfo.status !== testInfo.expectedStatus) + * if (test.info().status !== test.info().expectedStatus) * console.log(`Did not run as expected, ended up at ${page.url()}`); * }); * @@ -3188,6 +4837,16 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * }); * ``` * + * Alternatively, you can delcare a hook **with a title**. + * + * ```js + * // example.spec.ts + * test.afterEach('Status check', async ({ page }) => { + * if (test.info().status !== test.info().expectedStatus) + * console.log(`Did not run as expected, ended up at ${page.url()}`); + * }); + * ``` + * * @param title Hook title. * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. */ @@ -3195,20 +4854,24 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue /** * Declares a `beforeAll` hook that is executed once per worker process before all tests. * + * When called in the scope of a test file, runs before all tests in the file. When called inside a + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group, runs + * before all tests in the group. + * + * You can use [test.afterAll([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-all) to + * teardown any resources set up in `beforeAll`. + * - `test.beforeAll(hookFunction)` + * - `test.beforeAll(title, hookFunction)` + * * **Details** * - * When called in the scope of a test file, runs before all tests in the file. When called inside a - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group, runs before all - * tests in the group. If multiple `beforeAll` hooks are added, they will run in the order of their registration. + * When multiple `beforeAll` hooks are added, they will run in the order of their registration. * * Note that worker process is restarted on test failures, and `beforeAll` hook runs again in the new worker. Learn * more about [workers and failures](https://playwright.dev/docs/test-retries). * * Playwright will continue running all applicable hooks even if some of them have failed. * - * You can use [test.afterAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-all-1) to teardown - * any resources set up in `beforeAll`. - * * **Usage** * * ```js @@ -3228,15 +4891,39 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * }); * ``` * + * Alternatively, you can declare a hook **with a title**. + * + * ```js + * // example.spec.ts + * test.beforeAll('Setup', async () => { + * console.log('Before tests'); + * }); + * ``` + * + * @param title Hook title. * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. */ beforeAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** - * Declares a `beforeAll` hook with a title that is executed once per worker process before all tests. + * Declares a `beforeAll` hook that is executed once per worker process before all tests. + * + * When called in the scope of a test file, runs before all tests in the file. When called inside a + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group, runs + * before all tests in the group. + * + * You can use [test.afterAll([title, hookFunction])](https://playwright.dev/docs/api/class-test#test-after-all) to + * teardown any resources set up in `beforeAll`. + * - `test.beforeAll(hookFunction)` + * - `test.beforeAll(title, hookFunction)` * * **Details** * - * See [test.beforeAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-all-1). + * When multiple `beforeAll` hooks are added, they will run in the order of their registration. + * + * Note that worker process is restarted on test failures, and `beforeAll` hook runs again in the new worker. Learn + * more about [workers and failures](https://playwright.dev/docs/test-retries). + * + * Playwright will continue running all applicable hooks even if some of them have failed. * * **Usage** * @@ -3244,15 +4931,28 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * // example.spec.ts * import { test, expect } from '@playwright/test'; * - * test.beforeAll('Setup', async () => { + * test.beforeAll(async () => { * console.log('Before tests'); * }); * + * test.afterAll(async () => { + * console.log('After tests'); + * }); + * * test('my test', async ({ page }) => { * // ... * }); * ``` * + * Alternatively, you can declare a hook **with a title**. + * + * ```js + * // example.spec.ts + * test.beforeAll('Setup', async () => { + * console.log('Before tests'); + * }); + * ``` + * * @param title Hook title. * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. */ @@ -3260,16 +4960,20 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue /** * Declares an `afterAll` hook that is executed once per worker after all tests. * + * When called in the scope of a test file, runs after all tests in the file. When called inside a + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group, runs + * after all tests in the group. + * * **Details** * - * When called in the scope of a test file, runs after all tests in the file. When called inside a - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group, runs after all - * tests in the group. If multiple `afterAll` hooks are added, they will run in the order of their registration. + * When multiple `afterAll` hooks are added, they will run in the order of their registration. * * Note that worker process is restarted on test failures, and `afterAll` hook runs again in the new worker. Learn * more about [workers and failures](https://playwright.dev/docs/test-retries). * * Playwright will continue running all applicable hooks even if some of them have failed. + * - `test.afterAll(hookFunction)` + * - `test.afterAll(title, hookFunction)` * * **Usage** * @@ -3280,19 +4984,49 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue * }); * ``` * + * Alternatively, you can declare a hook **with a title**. + * + * ```js + * test.afterAll('Teardown', async () => { + * console.log('Done with tests'); + * // ... + * }); + * ``` + * + * @param title Hook title. * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. */ afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** - * Declares an `afterAll` hook with a title that is executed once per worker after all tests. + * Declares an `afterAll` hook that is executed once per worker after all tests. + * + * When called in the scope of a test file, runs after all tests in the file. When called inside a + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group, runs + * after all tests in the group. * * **Details** * - * See [test.afterAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-all-1). + * When multiple `afterAll` hooks are added, they will run in the order of their registration. + * + * Note that worker process is restarted on test failures, and `afterAll` hook runs again in the new worker. Learn + * more about [workers and failures](https://playwright.dev/docs/test-retries). + * + * Playwright will continue running all applicable hooks even if some of them have failed. + * - `test.afterAll(hookFunction)` + * - `test.afterAll(title, hookFunction)` * * **Usage** * * ```js + * test.afterAll(async () => { + * console.log('Done with tests'); + * // ... + * }); + * ``` + * + * Alternatively, you can declare a hook **with a title**. + * + * ```js * test.afterAll('Teardown', async () => { * console.log('Done with tests'); * // ... @@ -3305,8 +5039,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; /** * Specifies options or fixtures to use in a single test file or a - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group. Most useful to - * set an option, for example set `locale` to configure `context` fixture. + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) group. Most + * useful to set an option, for example set `locale` to configure `context` fixture. * * **Usage** * @@ -6827,6 +8561,16 @@ interface TestProject { */ maxDiffPixelRatio?: number; }; + + /** + * Configuration for the [expect(value).toPass()](https://playwright.dev/docs/test-assertions) method. + */ + toPass?: { + /** + * timeout for toPass method in milliseconds. + */ + timeout?: number; + }; }; /** diff --git a/playwright/packages/playwright/types/testReporter.d.ts b/playwright/packages/playwright/types/testReporter.d.ts index d38f07be54..728f5299ef 100644 --- a/playwright/packages/playwright/types/testReporter.d.ts +++ b/playwright/packages/playwright/types/testReporter.d.ts @@ -26,7 +26,8 @@ export type { FullConfig, TestStatus } from './test'; * - {@link TestCase} #1 * - {@link TestCase} #2 * - Suite corresponding to a - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group + * [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 * - < more test cases ... > @@ -71,8 +72,9 @@ export interface Suite { /** * Test cases in the suite. Note that only test cases defined directly in this suite are in the list. Any test cases - * defined in nested [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) - * groups are listed in the child [suite.suites](https://playwright.dev/docs/api/class-suite#suite-suites). + * defined in nested + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) groups are + * listed in the child [suite.suites](https://playwright.dev/docs/api/class-suite#suite-suites). */ tests: Array<TestCase>; @@ -81,27 +83,31 @@ export interface Suite { * - Empty for root suite. * - Project name for project suite. * - File path for file suite. - * - Title passed to [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) - * for a group suite. + * - Title passed to + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe) for a + * group suite. */ title: string; } /** * `TestCase` corresponds to every - * [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call) call in a test file. When - * a single [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call) is running in - * multiple projects or repeated multiple times, it will have multiple `TestCase` objects in corresponding projects' - * suites. + * [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) call in a test file. + * When a single [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) is + * running in multiple projects or repeated multiple times, it will have multiple `TestCase` objects in corresponding + * projects' suites. */ export interface TestCase { /** * Expected test status. - * - Tests marked as [test.skip(title, testFunction)](https://playwright.dev/docs/api/class-test#test-skip-1) or - * [test.fixme(title, testFunction)](https://playwright.dev/docs/api/class-test#test-fixme-1) are expected to be - * `'skipped'`. - * - Tests marked as [test.fail()](https://playwright.dev/docs/api/class-test#test-fail-1) are expected to be - * `'failed'`. + * - Tests marked as + * [test.skip([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-skip) + * or + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme) + * are expected to be `'skipped'`. + * - Tests marked as + * [test.fail([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fail) + * are expected to be `'failed'`. * - Other tests are expected to be `'passed'`. * * See also [testResult.status](https://playwright.dev/docs/api/class-testresult#test-result-status) for the actual @@ -127,9 +133,18 @@ export interface TestCase { titlePath(): Array<string>; /** - * The list of annotations applicable to the current test. Includes annotations from the test, annotations from all - * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) groups the test - * belongs to and file-level annotations for the test file. + * The list of annotations applicable to the current test. Includes: + * - annotations defined on the test or suite via + * [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) and + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe); + * - annotations implicitly added by methods + * [test.skip([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-skip), + * [test.fixme([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fixme) + * and + * [test.fail([title, details, body, condition, callback, description])](https://playwright.dev/docs/api/class-test#test-fail); + * - annotations appended to + * [testInfo.annotations](https://playwright.dev/docs/api/class-testinfo#test-info-annotations) during the test + * execution. * * Annotations are available during test execution through * [testInfo.annotations](https://playwright.dev/docs/api/class-testinfo#test-info-annotations). @@ -182,19 +197,29 @@ export interface TestCase { */ retries: number; + /** + * The list of tags defined on the test or suite via + * [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) or + * [test.describe([title, details, callback])](https://playwright.dev/docs/api/class-test#test-describe), as well as + * `@`-tokens extracted from test and suite titles. + * + * Learn more about [test tags](https://playwright.dev/docs/test-annotations#tag-tests). + */ + tags: Array<string>; + /** * The timeout given to the test. Affected by * [testConfig.timeout](https://playwright.dev/docs/api/class-testconfig#test-config-timeout), * [testProject.timeout](https://playwright.dev/docs/api/class-testproject#test-project-timeout), * [test.setTimeout(timeout)](https://playwright.dev/docs/api/class-test#test-set-timeout), - * [test.slow()](https://playwright.dev/docs/api/class-test#test-slow-1) and + * [test.slow([condition, callback, description])](https://playwright.dev/docs/api/class-test#test-slow) and * [testInfo.setTimeout(timeout)](https://playwright.dev/docs/api/class-testinfo#test-info-set-timeout). */ timeout: number; /** * Test title as passed to the - * [test.(call)(title, testFunction)](https://playwright.dev/docs/api/class-test#test-call) call. + * [test.(call)(title[, details, body])](https://playwright.dev/docs/api/class-test#test-call) call. */ title: string; } diff --git a/playwright/packages/protocol/src/channels.ts b/playwright/packages/protocol/src/channels.ts index cf2cd36501..379b5a48e8 100644 --- a/playwright/packages/protocol/src/channels.ts +++ b/playwright/packages/protocol/src/channels.ts @@ -640,7 +640,6 @@ export interface DebugControllerEventTarget { on(event: 'stateChanged', callback: (params: DebugControllerStateChangedEvent) => void): this; on(event: 'sourceChanged', callback: (params: DebugControllerSourceChangedEvent) => void): this; on(event: 'paused', callback: (params: DebugControllerPausedEvent) => void): this; - on(event: 'browsersChanged', callback: (params: DebugControllerBrowsersChangedEvent) => void): this; } export interface DebugControllerChannel extends DebugControllerEventTarget, Channel { _type_DebugController: boolean; @@ -674,13 +673,6 @@ export type DebugControllerSourceChangedEvent = { export type DebugControllerPausedEvent = { paused: boolean, }; -export type DebugControllerBrowsersChangedEvent = { - browsers: { - contexts: { - pages: string[], - }[], - }[], -}; export type DebugControllerInitializeParams = { codegenId: string, sdkLanguage: 'javascript' | 'python' | 'java' | 'csharp', @@ -740,7 +732,6 @@ export interface DebugControllerEvents { 'stateChanged': DebugControllerStateChangedEvent; 'sourceChanged': DebugControllerSourceChangedEvent; 'paused': DebugControllerPausedEvent; - 'browsersChanged': DebugControllerBrowsersChangedEvent; } // ----------- SocksSupport ----------- @@ -1462,7 +1453,6 @@ export type BrowserContextBindingCallEvent = { binding: BindingCallChannel, }; export type BrowserContextConsoleEvent = { - page: PageChannel, type: string, text: string, args: JSHandleChannel[], @@ -1471,6 +1461,7 @@ export type BrowserContextConsoleEvent = { lineNumber: number, columnNumber: number, }, + page: PageChannel, }; export type BrowserContextCloseEvent = {}; export type BrowserContextDialogEvent = { @@ -1761,6 +1752,7 @@ export interface PageEventTarget { on(event: 'fileChooser', callback: (params: PageFileChooserEvent) => void): this; on(event: 'frameAttached', callback: (params: PageFrameAttachedEvent) => void): this; 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: 'video', callback: (params: PageVideoEvent) => void): this; on(event: 'webSocket', callback: (params: PageWebSocketEvent) => void): this; @@ -1776,6 +1768,8 @@ 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>; + registerLocatorHandler(params: PageRegisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageRegisterLocatorHandlerResult>; + resolveLocatorHandlerNoReply(params: PageResolveLocatorHandlerNoReplyParams, metadata?: CallMetadata): Promise<PageResolveLocatorHandlerNoReplyResult>; reload(params: PageReloadParams, metadata?: CallMetadata): Promise<PageReloadResult>; expectScreenshot(params: PageExpectScreenshotParams, metadata?: CallMetadata): Promise<PageExpectScreenshotResult>; screenshot(params: PageScreenshotParams, metadata?: CallMetadata): Promise<PageScreenshotResult>; @@ -1822,6 +1816,9 @@ export type PageFrameAttachedEvent = { export type PageFrameDetachedEvent = { frame: FrameChannel, }; +export type PageLocatorHandlerTriggeredEvent = { + uid: number, +}; export type PageRouteEvent = { route: RouteChannel, }; @@ -1907,6 +1904,22 @@ export type PageGoForwardOptions = { export type PageGoForwardResult = { response?: ResponseChannel, }; +export type PageRegisterLocatorHandlerParams = { + selector: string, +}; +export type PageRegisterLocatorHandlerOptions = { + +}; +export type PageRegisterLocatorHandlerResult = { + uid: number, +}; +export type PageResolveLocatorHandlerNoReplyParams = { + uid: number, +}; +export type PageResolveLocatorHandlerNoReplyOptions = { + +}; +export type PageResolveLocatorHandlerNoReplyResult = void; export type PageReloadParams = { timeout?: number, waitUntil?: LifecycleEvent, @@ -1926,26 +1939,22 @@ export type PageExpectScreenshotParams = { frame: FrameChannel, selector: string, }, - comparatorOptions?: { - comparator?: string, - maxDiffPixels?: number, - maxDiffPixelRatio?: number, - threshold?: number, - }, - screenshotOptions?: { - fullPage?: boolean, - clip?: Rect, - omitBackground?: boolean, - caret?: 'hide' | 'initial', - animations?: 'disabled' | 'allow', - scale?: 'css' | 'device', - mask?: { - frame: FrameChannel, - selector: string, - }[], - maskColor?: string, - style?: string, - }, + comparator?: string, + maxDiffPixels?: number, + maxDiffPixelRatio?: number, + threshold?: number, + fullPage?: boolean, + clip?: Rect, + omitBackground?: boolean, + caret?: 'hide' | 'initial', + animations?: 'disabled' | 'allow', + scale?: 'css' | 'device', + mask?: { + frame: FrameChannel, + selector: string, + }[], + maskColor?: string, + style?: string, }; export type PageExpectScreenshotOptions = { expected?: Binary, @@ -1954,26 +1963,22 @@ export type PageExpectScreenshotOptions = { frame: FrameChannel, selector: string, }, - comparatorOptions?: { - comparator?: string, - maxDiffPixels?: number, - maxDiffPixelRatio?: number, - threshold?: number, - }, - screenshotOptions?: { - fullPage?: boolean, - clip?: Rect, - omitBackground?: boolean, - caret?: 'hide' | 'initial', - animations?: 'disabled' | 'allow', - scale?: 'css' | 'device', - mask?: { - frame: FrameChannel, - selector: string, - }[], - maskColor?: string, - style?: string, - }, + comparator?: string, + maxDiffPixels?: number, + maxDiffPixelRatio?: number, + threshold?: number, + fullPage?: boolean, + clip?: Rect, + omitBackground?: boolean, + caret?: 'hide' | 'initial', + animations?: 'disabled' | 'allow', + scale?: 'css' | 'device', + mask?: { + frame: FrameChannel, + selector: string, + }[], + maskColor?: string, + style?: string, }; export type PageExpectScreenshotResult = { diff?: Binary, @@ -2169,6 +2174,8 @@ export type PagePdfParams = { left?: string, right?: string, }, + tagged?: boolean, + outline?: boolean, }; export type PagePdfOptions = { scale?: number, @@ -2188,6 +2195,8 @@ export type PagePdfOptions = { left?: string, right?: string, }, + tagged?: boolean, + outline?: boolean, }; export type PagePdfResult = { pdf: Binary, @@ -2258,6 +2267,7 @@ export interface PageEvents { 'fileChooser': PageFileChooserEvent; 'frameAttached': PageFrameAttachedEvent; 'frameDetached': PageFrameDetachedEvent; + 'locatorHandlerTriggered': PageLocatorHandlerTriggeredEvent; 'route': PageRouteEvent; 'video': PageVideoEvent; 'webSocket': PageWebSocketEvent; @@ -4068,15 +4078,27 @@ export type ElectronApplicationInitializer = { }; export interface ElectronApplicationEventTarget { on(event: 'close', callback: (params: ElectronApplicationCloseEvent) => void): this; + on(event: 'console', callback: (params: ElectronApplicationConsoleEvent) => void): this; } export interface ElectronApplicationChannel extends ElectronApplicationEventTarget, EventTargetChannel { _type_ElectronApplication: boolean; browserWindow(params: ElectronApplicationBrowserWindowParams, metadata?: CallMetadata): Promise<ElectronApplicationBrowserWindowResult>; evaluateExpression(params: ElectronApplicationEvaluateExpressionParams, metadata?: CallMetadata): Promise<ElectronApplicationEvaluateExpressionResult>; evaluateExpressionHandle(params: ElectronApplicationEvaluateExpressionHandleParams, metadata?: CallMetadata): Promise<ElectronApplicationEvaluateExpressionHandleResult>; + updateSubscription(params: ElectronApplicationUpdateSubscriptionParams, metadata?: CallMetadata): Promise<ElectronApplicationUpdateSubscriptionResult>; close(params?: ElectronApplicationCloseParams, metadata?: CallMetadata): Promise<ElectronApplicationCloseResult>; } export type ElectronApplicationCloseEvent = {}; +export type ElectronApplicationConsoleEvent = { + type: string, + text: string, + args: JSHandleChannel[], + location: { + url: string, + lineNumber: number, + columnNumber: number, + }, +}; export type ElectronApplicationBrowserWindowParams = { page: PageChannel, }; @@ -4108,12 +4130,21 @@ export type ElectronApplicationEvaluateExpressionHandleOptions = { export type ElectronApplicationEvaluateExpressionHandleResult = { handle: JSHandleChannel, }; +export type ElectronApplicationUpdateSubscriptionParams = { + event: 'console', + enabled: boolean, +}; +export type ElectronApplicationUpdateSubscriptionOptions = { + +}; +export type ElectronApplicationUpdateSubscriptionResult = void; export type ElectronApplicationCloseParams = {}; export type ElectronApplicationCloseOptions = {}; export type ElectronApplicationCloseResult = void; export interface ElectronApplicationEvents { 'close': ElectronApplicationCloseEvent; + 'console': ElectronApplicationConsoleEvent; } // ----------- Android ----------- diff --git a/playwright/packages/protocol/src/protocol.yml b/playwright/packages/protocol/src/protocol.yml index 7eb7a96a9b..c21cd006e9 100644 --- a/playwright/packages/protocol/src/protocol.yml +++ b/playwright/packages/protocol/src/protocol.yml @@ -785,23 +785,6 @@ DebugController: parameters: paused: boolean - # Deprecated - browsersChanged: - parameters: - browsers: - type: array - items: - type: object - properties: - contexts: - type: array - items: - type: object - properties: - pages: - type: array - items: string - SocksSupport: type: interface @@ -987,6 +970,21 @@ Browser: close: +ConsoleMessage: + type: mixin + properties: + type: string + text: string + args: + type: array + items: JSHandle + location: + type: object + properties: + url: string + lineNumber: number + columnNumber: number + EventTarget: type: interface @@ -1192,18 +1190,8 @@ BrowserContext: console: parameters: + $mixin: ConsoleMessage page: Page - type: string - text: string - args: - type: array - items: JSHandle - location: - type: object - properties: - url: string - lineNumber: number - columnNumber: number close: @@ -1349,6 +1337,16 @@ Page: slowMo: true snapshot: true + registerLocatorHandler: + parameters: + selector: string + returns: + uid: number + + resolveLocatorHandlerNoReply: + parameters: + uid: number + reload: parameters: timeout: number? @@ -1369,19 +1367,13 @@ Page: properties: frame: Frame selector: string - comparatorOptions: - type: object? - properties: - comparator: string? - maxDiffPixels: number? - maxDiffPixelRatio: number? - threshold: number? - screenshotOptions: - type: object? - properties: - fullPage: boolean? - clip: Rect? - $mixin: CommonScreenshotOptions + comparator: string? + maxDiffPixels: number? + maxDiffPixelRatio: number? + threshold: number? + fullPage: boolean? + clip: Rect? + $mixin: CommonScreenshotOptions returns: diff: binary? errorMessage: string? @@ -1568,6 +1560,8 @@ Page: bottom: string? left: string? right: string? + tagged: boolean? + outline: boolean? returns: pdf: binary @@ -1668,6 +1662,10 @@ Page: parameters: frame: Frame + locatorHandlerTriggered: + parameters: + uid: number + route: parameters: route: Route @@ -3211,11 +3209,21 @@ ElectronApplication: returns: handle: JSHandle + updateSubscription: + parameters: + event: + type: enum + literals: + - console + enabled: boolean + close: events: close: - + console: + parameters: + $mixin: ConsoleMessage Android: type: interface diff --git a/playwright/packages/recorder/src/recorderTypes.ts b/playwright/packages/recorder/src/recorderTypes.ts index d9c067baca..393f497fe8 100644 --- a/playwright/packages/recorder/src/recorderTypes.ts +++ b/playwright/packages/recorder/src/recorderTypes.ts @@ -16,12 +16,27 @@ import type { Language } from '../../playwright-core/src/utils/isomorphic/locatorGenerators'; -export type Point = { x: number, y: number }; +export type Point = { x: number; y: number }; -export type Mode = 'inspecting' | 'recording' | 'none' | 'assertingText' | 'recording-inspecting' | 'standby' | 'assertingVisibility' | 'assertingValue'; +export type Mode = + | 'inspecting' + | 'recording' + | 'none' + | 'assertingText' + | 'recording-inspecting' + | 'standby' + | 'assertingVisibility' + | 'assertingValue'; export type EventData = { - event: 'clear' | 'resume' | 'step' | 'pause' | 'setMode' | 'selectorUpdated' | 'fileChanged'; + event: + | 'clear' + | 'resume' + | 'step' + | 'pause' + | 'setMode' + | 'selectorUpdated' + | 'fileChanged'; params: any; }; @@ -33,7 +48,7 @@ export type UIState = { mode: Mode; actionPoint?: Point; actionSelector?: string; - language: 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl'; + language: Language; testIdAttributeName: string; overlay: OverlayState; }; @@ -49,8 +64,8 @@ export type CallLog = { reveal?: boolean; duration?: number; params: { - url?: string, - selector?: string, + url?: string; + selector?: string; }; }; diff --git a/playwright/packages/trace-viewer/src/snapshotRenderer.ts b/playwright/packages/trace-viewer/src/snapshotRenderer.ts index 9642c7c1ad..fdcea83288 100644 --- a/playwright/packages/trace-viewer/src/snapshotRenderer.ts +++ b/playwright/packages/trace-viewer/src/snapshotRenderer.ts @@ -214,6 +214,10 @@ function snapshotNodes(snapshot: FrameSnapshot): NodeSnapshot[] { function snapshotScript(...targetIds: (string | undefined)[]) { function applyPlaywrightAttributes(unwrapPopoutUrl: (url: string) => string, ...targetIds: (string | undefined)[]) { + const kPointerWarningTitle = 'Recorded click position in absolute coordinates did not' + + ' match the center of the clicked element. This is likely due to a difference between' + + ' the test runner and the trace viewer operating systems.'; + const scrollTops: Element[] = []; const scrollLefts: Element[] = []; const targetElements: Element[] = []; @@ -323,6 +327,9 @@ function snapshotScript(...targetIds: (string | undefined)[]) { pointElement.style.borderRadius = '10px'; pointElement.style.margin = '-10px 0 0 -10px'; pointElement.style.zIndex = '2147483646'; + pointElement.style.display = 'flex'; + pointElement.style.alignItems = 'center'; + pointElement.style.justifyContent = 'center'; if (hasTargetElements) { // Sometimes there are layout discrepancies between recording and rendering, e.g. fonts, // that may place the point at the wrong place. To avoid confusion, we just show the @@ -332,9 +339,17 @@ function snapshotScript(...targetIds: (string | undefined)[]) { const centerY = (box.top + box.height / 2); pointElement.style.left = centerX + 'px'; pointElement.style.top = centerY + 'px'; - // "Blue dot" to indicate that action point is not 100% correct. - if (Math.abs(centerX - pointX) >= 2 || Math.abs(centerY - pointY) >= 2) - pointElement.style.backgroundColor = '#3646f4'; + // "Warning symbol" indicates that action point is not 100% correct. + if (Math.abs(centerX - pointX) >= 10 || Math.abs(centerY - pointY) >= 10) { + const warningElement = document.createElement('x-pw-pointer-warning'); + warningElement.textContent = '⚠'; + warningElement.style.fontSize = '19px'; + warningElement.style.color = 'white'; + warningElement.style.marginTop = '-3.5px'; + warningElement.style.userSelect = 'none'; + pointElement.appendChild(warningElement); + pointElement.setAttribute('title', kPointerWarningTitle); + } } else { // For actions without a target element, e.g. page.mouse.move(), // show the point at the recorder location. diff --git a/playwright/packages/trace-viewer/src/ui/errorsTab.tsx b/playwright/packages/trace-viewer/src/ui/errorsTab.tsx index e600bb4be2..71b6059c29 100644 --- a/playwright/packages/trace-viewer/src/ui/errorsTab.tsx +++ b/playwright/packages/trace-viewer/src/ui/errorsTab.tsx @@ -45,7 +45,7 @@ export function useErrorsTabModel(model: modelUtil.MultiTraceModel | undefined): export const ErrorsTab: React.FunctionComponent<{ errorsModel: ErrorsTabModel, sdkLanguage: Language, - revealInSource: (action: modelUtil.ActionTraceEventInContext) => void, + revealInSource: (error: ErrorDescription) => void, }> = ({ errorsModel, sdkLanguage, revealInSource }) => { if (!errorsModel.errors.size) return <PlaceholderPanel text='No errors' />; @@ -56,7 +56,7 @@ export const ErrorsTab: React.FunctionComponent<{ let longLocation: string | undefined; const stackFrame = error.stack?.[0]; if (stackFrame) { - const file = stackFrame.file.replace(/.*\/(.*)/, '$1'); + const file = stackFrame.file.replace(/.*[/\\](.*)/, '$1'); location = file + ':' + stackFrame.line; longLocation = stackFrame.file + ':' + stackFrame.line; } @@ -70,7 +70,7 @@ export const ErrorsTab: React.FunctionComponent<{ }}> {error.action && renderAction(error.action, { sdkLanguage })} {location && <div className='action-location'> - @ <span title={longLocation} onClick={() => error.action && revealInSource(error.action)}>{location}</span> + @ <span title={longLocation} onClick={() => revealInSource(error)}>{location}</span> </div>} </div> <ErrorMessage error={message} /> diff --git a/playwright/packages/trace-viewer/src/ui/networkTab.css b/playwright/packages/trace-viewer/src/ui/networkTab.css index 99c4d45d81..130e2d2d25 100644 --- a/playwright/packages/trace-viewer/src/ui/networkTab.css +++ b/playwright/packages/trace-viewer/src/ui/networkTab.css @@ -14,96 +14,23 @@ limitations under the License. */ -.network-request-status .status-failure { - color: var(--vscode-statusBar-foreground); - background-color: var(--vscode-statusBarItem-errorBackground); -} - -.network-request-status .status-route { +.network-request-status-route { color: var(--vscode-statusBar-foreground); background-color: var(--vscode-statusBar-background); } -.network-request-status .status-route.api { +.network-request-status-route.api { color: var(--vscode-statusBar-foreground); background-color: var(--vscode-statusBarItem-remoteBackground); } -.network-request-column { - overflow: hidden; - text-overflow: ellipsis; - flex: 0.5; - padding: 0 5px; - display: flex; -} - -.network-request-start { - flex: 0 0 65px; - justify-content: right; - padding-right: 10px; -} - -.network-request-status { - flex: 0 0 75px; -} - -.network-request-method { - flex: 0 0 65px; -} - -.network-request-file { - display: flex; - flex: 1; -} - -.network-request-file-url { - overflow: hidden; - text-overflow: ellipsis; - flex: 1; -} - -.network-request-body .network-request-start, -.network-request-body .network-request-status, -.network-request-body .network-request-duration, -.network-request-body .network-request-size { - justify-content: end; -} - -.network-request-header { - margin: 3px 14px 0 5px; - height: 30px; - border-bottom: 1px solid var(--vscode-panel-border); - flex: none; - align-items: center; - cursor: pointer; - user-select: none; -} - -.network-request-header .codicon-triangle-up { - display: none; -} -.network-request-header .codicon-triangle-down { - display: none; -} - -.network-request-header > div { - display: flex; - align-items: center; - white-space: nowrap; - height: 100%; - overflow: hidden; - padding: 0 5px; - justify-content: space-between; -} - -.network-request-header > .filter-positive .codicon-triangle-down { - display: initial !important; -} - -.network-request-header > .filter-negative .codicon-triangle-up { - display: initial !important; +.network-grid-view .grid-view-column-method, +.network-grid-view .grid-view-column-status { + text-align: center; } -.network-request-header .network-request-column { - border-right: 1px solid var(--vscode-panel-border); +.network-grid-view .grid-view-column-duration, +.network-grid-view .grid-view-column-size, +.network-grid-view .grid-view-column-start { + text-align: end; } diff --git a/playwright/packages/trace-viewer/src/ui/networkTab.tsx b/playwright/packages/trace-viewer/src/ui/networkTab.tsx index c2b27fb023..b3caa501b5 100644 --- a/playwright/packages/trace-viewer/src/ui/networkTab.tsx +++ b/playwright/packages/trace-viewer/src/ui/networkTab.tsx @@ -22,7 +22,7 @@ import { NetworkResourceDetails } from './networkResourceDetails'; import { bytesToString, msToString } from '@web/uiUtils'; import { PlaceholderPanel } from './placeholderPanel'; import type { MultiTraceModel } from './modelUtil'; -import { GridView } from '@web/components/gridView'; +import { GridView, type RenderedGridCell } from '@web/components/gridView'; import { SplitView } from '@web/components/splitView'; type NetworkTabModel = { @@ -32,7 +32,7 @@ type NetworkTabModel = { type RenderedEntry = { name: { name: string, url: string }, method: string, - status: { code: number, text: string, className: string }, + status: { code: number, text: string }, contentType: string, duration: number, size: number, @@ -83,7 +83,9 @@ export const NetworkTab: React.FunctionComponent<{ onHighlighted={item => onEntryHovered(item?.resource)} columns={selectedEntry ? ['name'] : ['name', 'method', 'status', 'contentType', 'duration', 'size', 'start', 'route']} columnTitle={columnTitle} - columnWidth={column => column === 'name' ? 200 : 100} + columnWidth={columnWidth} + isError={item => item.status.code >= 400} + isInfo={item => !!item.route} render={(item, column) => renderCell(item, column)} sorting={sorting} setSorting={setSorting} @@ -117,23 +119,44 @@ const columnTitle = (column: ColumnName) => { return ''; }; -const renderCell = (entry: RenderedEntry, column: ColumnName) => { +const columnWidth = (column: ColumnName) => { if (column === 'name') - return <span title={entry.name.url}>{entry.name.name}</span>; + return 200; if (column === 'method') - return <span>{entry.method}</span>; + return 60; if (column === 'status') - return <span className={entry.status.className} title={entry.status.text}>{entry.status.code > 0 ? entry.status.code : ''}</span>; + return 60; if (column === 'contentType') - return <span>{entry.contentType}</span>; + return 200; + return 100; +}; + +const renderCell = (entry: RenderedEntry, column: ColumnName): RenderedGridCell => { + if (column === 'name') { + return { + body: entry.name.name, + title: entry.name.url, + }; + } + if (column === 'method') + return { body: entry.method }; + if (column === 'status') { + return { + body: entry.status.code > 0 ? entry.status.code : '', + title: entry.status.text + }; + } + if (column === 'contentType') + return { body: entry.contentType }; if (column === 'duration') - return <span>{msToString(entry.duration)}</span>; + return { body: msToString(entry.duration) }; if (column === 'size') - return <span>{bytesToString(entry.size)}</span>; + return { body: bytesToString(entry.size) }; if (column === 'start') - return <span>{msToString(entry.start)}</span>; + return { body: msToString(entry.start) }; if (column === 'route') - return entry.route && <span className={`status-route ${entry.route}`}>{entry.route}</span>; + return { body: entry.route }; + return { body: '' }; }; const renderEntry = (resource: Entry, boundaries: Boundaries): RenderedEntry => { @@ -155,7 +178,7 @@ const renderEntry = (resource: Entry, boundaries: Boundaries): RenderedEntry => return { name: { name: resourceName, url: resource.request.url }, method: resource.request.method, - status: { code: resource.response.status, text: resource.response.statusText, className: statusClassName(resource.response.status) }, + status: { code: resource.response.status, text: resource.response.statusText }, contentType: contentType, duration: resource.time, size: resource.response._transferSize! > 0 ? resource.response._transferSize! : resource.response.bodySize, @@ -165,14 +188,6 @@ const renderEntry = (resource: Entry, boundaries: Boundaries): RenderedEntry => }; }; -function statusClassName(status: number): string { - if (status >= 200 && status < 400) - return 'status-success'; - if (status >= 400) - return 'status-failure'; - return ''; -} - function formatRouteStatus(request: Entry): string { if (request._wasAborted) return 'aborted'; diff --git a/playwright/packages/trace-viewer/src/ui/snapshotTab.tsx b/playwright/packages/trace-viewer/src/ui/snapshotTab.tsx index 9ae19d9adf..bdb955c13c 100644 --- a/playwright/packages/trace-viewer/src/ui/snapshotTab.tsx +++ b/playwright/packages/trace-viewer/src/ui/snapshotTab.tsx @@ -22,7 +22,7 @@ import { Toolbar } from '@web/components/toolbar'; import { ToolbarButton } from '@web/components/toolbarButton'; import { useMeasure } from '@web/uiUtils'; import { InjectedScript } from '@injected/injectedScript'; -import { Recorder } from '@injected/recorder'; +import { Recorder } from '@injected/recorder/recorder'; import ConsoleAPI from '@injected/consoleApi'; import { asLocator } from '@isomorphic/locatorGenerators'; import type { Language } from '@isomorphic/locatorGenerators'; @@ -279,7 +279,7 @@ function createRecorders(recorders: { recorder: Recorder, frameSelector: string for (let i = 0; i < frameWindow.frames.length; ++i) { const childFrame = frameWindow.frames[i]; - const frameSelector = childFrame.frameElement ? win._injectedScript.generateSelector(childFrame.frameElement, { omitInternalEngines: true, testIdAttributeName }) + ' >> internal:control=enter-frame >> ' : ''; + const frameSelector = childFrame.frameElement ? win._injectedScript.generateSelectorSimple(childFrame.frameElement, { omitInternalEngines: true, testIdAttributeName }) + ' >> internal:control=enter-frame >> ' : ''; createRecorders(recorders, sdkLanguage, testIdAttributeName, isUnderTest, parentFrameSelector + frameSelector, childFrame); } } diff --git a/playwright/packages/trace-viewer/src/ui/sourceTab.tsx b/playwright/packages/trace-viewer/src/ui/sourceTab.tsx index 50d96f8ee3..aebbbd872f 100644 --- a/playwright/packages/trace-viewer/src/ui/sourceTab.tsx +++ b/playwright/packages/trace-viewer/src/ui/sourceTab.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import type { ActionTraceEvent } from '@trace/trace'; import { SplitView } from '@web/components/splitView'; import * as React from 'react'; import { useAsyncMemo } from '@web/uiUtils'; @@ -23,26 +22,27 @@ import { StackTraceView } from './stackTrace'; import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper'; import type { SourceHighlight } from '@web/components/codeMirrorWrapper'; import type { SourceLocation, SourceModel } from './modelUtil'; +import type { StackFrame } from '@protocol/channels'; export const SourceTab: React.FunctionComponent<{ - action: ActionTraceEvent | undefined, + stack: StackFrame[] | undefined, sources: Map<string, SourceModel>, hideStackFrames?: boolean, rootDir?: string, fallbackLocation?: SourceLocation, -}> = ({ action, sources, hideStackFrames, rootDir, fallbackLocation }) => { - const [lastAction, setLastAction] = React.useState<ActionTraceEvent | undefined>(); +}> = ({ stack, sources, hideStackFrames, rootDir, fallbackLocation }) => { + const [lastStack, setLastStack] = React.useState<StackFrame[] | undefined>(); const [selectedFrame, setSelectedFrame] = React.useState<number>(0); React.useEffect(() => { - if (lastAction !== action) { - setLastAction(action); + if (lastStack !== stack) { + setLastStack(stack); setSelectedFrame(0); } - }, [action, lastAction, setLastAction, setSelectedFrame]); + }, [stack, lastStack, setLastStack, setSelectedFrame]); const { source, highlight, targetLine, fileName } = useAsyncMemo<{ source: SourceModel, targetLine?: number, fileName?: string, highlight: SourceHighlight[] }>(async () => { - const actionLocation = action?.stack?.[selectedFrame]; + const actionLocation = stack?.[selectedFrame]; const shouldUseFallback = !actionLocation?.file; if (shouldUseFallback && !fallbackLocation) return { source: { file: '', errors: [], content: undefined }, targetLine: 0, highlight: [] }; @@ -67,20 +67,23 @@ export const SourceTab: React.FunctionComponent<{ let response = await fetch(`sha1/src@${sha1}.txt`); if (response.status === 404) response = await fetch(`file?path=${encodeURIComponent(file)}`); - source.content = await response.text(); + if (response.status >= 400) + source.content = `<Unable to read "${file}">`; + else + source.content = await response.text(); } catch { source.content = `<Unable to read "${file}">`; } } return { source, highlight, targetLine, fileName }; - }, [action, selectedFrame, rootDir, fallbackLocation], { source: { errors: [], content: 'Loading\u2026' }, highlight: [] }); + }, [stack, selectedFrame, rootDir, fallbackLocation], { source: { errors: [], content: 'Loading\u2026' }, highlight: [] }); return <SplitView sidebarSize={200} orientation='horizontal' sidebarHidden={hideStackFrames}> <div className='vbox' data-testid='source-code'> {fileName && <div className='source-tab-file-name'>{fileName}</div>} <CodeMirrorWrapper text={source.content || ''} language='javascript' highlight={highlight} revealLine={targetLine} readOnly={true} lineNumbers={true} /> </div> - <StackTraceView action={action} selectedFrame={selectedFrame} setSelectedFrame={setSelectedFrame} /> + <StackTraceView stack={stack} selectedFrame={selectedFrame} setSelectedFrame={setSelectedFrame} /> </SplitView>; }; diff --git a/playwright/packages/trace-viewer/src/ui/stackTrace.tsx b/playwright/packages/trace-viewer/src/ui/stackTrace.tsx index 4e35946d84..6a0bad1672 100644 --- a/playwright/packages/trace-viewer/src/ui/stackTrace.tsx +++ b/playwright/packages/trace-viewer/src/ui/stackTrace.tsx @@ -16,18 +16,17 @@ import * as React from 'react'; import './stackTrace.css'; -import type { ActionTraceEvent } from '@trace/trace'; import { ListView } from '@web/components/listView'; import type { StackFrame } from '@protocol/channels'; const StackFrameListView = ListView<StackFrame>; export const StackTraceView: React.FunctionComponent<{ - action: ActionTraceEvent | undefined, + stack: StackFrame[] | undefined, selectedFrame: number, setSelectedFrame: (index: number) => void -}> = ({ action, setSelectedFrame, selectedFrame }) => { - const frames = action?.stack || []; +}> = ({ stack, setSelectedFrame, selectedFrame }) => { + const frames = stack || []; return <StackFrameListView name='stack-trace' items={frames} diff --git a/playwright/packages/trace-viewer/src/ui/workbench.tsx b/playwright/packages/trace-viewer/src/ui/workbench.tsx index 7586e530d4..dc08b95fff 100644 --- a/playwright/packages/trace-viewer/src/ui/workbench.tsx +++ b/playwright/packages/trace-viewer/src/ui/workbench.tsx @@ -23,6 +23,7 @@ import { ErrorsTab, useErrorsTabModel } from './errorsTab'; import { ConsoleTab, useConsoleTabModel } from './consoleTab'; import type * as modelUtil from './modelUtil'; import type { ActionTraceEventInContext, MultiTraceModel } from './modelUtil'; +import type { StackFrame } from '@protocol/channels'; import { NetworkTab, useNetworkTabModel } from './networkTab'; import { SnapshotTab } from './snapshotTab'; import { SourceTab } from './sourceTab'; @@ -51,7 +52,8 @@ export const Workbench: React.FunctionComponent<{ isLive?: boolean, status?: UITestStatus, }> = ({ model, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive, status }) => { - const [selectedAction, setSelectedAction] = React.useState<ActionTraceEventInContext | undefined>(undefined); + const [selectedAction, setSelectedActionImpl] = React.useState<ActionTraceEventInContext | undefined>(undefined); + const [revealedStack, setRevealedStack] = React.useState<StackFrame[] | undefined>(undefined); const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEventInContext | undefined>(); const [highlightedEntry, setHighlightedEntry] = React.useState<Entry | undefined>(); const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions'); @@ -62,6 +64,11 @@ export const Workbench: React.FunctionComponent<{ const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>(); const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom'); + const setSelectedAction = React.useCallback((action: ActionTraceEventInContext | undefined) => { + setSelectedActionImpl(action); + setRevealedStack(action?.stack); + }, [setSelectedActionImpl, setRevealedStack]); + const sources = React.useMemo(() => model?.sources || new Map(), [model]); React.useEffect(() => { @@ -137,8 +144,11 @@ export const Workbench: React.FunctionComponent<{ id: 'errors', title: 'Errors', errorCount: errorsModel.errors.size, - render: () => <ErrorsTab errorsModel={errorsModel} sdkLanguage={sdkLanguage} revealInSource={action => { - setSelectedAction(action); + render: () => <ErrorsTab errorsModel={errorsModel} sdkLanguage={sdkLanguage} revealInSource={error => { + if (error.action) + setSelectedAction(error.action); + else + setRevealedStack(error.stack); selectPropertiesTab('source'); }} /> }; @@ -146,7 +156,7 @@ export const Workbench: React.FunctionComponent<{ id: 'source', title: 'Source', render: () => <SourceTab - action={activeAction} + stack={revealedStack} sources={sources} hideStackFrames={hideStackFrames} rootDir={rootDir} diff --git a/playwright/packages/trace-viewer/src/ui/wsPort.ts b/playwright/packages/trace-viewer/src/ui/wsPort.ts index fc08d4cdf4..298fa71d17 100644 --- a/playwright/packages/trace-viewer/src/ui/wsPort.ts +++ b/playwright/packages/trace-viewer/src/ui/wsPort.ts @@ -20,7 +20,9 @@ const callbacks = new Map<number, { resolve: (arg: any) => void, reject: (arg: E export async function connect(options: { onEvent: (method: string, params?: any) => void, onClose: () => void }): Promise<(method: string, params?: any) => Promise<any>> { const guid = new URLSearchParams(window.location.search).get('ws'); - const ws = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.hostname}:${window.location.port}/${guid}`); + const wsURL = new URL(`../${guid}`, window.location.toString()); + wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:'); + const ws = new WebSocket(wsURL); await new Promise(f => ws.addEventListener('open', f)); ws.addEventListener('close', options.onClose); ws.addEventListener('message', event => { diff --git a/playwright/packages/web/src/components/gridView.css b/playwright/packages/web/src/components/gridView.css index 6443ad4a38..65dcd56962 100644 --- a/playwright/packages/web/src/components/gridView.css +++ b/playwright/packages/web/src/components/gridView.css @@ -20,6 +20,10 @@ flex: auto; } +.grid-view .list-view-entry { + padding-left: 0; +} + .grid-view-cell { overflow: hidden; text-overflow: ellipsis; diff --git a/playwright/packages/web/src/components/gridView.tsx b/playwright/packages/web/src/components/gridView.tsx index 0e6e74ed5b..2a5257da2b 100644 --- a/playwright/packages/web/src/components/gridView.tsx +++ b/playwright/packages/web/src/components/gridView.tsx @@ -22,11 +22,16 @@ import { ResizeView } from '@web/shared/resizeView'; export type Sorting<T> = { by: keyof T, negate: boolean }; +export type RenderedGridCell = { + body: React.ReactNode; + title?: string; +}; + export type GridViewProps<T> = Omit<ListViewProps<T>, 'render'> & { columns: (keyof T)[], columnTitle: (column: keyof T) => string, columnWidth: (column: keyof T) => number, - render: (item: T, column: keyof T, index: number) => React.ReactNode, + render: (item: T, column: keyof T, index: number) => RenderedGridCell, sorting?: Sorting<T>, setSorting?: (sorting: Sorting<T> | undefined) => void, }; @@ -43,7 +48,7 @@ export function GridView<T>(model: GridViewProps<T>) { model.setSorting?.({ by: f, negate: model.sorting?.by === f ? !model.sorting.negate : false }); }, [model]); - return <div className='grid-view'> + return <div className={`grid-view ${model.name}-grid-view`}> <ResizeView orientation={'horizontal'} offsets={offsets} @@ -75,10 +80,12 @@ export function GridView<T>(model: GridViewProps<T>) { render={(item, index) => { return <> {model.columns.map((column, i) => { + const { body, title } = model.render(item, column, index); return <div - className='grid-view-cell' + className={`grid-view-cell grid-view-column-${String(column)}`} + title={title} style={{ width: offsets[i] - (offsets[i - 1] || 0) }}> - {model.render(item, column, index)} + {body} </div>; })} </>; @@ -87,6 +94,7 @@ export function GridView<T>(model: GridViewProps<T>) { indent={model.indent} isError={model.isError} isWarning={model.isWarning} + isInfo={model.isInfo} selectedItem={model.selectedItem} onAccepted={model.onAccepted} onSelected={model.onSelected} diff --git a/playwright/packages/web/src/components/listView.css b/playwright/packages/web/src/components/listView.css index e64e47dd80..cc8c769332 100644 --- a/playwright/packages/web/src/components/listView.css +++ b/playwright/packages/web/src/components/listView.css @@ -74,8 +74,14 @@ .list-view-entry.error { color: var(--vscode-list-errorForeground); + background-color: var(--vscode-inputValidation-errorBackground); } .list-view-entry.warning { color: var(--vscode-list-warningForeground); + background-color: var(--vscode-inputValidation-warningBackground); +} + +.list-view-entry.info { + background-color: var(--vscode-inputValidation-infoBackground); } diff --git a/playwright/packages/web/src/components/listView.tsx b/playwright/packages/web/src/components/listView.tsx index 5f9eb7bd0e..a9bd9f8fb3 100644 --- a/playwright/packages/web/src/components/listView.tsx +++ b/playwright/packages/web/src/components/listView.tsx @@ -26,6 +26,7 @@ export type ListViewProps<T> = { indent?: (item: T, index: number) => number | undefined, isError?: (item: T, index: number) => boolean, isWarning?: (item: T, index: number) => boolean, + isInfo?: (item: T, index: number) => boolean, selectedItem?: T, onAccepted?: (item: T, index: number) => void, onSelected?: (item: T, index: number) => void, @@ -48,6 +49,7 @@ export function ListView<T>({ icon, isError, isWarning, + isInfo, indent, selectedItem, onAccepted, @@ -136,12 +138,13 @@ export function ListView<T>({ const highlightedSuffix = !noHighlightOnHover && highlightedItem === item ? ' highlighted' : ''; const errorSuffix = isError?.(item, index) ? ' error' : ''; const warningSuffix = isWarning?.(item, index) ? ' warning' : ''; + const infoSuffix = isInfo?.(item, index) ? ' info' : ''; const indentation = indent?.(item, index) || 0; const rendered = render(item, index); return <div key={id?.(item, index) || index} role='listitem' - className={'list-view-entry' + selectedSuffix + highlightedSuffix + errorSuffix + warningSuffix} + className={'list-view-entry' + selectedSuffix + highlightedSuffix + errorSuffix + warningSuffix + infoSuffix} onClick={() => onSelected?.(item, index)} onMouseEnter={() => setHighlightedItem(item)} onMouseLeave={() => setHighlightedItem(undefined)} diff --git a/playwright/packages/web/src/shared/resizeView.tsx b/playwright/packages/web/src/shared/resizeView.tsx index b915ce95d4..e575866de0 100644 --- a/playwright/packages/web/src/shared/resizeView.tsx +++ b/playwright/packages/web/src/shared/resizeView.tsx @@ -57,7 +57,7 @@ export const ResizeView: React.FC<{ top: 0, right: 0, bottom: 0, - left: 0, + left: -(7 - resizerWidth) / 2, zIndex: 1000, pointerEvents: 'none', }} diff --git a/playwright/tests/.eslintrc.js b/playwright/tests/.eslintrc.js index 719e15f986..2621e5234e 100644 --- a/playwright/tests/.eslintrc.js +++ b/playwright/tests/.eslintrc.js @@ -11,5 +11,6 @@ module.exports = { }, rules: { '@typescript-eslint/no-floating-promises': 'error', + "@typescript-eslint/no-unnecessary-boolean-literal-compare": 2, }, }; diff --git a/playwright/tests/assets/frames/frame.html b/playwright/tests/assets/frames/frame.html index 38360b0911..3d3b943431 100644 --- a/playwright/tests/assets/frames/frame.html +++ b/playwright/tests/assets/frames/frame.html @@ -1,6 +1,9 @@ <!DOCTYPE html> <link rel='stylesheet' href='./style.css'> <script src='./script.js' type='text/javascript'></script> +<script> + fetch('./404'); +</script> <style> body { height: 100px; diff --git a/playwright/tests/assets/headings.html b/playwright/tests/assets/headings.html new file mode 100644 index 0000000000..d1f1ad620a --- /dev/null +++ b/playwright/tests/assets/headings.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Headings + + + +

Title

+

Subtitle

+

Subsubtitle

+

Subtitle

+ + + \ No newline at end of file diff --git a/playwright/tests/assets/input/handle-locator.html b/playwright/tests/assets/input/handle-locator.html new file mode 100644 index 0000000000..865fb53645 --- /dev/null +++ b/playwright/tests/assets/input/handle-locator.html @@ -0,0 +1,82 @@ + + + + Interstitial test + + + +
+
A place on the side to hover
+
+
This interstitial covers the button
+ +
+ + + diff --git a/playwright/tests/components/ct-react-vite/package.json b/playwright/tests/components/ct-react-vite/package.json index 7f661a1532..1e322f4b9f 100644 --- a/playwright/tests/components/ct-react-vite/package.json +++ b/playwright/tests/components/ct-react-vite/package.json @@ -16,8 +16,8 @@ "devDependencies": { "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", - "@vitejs/plugin-react": "^3.1.0", + "@vitejs/plugin-react": "^4.2.1", "typescript": "^4.5.4", - "vite": "^4.2.1" + "vite": "^5.0.11" } } diff --git a/playwright/tests/components/ct-react-vite/src/App.tsx b/playwright/tests/components/ct-react-vite/src/App.tsx index 5f4afe2236..fd004390f3 100644 --- a/playwright/tests/components/ct-react-vite/src/App.tsx +++ b/playwright/tests/components/ct-react-vite/src/App.tsx @@ -3,10 +3,11 @@ import logo from './assets/logo.svg'; import LoginPage from './pages/LoginPage'; import DashboardPage from './pages/DashboardPage'; -export default function App() { +export default function App({ title }: { title?: string }) { return <>
logo + {title &&

{title}

} Login Dashboard
diff --git a/playwright/tests/components/ct-react-vite/src/components/CheckChildrenProp.tsx b/playwright/tests/components/ct-react-vite/src/components/CheckChildrenProp.tsx new file mode 100644 index 0000000000..3e8f405a5b --- /dev/null +++ b/playwright/tests/components/ct-react-vite/src/components/CheckChildrenProp.tsx @@ -0,0 +1,7 @@ +import type { PropsWithChildren } from 'react'; + +type DefaultChildrenProps = PropsWithChildren<{}>; + +export default function CheckChildrenProp(props: DefaultChildrenProps) { + return <>{'children' in props ? props.children : 'No Children'} +} diff --git a/playwright/tests/components/ct-react-vite/tests/children.spec.tsx b/playwright/tests/components/ct-react-vite/tests/children.spec.tsx index 93e1f1af72..03da7d2c55 100644 --- a/playwright/tests/components/ct-react-vite/tests/children.spec.tsx +++ b/playwright/tests/components/ct-react-vite/tests/children.spec.tsx @@ -1,5 +1,6 @@ import { test, expect } from '@playwright/experimental-ct-react'; import Button from '@/components/Button'; +import CheckChildrenProp from '@/components/CheckChildrenProp'; import DefaultChildren from '@/components/DefaultChildren'; import MultipleChildren from '@/components/MultipleChildren'; @@ -58,3 +59,8 @@ test('render number as child', async ({ mount }) => { const component = await mount({1337}); await expect(component).toContainText('1337'); }); + +test('absence of children when children prop is not provided', async ({ mount }) => { + const component = await mount(); + await expect(component).toContainText('No Children'); +}); diff --git a/playwright/tests/components/ct-react-vite/tests/react-router.spec.tsx b/playwright/tests/components/ct-react-vite/tests/react-router.spec.tsx index 4d29a9dc8a..939154e9d4 100644 --- a/playwright/tests/components/ct-react-vite/tests/react-router.spec.tsx +++ b/playwright/tests/components/ct-react-vite/tests/react-router.spec.tsx @@ -12,3 +12,15 @@ test('navigate to a page by clicking a link', async ({ page, mount }) => { await expect(component.getByRole('main')).toHaveText('Dashboard'); await expect(page).toHaveURL('/dashboard'); }); + +test('update should not reset mount hooks', async ({ page, mount }) => { + const component = await mount(, { + hooksConfig: { routing: true }, + }); + await expect(component.getByRole('heading')).toHaveText('before'); + await expect(component.getByRole('main')).toHaveText('Login'); + + await component.update(); + await expect(component.getByRole('heading')).toHaveText('after'); + await expect(component.getByRole('main')).toHaveText('Login'); +}); diff --git a/playwright/tests/components/ct-react-vite/tests/unmount.spec.tsx b/playwright/tests/components/ct-react-vite/tests/unmount.spec.tsx index acc11c4fdb..20374d8b8e 100644 --- a/playwright/tests/components/ct-react-vite/tests/unmount.spec.tsx +++ b/playwright/tests/components/ct-react-vite/tests/unmount.spec.tsx @@ -17,3 +17,9 @@ test('unmount a multi root component', async ({ mount, page }) => { await expect(page.locator('#root')).not.toContainText('root 1'); await expect(page.locator('#root')).not.toContainText('root 2'); }); + +test('unmount twice throws an error', async ({ mount }) => { + const component = await mount(`); + + // we hover the nested div, but it must record the button + const locator = await recorder.hoverOverElement('div'); + expect(locator).toBe(`getByRole('button', { name: 'Submit' })`); + + const [sources] = await Promise.all([ + recorder.waitForOutput('JavaScript', 'Submit'), + recorder.trustedClick(), + ]); + + expect.soft(sources.get('JavaScript')!.text).toContain(` + await page.getByRole('button', { name: 'Submit' }).click();`); + + expect.soft(sources.get('Python')!.text).toContain(` + page.get_by_role("button", name="Submit").click()`); + + expect.soft(sources.get('Python Async')!.text).toContain(` + await page.get_by_role("button", name="Submit").click()`); + + expect.soft(sources.get('Java')!.text).toContain(` + page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Submit")).click()`); + + expect.soft(sources.get('C#')!.text).toContain(` +await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync();`); + }); }); diff --git a/playwright/tests/library/inspector/cli-codegen-3.spec.ts b/playwright/tests/library/inspector/cli-codegen-3.spec.ts index 77d91bb0e2..96a6295f13 100644 --- a/playwright/tests/library/inspector/cli-codegen-3.spec.ts +++ b/playwright/tests/library/inspector/cli-codegen-3.spec.ts @@ -558,4 +558,94 @@ await page.GetByLabel("Coun\\"try").ClickAsync();`); 'click', ]); }); + + test('should assert value', async ({ openRecorder }) => { + const recorder = await openRecorder(); + + await recorder.setContentAndWait(` + + + + + `); + + await recorder.page.click('x-pw-tool-item.value'); + await recorder.hoverOverElement('#first'); + const [sources1] = await Promise.all([ + recorder.waitForOutput('JavaScript', '#first'), + recorder.trustedClick(), + ]); + expect.soft(sources1.get('JavaScript')!.text).toContain(`await expect(page.locator('#first')).toHaveValue('foo')`); + expect.soft(sources1.get('Python')!.text).toContain(`expect(page.locator("#first")).to_have_value("foo")`); + expect.soft(sources1.get('Python Async')!.text).toContain(`await expect(page.locator("#first")).to_have_value("foo")`); + expect.soft(sources1.get('Java')!.text).toContain(`assertThat(page.locator("#first")).hasValue("foo")`); + expect.soft(sources1.get('C#')!.text).toContain(`await Expect(page.Locator("#first")).ToHaveValueAsync("foo")`); + + await recorder.page.click('x-pw-tool-item.value'); + await recorder.hoverOverElement('#third'); + const [sources3] = await Promise.all([ + recorder.waitForOutput('JavaScript', '#third'), + recorder.trustedClick(), + ]); + expect.soft(sources3.get('JavaScript')!.text).toContain(`await expect(page.locator('#third')).toBeEmpty()`); + expect.soft(sources3.get('Python')!.text).toContain(`expect(page.locator("#third")).to_be_empty()`); + expect.soft(sources3.get('Python Async')!.text).toContain(`await expect(page.locator("#third")).to_be_empty()`); + expect.soft(sources3.get('Java')!.text).toContain(`assertThat(page.locator("#third")).isEmpty()`); + expect.soft(sources3.get('C#')!.text).toContain(`await Expect(page.Locator("#third")).ToBeEmptyAsync()`); + + await recorder.page.click('x-pw-tool-item.value'); + await recorder.hoverOverElement('#fourth'); + const [sources4] = await Promise.all([ + recorder.waitForOutput('JavaScript', '#fourth'), + recorder.trustedClick(), + ]); + expect.soft(sources4.get('JavaScript')!.text).toContain(`await expect(page.locator('#fourth')).toBeChecked()`); + expect.soft(sources4.get('Python')!.text).toContain(`expect(page.locator("#fourth")).to_be_checked()`); + expect.soft(sources4.get('Python Async')!.text).toContain(`await expect(page.locator("#fourth")).to_be_checked()`); + expect.soft(sources4.get('Java')!.text).toContain(`assertThat(page.locator("#fourth")).isChecked()`); + expect.soft(sources4.get('C#')!.text).toContain(`await Expect(page.Locator("#fourth")).ToBeCheckedAsync()`); + }); + + test('should assert value on disabled input', async ({ openRecorder, browserName }) => { + test.fixme(browserName === 'firefox', 'pointerup event is not dispatched on a disabled input'); + + const recorder = await openRecorder(); + + await recorder.setContentAndWait(` + + + + + `); + + await recorder.page.click('x-pw-tool-item.value'); + await recorder.hoverOverElement('#second'); + const [sources2] = await Promise.all([ + recorder.waitForOutput('JavaScript', '#second'), + recorder.trustedClick(), + ]); + expect.soft(sources2.get('JavaScript')!.text).toContain(`await expect(page.locator('#second')).toHaveValue('bar')`); + expect.soft(sources2.get('Python')!.text).toContain(`expect(page.locator("#second")).to_have_value("bar")`); + expect.soft(sources2.get('Python Async')!.text).toContain(`await expect(page.locator("#second")).to_have_value("bar")`); + expect.soft(sources2.get('Java')!.text).toContain(`assertThat(page.locator("#second")).hasValue("bar")`); + expect.soft(sources2.get('C#')!.text).toContain(`await Expect(page.Locator("#second")).ToHaveValueAsync("bar")`); + }); + + test('should assert visibility', async ({ openRecorder }) => { + const recorder = await openRecorder(); + + await recorder.setContentAndWait(``); + + await recorder.page.click('x-pw-tool-item.visibility'); + await recorder.hoverOverElement('input'); + const [sources1] = await Promise.all([ + recorder.waitForOutput('JavaScript', 'textbox'), + recorder.trustedClick(), + ]); + expect.soft(sources1.get('JavaScript')!.text).toContain(`await expect(page.getByRole('textbox')).toBeVisible()`); + expect.soft(sources1.get('Python')!.text).toContain(`expect(page.get_by_role("textbox")).to_be_visible()`); + expect.soft(sources1.get('Python Async')!.text).toContain(`await expect(page.get_by_role("textbox")).to_be_visible()`); + expect.soft(sources1.get('Java')!.text).toContain(`assertThat(page.getByRole(AriaRole.TEXTBOX)).isVisible()`); + expect.soft(sources1.get('C#')!.text).toContain(`await Expect(page.GetByRole(AriaRole.Textbox)).ToBeVisibleAsync()`); + }); }); diff --git a/playwright/tests/library/inspector/cli-codegen-java.spec.ts b/playwright/tests/library/inspector/cli-codegen-java.spec.ts index 738ec37cf9..6b22a1a4cb 100644 --- a/playwright/tests/library/inspector/cli-codegen-java.spec.ts +++ b/playwright/tests/library/inspector/cli-codegen-java.spec.ts @@ -103,3 +103,33 @@ test('should work with --save-har', async ({ runCLI }, testInfo) => { const json = JSON.parse(fs.readFileSync(harFileName, 'utf-8')); expect(json.log.creator.name).toBe('Playwright'); }); + +test('should print the correct imports in junit', async ({ runCLI, channel, browserName }) => { + const cli = runCLI(['--target=java-junit', emptyHTML]); + const expectedImportResult = `import com.microsoft.playwright.junit.UsePlaywright; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.*; + +import org.junit.jupiter.api.*; +import static com.microsoft.playwright.assertions.PlaywrightAssertions.*;`; + await cli.waitFor(expectedImportResult); +}); + +test('should print a valid basic program in junit', async ({ runCLI, channel, browserName }) => { + const cli = runCLI(['--target=java-junit', emptyHTML]); + const expectedResult = `import com.microsoft.playwright.junit.UsePlaywright; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.*; + +import org.junit.jupiter.api.*; +import static com.microsoft.playwright.assertions.PlaywrightAssertions.*; + +@UsePlaywright +public class TestExample { + @Test + void test(Page page) { + page.navigate("${emptyHTML}"); + } +}`; + await cli.waitFor(expectedResult); +}); diff --git a/playwright/tests/library/inspector/console-api.spec.ts b/playwright/tests/library/inspector/console-api.spec.ts index 02a2bd58e0..51d2262bf8 100644 --- a/playwright/tests/library/inspector/console-api.spec.ts +++ b/playwright/tests/library/inspector/console-api.spec.ts @@ -64,9 +64,14 @@ it('should support playwright.locator.values', async ({ page }) => { }); it('should support playwright.locator({ has })', async ({ page }) => { - await page.setContent('
Hi
Hello
'); + await page.setContent(` +
Hi
+
Hello
+
dont match
+ `); expect(await page.evaluate(`playwright.locator('div', { has: playwright.locator('span') }).element.innerHTML`)).toContain('Hello'); expect(await page.evaluate(`playwright.locator('div', { has: playwright.locator('text=Hello') }).element.innerHTML`)).toContain('span'); + expect(await page.evaluate(`playwright.locator('div', { has: playwright.locator('span', { hasText: 'Hello' }) }).elements.length`)).toBe(1); }); it('should support playwright.locator({ hasNot })', async ({ page }) => { diff --git a/playwright/tests/library/inspector/inspectorTest.ts b/playwright/tests/library/inspector/inspectorTest.ts index 2730a5df03..7f759f13aa 100644 --- a/playwright/tests/library/inspector/inspectorTest.ts +++ b/playwright/tests/library/inspector/inspectorTest.ts @@ -35,6 +35,7 @@ const codegenLang2Id: Map = new Map([ ['JSON', 'jsonl'], ['JavaScript', 'javascript'], ['Java', 'java'], + ['Java JUnit', 'java-junit'], ['Python', 'python'], ['Python Async', 'python-async'], ['Pytest', 'python-pytest'], @@ -202,7 +203,7 @@ class CLIMock { constructor(childProcess: CommonFixtures['childProcess'], browserName: string, channel: string | undefined, headless: boolean | undefined, args: string[], executablePath: string | undefined, autoExitWhen: string | undefined) { const nodeArgs = [ 'node', - path.join(__dirname, '..', '..', '..', 'packages', 'playwright-core', 'lib', 'cli', 'cli.js'), + path.join(__dirname, '..', '..', '..', 'packages', 'playwright-core', 'cli.js'), 'codegen', ...args, `--browser=${browserName}`, diff --git a/playwright/tests/library/locator-generator.spec.ts b/playwright/tests/library/locator-generator.spec.ts index a5731214de..30ee22ef0e 100644 --- a/playwright/tests/library/locator-generator.spec.ts +++ b/playwright/tests/library/locator-generator.spec.ts @@ -539,6 +539,13 @@ it('parseLocator quotes', async () => { expect.soft(parseLocator('java', `locator('text="bar"')`, '')).toBe(``); expect.soft(parseLocator('csharp', `Locator("text='bar'")`, '')).toBe(`text='bar'`); expect.soft(parseLocator('csharp', `Locator('text="bar"')`, '')).toBe(``); + + const mixedQuotes = ` + locator("[id*=freetext-field]") + .locator('input:below(:text("Assigned Number:"))') + .locator("visible=true") + `; + expect.soft(parseLocator('javascript', mixedQuotes, '')).toBe(`[id*=freetext-field] >> input:below(:text("Assigned Number:")) >> visible=true`); }); it('parseLocator css', async () => { @@ -563,7 +570,7 @@ it('parse locators strictly', () => { // Quotes expect.soft(parseLocator('javascript', `locator("div").filter({ hasText: "Goodbye world" }).locator("span")`)).toBe(selector); - expect.soft(parseLocator('python', `locator('div').filter(has_text='Goodbye world').locator('span')`)).toBe(selector); + expect.soft(parseLocator('python', `locator('div').filter(has_text='Goodbye world').locator('span')`)).not.toBe(selector); // Whitespace expect.soft(parseLocator('csharp', `Locator("div") . Filter (new ( ) { HasText = "Goodbye world" }).Locator( "span" )`)).toBe(selector); diff --git a/playwright/tests/library/pdf.spec.ts b/playwright/tests/library/pdf.spec.ts index 74b89b776e..3ef002e25c 100644 --- a/playwright/tests/library/pdf.spec.ts +++ b/playwright/tests/library/pdf.spec.ts @@ -26,3 +26,18 @@ it('should be able to save file', async ({ contextFactory, headless, browserName await page.pdf({ path: outputFile }); expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0); }); + +it('should be able to generate outline', async ({ contextFactory, server, headless, browserName }, testInfo) => { + it.skip(!headless || browserName !== 'chromium', 'Printing to pdf is currently only supported in headless chromium.'); + // const context = await contextFactory(); + const context = await contextFactory({ + baseURL: server.PREFIX, + }); + const page = await context.newPage(); + await page.goto('/headings.html'); + const outputFileNoOutline = testInfo.outputPath('outputNoOutline.pdf'); + const outputFileOutline = testInfo.outputPath('outputOutline.pdf'); + await page.pdf({ path: outputFileNoOutline }); + await page.pdf({ path: outputFileOutline, tagged: true, outline: true }); + expect(fs.readFileSync(outputFileOutline).byteLength).toBeGreaterThan(fs.readFileSync(outputFileNoOutline).byteLength); +}); diff --git a/playwright/tests/library/proxy.spec.ts b/playwright/tests/library/proxy.spec.ts index b69098422c..0425185cd5 100644 --- a/playwright/tests/library/proxy.spec.ts +++ b/playwright/tests/library/proxy.spec.ts @@ -91,14 +91,14 @@ it.describe('should proxy local network requests', () => { } ]) { it(`${params.description}`, async ({ platform, browserName, browserType, server, proxyServer }) => { - it.skip(browserName === 'webkit' && platform === 'darwin' && ['localhost', '127.0.0.1'].includes(params.target), 'Mac webkit does not proxy localhost.'); + it.skip(browserName === 'webkit' && platform === 'darwin' && ['localhost', '127.0.0.1'].includes(params.target) && additionalBypass, 'Mac webkit does not proxy localhost when bypass rules are set.'); const path = `/target-${additionalBypass}-${params.target}.html`; server.setRoute(path, async (req, res) => { res.end('Served by the proxy'); }); - const url = `http://${params.target}:${server.PORT}${path}`; + const url = `http://${params.target}:55555${path}`; proxyServer.forwardTo(server.PORT); const browser = await browserType.launch({ proxy: { server: `localhost:${proxyServer.PORT}`, bypass: additionalBypass ? '1.non.existent.domain.for.the.test' : undefined } diff --git a/playwright/tests/library/selector-generator.spec.ts b/playwright/tests/library/selector-generator.spec.ts index 6b87b6171c..01809b9611 100644 --- a/playwright/tests/library/selector-generator.spec.ts +++ b/playwright/tests/library/selector-generator.spec.ts @@ -21,6 +21,10 @@ async function generate(pageOrFrame: Page | Frame, target: string): Promise (window as any).playwright.selector(e)); } +async function generateMultiple(pageOrFrame: Page | Frame, target: string): Promise { + return pageOrFrame.$eval(target, e => (window as any).__injectedScript.generateSelector(e, { multiple: true, testIdAttributeName: 'data-testid' }).selectors); +} + it.describe('selector generator', () => { it.skip(({ mode }) => mode !== 'default'); @@ -519,8 +523,8 @@ it.describe('selector generator', () => { const selectors = await page.evaluate(() => { const target = document.querySelector('section > span'); const root = document.querySelector('section'); - const relative = (window as any).__injectedScript.generateSelector(target, { root }); - const absolute = (window as any).__injectedScript.generateSelector(target); + const relative = (window as any).__injectedScript.generateSelectorSimple(target, { root }); + const absolute = (window as any).__injectedScript.generateSelectorSimple(target); return { relative, absolute }; }); expect(selectors).toEqual({ @@ -528,4 +532,44 @@ it.describe('selector generator', () => { absolute: `section >> internal:text="Hello"i`, }); }); + + it('should generate multiple: noText in role', async ({ page }) => { + await page.setContent(` + + `); + expect(await generateMultiple(page, 'button')).toEqual([`internal:role=button[name="Click me"i]`, `internal:role=button`]); + }); + + it('should generate multiple: noText in text', async ({ page }) => { + await page.setContent(` +
Some div
+ `); + expect(await generateMultiple(page, 'div')).toEqual([`internal:text="Some div"i`, `div`]); + }); + + it('should generate multiple: noId', async ({ page }) => { + await page.setContent(` +
+
+ `); + expect(await generateMultiple(page, '#second button')).toEqual([ + `#second >> internal:role=button[name="Click me"i]`, + `#second >> internal:role=button`, + `internal:role=button[name="Click me"i] >> nth=1`, + `internal:role=button >> nth=1`, + ]); + }); + + it('should generate multiple: noId noText', async ({ page }) => { + await page.setContent(` +
Some span
+
Some span
+ `); + expect(await generateMultiple(page, '#second span')).toEqual([ + `#second >> internal:text="Some span"i`, + `#second span`, + `internal:text="Some span"i >> nth=1`, + `span >> nth=1`, + ]); + }); }); diff --git a/playwright/tests/library/signals.spec.ts b/playwright/tests/library/signals.spec.ts index 6ae54cfd3c..232db694f3 100644 --- a/playwright/tests/library/signals.spec.ts +++ b/playwright/tests/library/signals.spec.ts @@ -133,4 +133,12 @@ test.describe('signals', () => { expect(await remoteServer.out('signal')).toBe('SIGKILL'); expect(await remoteServer.childExitCode()).toBe(130); }); + + test('should not prevent default SIGTERM handling after browser close', async ({ startRemoteServer, server, platform }, testInfo) => { + const remoteServer = await startRemoteServer('launchServer', { startStopAndRunHttp: true }); + expect(await remoteServer.out('closed')).toBe('success'); + process.kill(remoteServer.child().pid, 'SIGTERM'); + expect(await remoteServer.childExitCode()).toBe(null); + expect(await remoteServer.childSignal()).toBe('SIGTERM'); + }); }); diff --git a/playwright/tests/library/trace-viewer.spec.ts b/playwright/tests/library/trace-viewer.spec.ts index 2b506c4169..3bd3c11288 100644 --- a/playwright/tests/library/trace-viewer.spec.ts +++ b/playwright/tests/library/trace-viewer.spec.ts @@ -243,7 +243,9 @@ test('should have network requests', async ({ showTraceViewer }) => { await traceViewer.showNetworkTab(); await expect(traceViewer.networkRequests).toContainText([/frame.htmlGET200text\/html/]); await expect(traceViewer.networkRequests).toContainText([/style.cssGET200text\/css/]); + await expect(traceViewer.networkRequests).toContainText([/404GET404text\/plain/]); await expect(traceViewer.networkRequests).toContainText([/script.jsGET200application\/javascript/]); + await expect(traceViewer.networkRequests.filter({ hasText: '404' })).toHaveCSS('background-color', 'rgb(242, 222, 222)'); }); test('should have network request overrides', async ({ page, server, runAndTrace }) => { @@ -684,6 +686,21 @@ test('should highlight target element in shadow dom', async ({ page, server, run await expect(frameExpect.locator('h1')).toHaveCSS('background-color', 'rgba(111, 168, 220, 0.498)'); }); +test('should highlight expect failure', async ({ page, server, runAndTrace }) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright-python/issues/2258' }); + const traceViewer = await runAndTrace(async () => { + try { + await page.goto(server.EMPTY_PAGE); + await expect(page).toHaveTitle('foo', { timeout: 100 }); + } catch (e) { + } + }); + + await expect(traceViewer.actionTitles.getByText('expect.toHaveTitle')).toHaveCSS('color', 'rgb(176, 16, 17)'); + await traceViewer.showErrorsTab(); + await expect(traceViewer.errorMessages.nth(0)).toHaveText('Expect failed'); +}); + test('should show action source', async ({ showTraceViewer }) => { const traceViewer = await showTraceViewer([traceFile]); await traceViewer.selectAction('locator.click'); diff --git a/playwright/tests/library/tracing.spec.ts b/playwright/tests/library/tracing.spec.ts index 61331fc5fe..e0477bd993 100644 --- a/playwright/tests/library/tracing.spec.ts +++ b/playwright/tests/library/tracing.spec.ts @@ -109,9 +109,7 @@ test('should not collect snapshots by default', async ({ context, page, server } expect(events.some(e => e.type === 'resource-snapshot')).toBeFalsy(); }); -test('should not include buffers in the trace', async ({ context, page, server, mode }, testInfo) => { - test.skip(mode !== 'default', 'no buffers with remote connections'); - +test('should not include buffers in the trace', async ({ context, page, server }, testInfo) => { await context.tracing.start({ snapshots: true }); await page.goto(server.PREFIX + '/empty.html'); await page.screenshot(); @@ -120,7 +118,9 @@ test('should not include buffers in the trace', async ({ context, page, server, const screenshotEvent = actionObjects.find(a => a.apiName === 'page.screenshot'); expect(screenshotEvent.beforeSnapshot).toBeTruthy(); expect(screenshotEvent.afterSnapshot).toBeTruthy(); - expect(screenshotEvent.result).toEqual({}); + expect(screenshotEvent.result).toEqual({ + 'binary': '', + }); }); test('should exclude internal pages', async ({ browserName, context, page, server }, testInfo) => { diff --git a/playwright/tests/page/elementhandle-scroll-into-view.spec.ts b/playwright/tests/page/elementhandle-scroll-into-view.spec.ts index 70705b05da..5fe53d0722 100644 --- a/playwright/tests/page/elementhandle-scroll-into-view.spec.ts +++ b/playwright/tests/page/elementhandle-scroll-into-view.spec.ts @@ -120,5 +120,6 @@ it('should timeout waiting for visible', async ({ page, server }) => { await page.setContent('
Hello
'); const div = await page.$('div'); const error = await div.scrollIntoViewIfNeeded({ timeout: 3000 }).catch(e => e); - expect(error.message).toContain('element is not displayed, retrying in 100ms'); + expect(error.message).toContain('element is not visible'); + expect(error.message).toContain('retrying scroll into view action'); }); diff --git a/playwright/tests/page/page-accessibility.spec.ts b/playwright/tests/page/page-accessibility.spec.ts index 3c71479e17..e6717c5e20 100644 --- a/playwright/tests/page/page-accessibility.spec.ts +++ b/playwright/tests/page/page-accessibility.spec.ts @@ -143,9 +143,8 @@ it('should not report text nodes inside controls', async function({ page, browse expect(await page.accessibility.snapshot()).toEqual(golden); }); -it('rich text editable fields should have children', async function({ page, browserName, browserVersion, channel, isWebView2 }) { +it('rich text editable fields should have children', async function({ page, browserName, browserVersion, isWebView2 }) { it.skip(browserName === 'webkit', 'WebKit rich text accessibility is iffy'); - it.skip(channel && channel.startsWith('msedge'), 'Edge is missing a Chromium fix'); it.skip(isWebView2, 'WebView2 is missing a Chromium fix'); await page.setContent(` @@ -178,9 +177,8 @@ it('rich text editable fields should have children', async function({ page, brow expect(snapshot.children[0]).toEqual(golden); }); -it('rich text editable fields with role should have children', async function({ page, browserName, browserMajorVersion, browserVersion, channel, isWebView2 }) { +it('rich text editable fields with role should have children', async function({ page, browserName, browserMajorVersion, browserVersion, isWebView2 }) { it.skip(browserName === 'webkit', 'WebKit rich text accessibility is iffy'); - it.skip(channel && channel.startsWith('msedge'), 'Edge is missing a Chromium fix'); it.skip(isWebView2, 'WebView2 is missing a Chromium fix'); await page.setContent(` @@ -350,3 +348,20 @@ it('should work when there is a title ', async ({ page }) => { expect(snapshot.name).toBe('This is the title'); expect(snapshot.children[0].name).toBe('This is the content'); }); + +it('should work with aria-invalid accessibility tree', async ({ page, browserName, server }) => { + await page.goto(server.EMPTY_PAGE); + await page.setContent(`WHO WE ARE`); + expect(await page.accessibility.snapshot()).toEqual({ + 'role': browserName === 'firefox' ? 'document' : 'WebArea', + 'name': '', + 'children': [ + { + 'role': 'link', + 'name': 'WHO WE ARE', + 'invalid': 'true', + 'value': browserName === 'firefox' ? `${server.PREFIX}/hi` : undefined + } + ] + }); +}); diff --git a/playwright/tests/page/page-add-locator-handler.spec.ts b/playwright/tests/page/page-add-locator-handler.spec.ts new file mode 100644 index 0000000000..7f75a86ffc --- /dev/null +++ b/playwright/tests/page/page-add-locator-handler.spec.ts @@ -0,0 +1,206 @@ +/** + * 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 { test, expect } from './pageTest'; +import { kTargetClosedErrorMessage } from '../config/errors'; + +test('should work', async ({ page, server }) => { + await page.goto(server.PREFIX + '/input/handle-locator.html'); + + let beforeCount = 0; + let afterCount = 0; + await page.addLocatorHandler(page.getByText('This interstitial covers the button'), async () => { + ++beforeCount; + await page.locator('#close').click(); + ++afterCount; + }); + + for (const args of [ + ['mouseover', 1], + ['mouseover', 1, 'capture'], + ['mouseover', 2], + ['mouseover', 2, 'capture'], + ['pointerover', 1], + ['pointerover', 1, 'capture'], + ['none', 1], + ['remove', 1], + ['hide', 1], + ]) { + await test.step(`${args[0]}${args[2] === 'capture' ? ' with capture' : ''} ${args[1]} times`, async () => { + await page.locator('#aside').hover(); + beforeCount = 0; + afterCount = 0; + await page.evaluate(args => { + (window as any).clicked = 0; + (window as any).setupAnnoyingInterstitial(...args); + }, args); + expect(beforeCount).toBe(0); + expect(afterCount).toBe(0); + await page.locator('#target').click(); + expect(beforeCount).toBe(args[1]); + expect(afterCount).toBe(args[1]); + expect(await page.evaluate('window.clicked')).toBe(1); + await expect(page.locator('#interstitial')).not.toBeVisible(); + }); + } +}); + +test('should work with a custom check', async ({ page, server }) => { + await page.goto(server.PREFIX + '/input/handle-locator.html'); + + await page.addLocatorHandler(page.locator('body'), async () => { + if (await page.getByText('This interstitial covers the button').isVisible()) + await page.locator('#close').click(); + }); + + for (const args of [ + ['mouseover', 2], + ['none', 1], + ['remove', 1], + ['hide', 1], + ]) { + await test.step(`${args[0]}${args[2] === 'capture' ? ' with capture' : ''} ${args[1]} times`, async () => { + await page.locator('#aside').hover(); + await page.evaluate(args => { + (window as any).clicked = 0; + (window as any).setupAnnoyingInterstitial(...args); + }, args); + await page.locator('#target').click(); + expect(await page.evaluate('window.clicked')).toBe(1); + await expect(page.locator('#interstitial')).not.toBeVisible(); + }); + } +}); + +test('should work with locator.hover()', async ({ page, server }) => { + await page.goto(server.PREFIX + '/input/handle-locator.html'); + + await page.addLocatorHandler(page.getByText('This interstitial covers the button'), async () => { + await page.locator('#close').click(); + }); + + await page.locator('#aside').hover(); + await page.evaluate(() => { + (window as any).setupAnnoyingInterstitial('pointerover', 1, 'capture'); + }); + await page.locator('#target').hover(); + await expect(page.locator('#interstitial')).not.toBeVisible(); + expect(await page.$eval('#target', e => window.getComputedStyle(e).backgroundColor)).toBe('rgb(255, 255, 0)'); +}); + +test('should not work with force:true', async ({ page, server }) => { + await page.goto(server.PREFIX + '/input/handle-locator.html'); + + await page.addLocatorHandler(page.getByText('This interstitial covers the button'), async () => { + await page.locator('#close').click(); + }); + + await page.locator('#aside').hover(); + await page.evaluate(() => { + (window as any).setupAnnoyingInterstitial('none', 1); + }); + await page.locator('#target').click({ force: true, timeout: 2000 }); + expect(await page.locator('#interstitial').isVisible()).toBe(true); + expect(await page.evaluate('window.clicked')).toBe(undefined); +}); + +test('should throw when page closes', async ({ page, server, isAndroid }) => { + test.fixme(isAndroid, 'GPU process crash: https://issues.chromium.org/issues/324909825'); + await page.goto(server.PREFIX + '/input/handle-locator.html'); + + await page.addLocatorHandler(page.getByText('This interstitial covers the button'), async () => { + await page.close(); + }); + + await page.locator('#aside').hover(); + await page.evaluate(() => { + (window as any).clicked = 0; + (window as any).setupAnnoyingInterstitial('mouseover', 1); + }); + const error = await page.locator('#target').click().catch(e => e); + expect(error.message).toContain(kTargetClosedErrorMessage); +}); + +test('should throw when handler times out', async ({ page, server }) => { + await page.goto(server.PREFIX + '/input/handle-locator.html'); + + let called = 0; + await page.addLocatorHandler(page.getByText('This interstitial covers the button'), async () => { + ++called; + // Deliberately timeout. + await new Promise(() => {}); + }); + + await page.locator('#aside').hover(); + await page.evaluate(() => { + (window as any).clicked = 0; + (window as any).setupAnnoyingInterstitial('mouseover', 1); + }); + const error = await page.locator('#target').click({ timeout: 3000 }).catch(e => e); + expect(error.message).toContain('Timeout 3000ms exceeded'); + + const error2 = await page.locator('#target').click({ timeout: 3000 }).catch(e => e); + expect(error2.message).toContain('Timeout 3000ms exceeded'); + + // Should not enter the same handler while it is still running. + expect(called).toBe(1); +}); + +test('should work with toBeVisible', async ({ page, server }) => { + await page.goto(server.PREFIX + '/input/handle-locator.html'); + + let called = 0; + await page.addLocatorHandler(page.getByText('This interstitial covers the button'), async () => { + ++called; + await page.locator('#close').click(); + }); + + await page.evaluate(() => { + (window as any).clicked = 0; + (window as any).setupAnnoyingInterstitial('remove', 1); + }); + await expect(page.locator('#target')).toBeVisible(); + await expect(page.locator('#interstitial')).not.toBeVisible(); + expect(called).toBe(1); +}); + +test('should work with toHaveScreenshot', async ({ page, server, isAndroid }) => { + test.fixme(isAndroid, 'Screenshots are cut off on Android'); + await page.setViewportSize({ width: 500, height: 500 }); + await page.goto(server.PREFIX + '/grid.html'); + + await page.evaluate(() => { + const overlay = document.createElement('div'); + document.body.append(overlay); + overlay.style.position = 'absolute'; + overlay.style.left = '0'; + overlay.style.right = '0'; + overlay.style.top = '0'; + overlay.style.bottom = '0'; + overlay.style.backgroundColor = 'red'; + + const closeButton = document.createElement('button'); + overlay.appendChild(closeButton); + closeButton.textContent = 'close'; + closeButton.addEventListener('click', () => overlay.remove()); + }); + + await page.addLocatorHandler(page.getByRole('button', { name: 'close' }), async () => { + await page.getByRole('button', { name: 'close' }).click(); + }); + + await expect(page).toHaveScreenshot('screenshot-grid.png'); +}); diff --git a/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-chromium.png b/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-chromium.png new file mode 100644 index 0000000000..122a4f0ae0 Binary files /dev/null and b/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-chromium.png differ diff --git a/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-firefox.png b/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-firefox.png new file mode 100644 index 0000000000..7b16493355 Binary files /dev/null and b/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-firefox.png differ diff --git a/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-webkit.png b/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-webkit.png new file mode 100644 index 0000000000..af070c52a8 Binary files /dev/null and b/playwright/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-webkit.png differ diff --git a/playwright/tests/page/page-click-scroll.spec.ts b/playwright/tests/page/page-click-scroll.spec.ts index 464801afdd..758a8d0eb9 100644 --- a/playwright/tests/page/page-click-scroll.spec.ts +++ b/playwright/tests/page/page-click-scroll.spec.ts @@ -78,9 +78,9 @@ it('should scroll into view display:contents with position', async ({ page, brow expect(await page.evaluate('window._clicked')).toBe(true); }); -it('should not crash when force-clicking hidden input', async ({ page, browserName, channel, browserMajorVersion }) => { +it('should not crash when force-clicking hidden input', async ({ page, isWebView2 }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/18183' }); - it.skip(browserName === 'chromium' && browserMajorVersion < 109 || channel && channel.startsWith('msedge') && browserMajorVersion < 110); + it.fixme(isWebView2); await page.setContent(``); const error = await page.locator('input').click({ force: true, timeout: 2000 }).catch(e => e); diff --git a/playwright/tests/page/page-click-timeout-1.spec.ts b/playwright/tests/page/page-click-timeout-1.spec.ts index 6856cead27..ad6b1fec77 100644 --- a/playwright/tests/page/page-click-timeout-1.spec.ts +++ b/playwright/tests/page/page-click-timeout-1.spec.ts @@ -32,5 +32,6 @@ it('should timeout waiting for button to be enabled', async ({ page }) => { const error = await page.click('text=Click target', { timeout: 3000 }).catch(e => e); expect(await page.evaluate('window.__CLICKED')).toBe(undefined); expect(error.message).toContain('page.click: Timeout 3000ms exceeded.'); - expect(error.message).toContain('element is not enabled - waiting'); + expect(error.message).toContain('element is not enabled'); + expect(error.message).toContain('retrying click action'); }); diff --git a/playwright/tests/page/page-click-timeout-2.spec.ts b/playwright/tests/page/page-click-timeout-2.spec.ts index 7a2f30eaa9..b3e4f12974 100644 --- a/playwright/tests/page/page-click-timeout-2.spec.ts +++ b/playwright/tests/page/page-click-timeout-2.spec.ts @@ -23,7 +23,8 @@ it('should timeout waiting for display:none to be gone', async ({ page, server } const error = await page.click('button', { timeout: 5000 }).catch(e => e); expect(error.message).toContain('page.click: Timeout 5000ms exceeded.'); expect(error.message).toContain('waiting for element to be visible, enabled and stable'); - expect(error.message).toContain('element is not visible - waiting'); + expect(error.message).toContain('element is not visible'); + expect(error.message).toContain('retrying click action'); }); it('should timeout waiting for visibility:hidden to be gone', async ({ page, server }) => { @@ -32,5 +33,6 @@ it('should timeout waiting for visibility:hidden to be gone', async ({ page, ser const error = await page.click('button', { timeout: 5000 }).catch(e => e); expect(error.message).toContain('page.click: Timeout 5000ms exceeded.'); expect(error.message).toContain('waiting for element to be visible, enabled and stable'); - expect(error.message).toContain('element is not visible - waiting'); + expect(error.message).toContain('element is not visible'); + expect(error.message).toContain('retrying click action'); }); diff --git a/playwright/tests/page/page-click-timeout-4.spec.ts b/playwright/tests/page/page-click-timeout-4.spec.ts index dcd490ab45..0b167a6344 100644 --- a/playwright/tests/page/page-click-timeout-4.spec.ts +++ b/playwright/tests/page/page-click-timeout-4.spec.ts @@ -27,7 +27,8 @@ it('should timeout waiting for stable position', async ({ page, server }) => { const error = await button.click({ timeout: 3000 }).catch(e => e); expect(error.message).toContain('elementHandle.click: Timeout 3000ms exceeded.'); expect(error.message).toContain('waiting for element to be visible, enabled and stable'); - expect(error.message).toContain('element is not stable - waiting'); + expect(error.message).toContain('element is not stable'); + expect(error.message).toContain('retrying click action'); }); it('should click for the second time after first timeout', async ({ page, server, mode }) => { diff --git a/playwright/tests/page/page-click.spec.ts b/playwright/tests/page/page-click.spec.ts index 2c8a32b40d..9059303ba5 100644 --- a/playwright/tests/page/page-click.spec.ts +++ b/playwright/tests/page/page-click.spec.ts @@ -860,9 +860,12 @@ it('should not hang when frame is detached', async ({ page, server, mode }) => { let resolveDetachPromise; const detachPromise = new Promise(resolve => resolveDetachPromise = resolve); + let firstTime = true; const __testHookBeforeStable = () => { // Detach the frame after "waiting for stable" has started. - + if (!firstTime) + return; + firstTime = false; setTimeout(async () => { await detachFrame(page, 'frame1'); resolveDetachPromise(); diff --git a/playwright/tests/page/page-event-console.spec.ts b/playwright/tests/page/page-event-console.spec.ts index df611dd9dd..3b5f652fc9 100644 --- a/playwright/tests/page/page-event-console.spec.ts +++ b/playwright/tests/page/page-event-console.spec.ts @@ -38,7 +38,10 @@ it('should work @smoke', async ({ page, browserName }) => { it('should emit same log twice', async ({ page }) => { const messages = []; page.on('console', m => messages.push(m.text())); - await page.evaluate(() => { for (let i = 0; i < 2; ++i) console.log('hello'); }); + await page.evaluate(() => { + for (let i = 0; i < 2; ++i) + console.log('hello'); + }); expect(messages).toEqual(['hello', 'hello']); }); diff --git a/playwright/tests/page/page-network-request.spec.ts b/playwright/tests/page/page-network-request.spec.ts index e915ffa3d3..70fc77b61f 100644 --- a/playwright/tests/page/page-network-request.spec.ts +++ b/playwright/tests/page/page-network-request.spec.ts @@ -451,7 +451,7 @@ it('should not allow to access frame on popup main request', async ({ page, serv it('page.reload return 304 status code', async ({ page, server, browserName }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28779' }); - it.fixme(browserName === 'chromium', 'Returns 200 instead of 304'); + it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/29441' }); it.fixme(browserName === 'firefox', 'Does not send second request'); let requestNumber = 0; server.setRoute('/test.html', (req, res) => { @@ -473,6 +473,12 @@ it('page.reload return 304 status code', async ({ page, server, browserName }) = expect(response1.status()).toBe(200); const response2 = await page.reload(); expect(requestNumber).toBe(2); - expect(response2.status()).toBe(304); - expect(response2.statusText()).toBe('Not Modified'); + if (browserName === 'chromium') { + expect(response2.status()).toBe(200); + expect(response2.statusText()).toBe('OK'); + expect(await response2.text()).toBe('
Test
'); + } else { + expect(response2.status()).toBe(304); + expect(response2.statusText()).toBe('Not Modified'); + } }); diff --git a/playwright/tests/page/page-screenshot.spec.ts b/playwright/tests/page/page-screenshot.spec.ts index 47188f52a4..278dd38228 100644 --- a/playwright/tests/page/page-screenshot.spec.ts +++ b/playwright/tests/page/page-screenshot.spec.ts @@ -723,7 +723,7 @@ it.describe('page screenshot animations', () => { el.addEventListener('transitionend', () => { const time = Date.now(); // Block main thread for 200ms, emulating heavy layout. - while (Date.now() - time < 200) ; + while (Date.now() - time < 200) {} const h1 = document.createElement('h1'); h1.textContent = 'woof-woof'; document.body.append(h1); diff --git a/playwright/tests/playwright-test/basic.spec.ts b/playwright/tests/playwright-test/basic.spec.ts index 3b47603c25..4c4b28ceeb 100644 --- a/playwright/tests/playwright-test/basic.spec.ts +++ b/playwright/tests/playwright-test/basic.spec.ts @@ -550,3 +550,22 @@ test('should support describe.fixme', async ({ runInlineTest }) => { expect(result.skipped).toBe(3); expect(result.output).toContain('heytest4'); }); + +test('should not allow mixing test types', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'mixed.spec.ts': ` + import { test } from '@playwright/test'; + + export const test2 = test.extend({ + value: 42, + }); + + test.describe("test1 suite", () => { + test2("test 2", async () => {}); + }); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.output).toContain(`Can't call test() inside a describe() suite of a different test type.`); + expect(result.output).toContain('> 9 | test2('); +}); diff --git a/playwright/tests/playwright-test/config.spec.ts b/playwright/tests/playwright-test/config.spec.ts index 0d786de0ce..38fef69915 100644 --- a/playwright/tests/playwright-test/config.spec.ts +++ b/playwright/tests/playwright-test/config.spec.ts @@ -223,7 +223,7 @@ test('should throw when test() is called in config file', async ({ runInlineTest }); test('should filter by project, case-insensitive', async ({ runInlineTest }) => { - const { passed, failed, output, skipped } = await runInlineTest({ + const { passed, failed, outputLines, skipped } = await runInlineTest({ 'playwright.config.ts': ` module.exports = { projects: [ { name: 'suite1' }, @@ -231,20 +231,44 @@ test('should filter by project, case-insensitive', async ({ runInlineTest }) => ] }; `, 'a.test.ts': ` - import { test, expect } from '@playwright/test'; + import { test } from '@playwright/test'; test('pass', async ({}, testInfo) => { - console.log(testInfo.project.name); + console.log('%%' + test.info().project.name); }); ` }, { project: 'SUite2' }); expect(passed).toBe(1); expect(failed).toBe(0); expect(skipped).toBe(0); - expect(output).toContain('suite2'); - expect(output).not.toContain('suite1'); + expect(new Set(outputLines)).toEqual(new Set([ + 'suite2', + ])); }); -test('should print nice error when project is unknown', async ({ runInlineTest }) => { +test('should filter by project wildcard', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.js': ` + module.exports = { + projects: [ + { name: 'project-name' }, + { name: 'foobar' } + ] + }; + `, + 'a.test.js': ` + const { test } = require('@playwright/test'); + test('one', async ({}) => { + console.log('%%' + test.info().project.name); + }); ` + }, { '--project': '*oj*t-Na*e' }); + expect(result.exitCode).toBe(0); + expect(result.output).toContain('Running 1 test using 1 worker'); + expect(new Set(result.outputLines)).toEqual(new Set([ + 'project-name', + ])); +}); + +test('should print nice error when the project wildcard does not match anything', async ({ runInlineTest }) => { const { output, exitCode } = await runInlineTest({ 'playwright.config.ts': ` module.exports = { projects: [ @@ -258,13 +282,53 @@ test('should print nice error when project is unknown', async ({ runInlineTest } console.log(testInfo.project.name); }); ` + }, { '--project': ['not*found'] }); + expect(exitCode).toBe(1); + expect(output).toContain('Error: No projects matched. Available projects: "suite1", "suite2"'); +}); + +test('should filter by project wildcard and exact name', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.js': ` + module.exports = { + projects: [ + { name: 'first' }, + { name: 'fooBar' }, + { name: 'foobarBaz' }, + { name: 'prefix' }, + { name: 'prefixEnd' }, + ] + }; + `, + 'a.test.js': ` + const { test } = require('@playwright/test'); + test('one', async ({}) => { + console.log('%%' + test.info().project.name); + }); ` + }, { '--project': ['first', '*bar', 'pref*x'] }); + expect(result.exitCode).toBe(0); + expect(new Set(result.outputLines)).toEqual(new Set(['first', 'fooBar', 'prefix'])); +}); + +test('should print nice error when project is unknown', async ({ runInlineTest }) => { + const { output, exitCode } = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { projects: [ + { name: 'suite1' }, + { name: 'suite2' }, + ] }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('pass', async ({}, testInfo) => {}); + ` }, { project: 'suite3' }); expect(exitCode).toBe(1); - expect(output).toContain('Project(s) "suite3" not found. Available named projects: "suite1", "suite2"'); + expect(output).toContain('Project(s) "suite3" not found. Available projects: "suite1", "suite2"'); }); test('should filter by project list, case-insensitive', async ({ runInlineTest }) => { - const { passed, failed, output, skipped } = await runInlineTest({ + const { passed, failed, outputLines, skipped } = await runInlineTest({ 'playwright.config.ts': ` module.exports = { projects: [ { name: 'suite1' }, @@ -276,21 +340,18 @@ test('should filter by project list, case-insensitive', async ({ runInlineTest } 'a.test.ts': ` import { test, expect } from '@playwright/test'; test('pass', async ({}, testInfo) => { - console.log(testInfo.project.name); + console.log('%%' + test.info().project.name); }); ` }, { project: ['SUite2', 'Suite3'] }); expect(passed).toBe(2); expect(failed).toBe(0); expect(skipped).toBe(0); - expect(output).toContain('suite2'); - expect(output).toContain('suite3'); - expect(output).not.toContain('suite1'); - expect(output).not.toContain('suite4'); + expect(new Set(outputLines)).toEqual(new Set(['suite3', 'suite2'])); }); test('should filter when duplicate project names exist', async ({ runInlineTest }) => { - const { passed, failed, output, skipped } = await runInlineTest({ + const { passed, failed, outputLines, skipped } = await runInlineTest({ 'playwright.config.ts': ` module.exports = { projects: [ { name: 'suite1' }, @@ -302,16 +363,14 @@ test('should filter when duplicate project names exist', async ({ runInlineTest 'a.test.ts': ` import { test, expect } from '@playwright/test'; test('pass', async ({}, testInfo) => { - console.log(testInfo.project.name); + console.log('%%' + test.info().project.name); }); ` }, { project: ['suite1', 'sUIte4'] }); expect(passed).toBe(3); expect(failed).toBe(0); expect(skipped).toBe(0); - expect(output).toContain('suite1'); - expect(output).toContain('suite4'); - expect(output).not.toContain('suite2'); + expect(new Set(outputLines)).toEqual(new Set(['suite1', 'suite1', 'suite4'])); }); test('should print nice error when some of the projects are unknown', async ({ runInlineTest }) => { @@ -330,7 +389,7 @@ test('should print nice error when some of the projects are unknown', async ({ r ` }, { project: ['suitE1', 'suIte3', 'SUite4'] }); expect(exitCode).toBe(1); - expect(output).toContain('Project(s) "suIte3", "SUite4" not found. Available named projects: "suite1", "suite2"'); + expect(output).toContain('Project(s) "suIte3", "SUite4" not found. Available projects: "suite1", "suite2"'); }); test('should work without config file', async ({ runInlineTest }) => { @@ -580,8 +639,12 @@ test('should merge ct configs', async ({ runInlineTest }) => { expect(derivedConfig).toEqual(expect.objectContaining({ use: { foo: 1, bar: 2 }, grep: 'hi', - build: { babelPlugins: [expect.anything()] }, - _plugins: [expect.anything()], + '@playwright/test': expect.objectContaining({ + babelPlugins: [[expect.stringContaining('tsxTransform.js')]] + }), + '@playwright/experimental-ct-core': expect.objectContaining({ + registerSourceFile: expect.stringContaining('registerSource'), + }), })); `, 'a.test.ts': ` diff --git a/playwright/tests/playwright-test/esm.spec.ts b/playwright/tests/playwright-test/esm.spec.ts index a672aff78a..7097659d0d 100644 --- a/playwright/tests/playwright-test/esm.spec.ts +++ b/playwright/tests/playwright-test/esm.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { test, expect } from './playwright-test-fixtures'; +import { test, expect, playwrightCtConfigText } from './playwright-test-fixtures'; test('should load nested as esm when package.json has type module', async ({ runInlineTest }) => { const result = await runInlineTest({ @@ -157,6 +157,32 @@ test('should use source maps', async ({ runInlineTest }) => { expect(output).toContain('[foo] › a.test.ts:4:7 › check project name'); }); +test('should use source maps when importing a file throws an error', async ({ runInlineTest }) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/29418' }); + + const result = await runInlineTest({ + 'package.json': `{ "type": "module" }`, + 'playwright.config.ts': ` + export default {}; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + + throw new Error('Oh my!'); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.output).toContain(`Error: Oh my! + + at a.test.ts:4 + + 2 | import { test, expect } from '@playwright/test'; + 3 | +> 4 | throw new Error('Oh my!'); + | ^ + `); +}); + test('should show the codeframe in errors', async ({ runInlineTest }) => { const result = await runInlineTest({ 'package.json': `{ "type": "module" }`, @@ -481,10 +507,7 @@ test('should resolve no-extension import to .jsx file in ESM mode', async ({ run test('should resolve .js import to .tsx file in ESM mode for components', async ({ runInlineTest }) => { const result = await runInlineTest({ 'package.json': `{ "type": "module" }`, - 'playwright.config.ts': ` - import { defineConfig } from '@playwright/experimental-ct-react'; - export default defineConfig({ projects: [{name: 'foo'}] }); - `, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, diff --git a/playwright/tests/playwright-test/expect-soft.spec.ts b/playwright/tests/playwright-test/expect-soft.spec.ts index ffb0252dab..c47d594e0e 100644 --- a/playwright/tests/playwright-test/expect-soft.spec.ts +++ b/playwright/tests/playwright-test/expect-soft.spec.ts @@ -22,8 +22,8 @@ test('soft expects should compile', async ({ runTSC }) => { import { test, expect } from '@playwright/test'; test('should work', () => { test.expect.soft(1+1).toBe(3); - test.expect.soft(1+1, 'custom error message').toBe(3); - test.expect.soft(1+1, { message: 'custom error message' }).toBe(3); + test.expect.soft(1+1, 'custom expect message').toBe(3); + test.expect.soft(1+1, { message: 'custom expect message' }).toBe(3); }); ` }); diff --git a/playwright/tests/playwright-test/expect-to-pass.spec.ts b/playwright/tests/playwright-test/expect-to-pass.spec.ts index 2c73283930..2ad00e0ce7 100644 --- a/playwright/tests/playwright-test/expect-to-pass.spec.ts +++ b/playwright/tests/playwright-test/expect-to-pass.spec.ts @@ -220,3 +220,43 @@ test('should show intermediate result for toPass that spills over test time', as expect(result.output).toContain('Expected: 2'); expect(result.output).toContain('Received: 3'); }); + +test('should respect timeout in config file when timeout parameter is not passed', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.js': `module.exports = { expect: { toPass: { timeout: 100 } } }`, + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('should fail', async () => { + await test.expect(() => { + expect(1).toBe(2); + }).toPass(); + }); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.output).toContain('Timeout 100ms exceeded while waiting on the predicate'); + expect(result.output).toContain('Received: 1'); + expect(result.output).toContain(` + 4 | await test.expect(() => { + `.trim()); +}); + +test('should give priority to timeout parameter over timeout in config file', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.js': `module.exports = { expect: { toPass: { timeout: 100 } } }`, + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('should fail', async () => { + await test.expect(() => { + expect(1).toBe(2); + }).toPass({ timeout: 200 }); + }); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.output).toContain('Timeout 200ms exceeded while waiting on the predicate'); + expect(result.output).toContain('Received: 1'); + expect(result.output).toContain(` + 4 | await test.expect(() => { + `.trim()); +}); diff --git a/playwright/tests/playwright-test/expect.spec.ts b/playwright/tests/playwright-test/expect.spec.ts index d5d4b338fa..9a564532ef 100644 --- a/playwright/tests/playwright-test/expect.spec.ts +++ b/playwright/tests/playwright-test/expect.spec.ts @@ -70,26 +70,26 @@ test('should not expand huge arrays', async ({ runInlineTest }) => { expect(result.output.length).toBeLessThan(100000); }); -test('should include custom error message', async ({ runInlineTest }) => { +test('should include custom expect message', async ({ runInlineTest }) => { const result = await runInlineTest({ 'expect-test.spec.ts': ` import { test, expect } from '@playwright/test'; test('custom expect message', () => { - test.expect(1+1, 'one plus one is two!').toEqual(3); + test.expect(1+1, 'one plus one should be two!').toEqual(3); }); ` }); expect(result.exitCode).toBe(1); expect(result.passed).toBe(0); expect(result.output).toContain([ - ` Error: one plus one is two!\n`, + ` Error: one plus one should be two!\n`, ` expect(received).toEqual(expected) // deep equality\n`, ` Expected: 3`, ` Received: 2`, ].join('\n')); }); -test('should include custom error message with web-first assertions', async ({ runInlineTest }) => { +test('should include custom expect message with web-first assertions', async ({ runInlineTest }) => { const result = await runInlineTest({ 'expect-test.spec.ts': ` import { test, expect } from '@playwright/test'; @@ -989,7 +989,7 @@ test('should respect timeout from configured expect when used outside of the tes finally { await browser?.close(); } - + ` }; const baseDir = await writeFiles(files); diff --git a/playwright/tests/playwright-test/find-related-tests.spec.ts b/playwright/tests/playwright-test/find-related-tests.spec.ts new file mode 100644 index 0000000000..981395ade1 --- /dev/null +++ b/playwright/tests/playwright-test/find-related-tests.spec.ts @@ -0,0 +1,88 @@ +/** + * Copyright Microsoft Corporation. All rights reserved. + * + * 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 { test, expect } from './playwright-test-fixtures'; +import path from 'path'; + +export const ctReactCliEntrypoint = path.join(__dirname, '../../packages/playwright-ct-react/cli.js'); + +test('should list related tests', async ({ runCLICommand }) => { + const result = await runCLICommand({ + 'playwright.config.ts': ` + import { defineConfig } from '@playwright/test'; + export default defineConfig({}); + `, + 'helper.ts': ` + export const value = 42; + `, + 'helper2.ts': ` + export { value } from './helper'; + `, + 'a.spec.ts': ` + import { test } from '@playwright/test'; + import { value } from './helper2'; + if (value) {} + test('', () => {}); + `, + 'b.spec.ts': ` + import { test } from '@playwright/test'; + import { value } from './helper'; + if (value) {} + test('', () => {}); + `, + }, 'find-related-test-files', ['helper.ts']); + expect(result.exitCode).toBe(0); + expect(result.stderr).toBeFalsy(); + const data = JSON.parse(result.stdout); + expect(data).toEqual({ + testFiles: [ + expect.stringContaining('a.spec.ts'), + expect.stringContaining('b.spec.ts'), + ] + }); +}); + +test('should list related tests for ct', async ({ runCLICommand }) => { + const result = await runCLICommand({ + 'playwright.config.ts': ` + import { defineConfig } from '@playwright/experimental-ct-react'; + export default defineConfig({}); + `, + 'playwright/index.html': ``, + 'playwright/index.js': ``, + 'helper.tsx': ` + export const HelperButton = () => ; + `, + 'button.tsx': ` + import { HelperButton } from './helper'; + export const Button = () => Click me; + `, + 'button.spec.tsx': ` + import { test } from '@playwright/experimental-ct-react'; + import { Button } from './button'; + test('foo', async ({ mount }) => { + await mount(; `, @@ -270,7 +265,7 @@ test('should grow cache', async ({ runInlineTest }, testInfo) => { await test.step('original test', async () => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/button1.tsx': ` @@ -305,7 +300,7 @@ test('should grow cache', async ({ runInlineTest }, testInfo) => { await test.step('run second test', async () => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, }, { workers: 1 }, undefined, { additionalArgs: ['button2'] }); expect(result.exitCode).toBe(0); expect(result.passed).toBe(1); @@ -315,7 +310,7 @@ test('should grow cache', async ({ runInlineTest }, testInfo) => { await test.step('run first test again', async () => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, }, { workers: 1 }, undefined, { additionalArgs: ['button2'] }); expect(result.exitCode).toBe(0); expect(result.passed).toBe(1); @@ -326,7 +321,7 @@ test('should grow cache', async ({ runInlineTest }, testInfo) => { test('should not use global config for preview', async ({ runInlineTest }) => { const result1 = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.js': ``, 'vite.config.js': ` @@ -347,7 +342,7 @@ test('should not use global config for preview', async ({ runInlineTest }) => { expect(result1.passed).toBe(1); const result2 = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, }, { workers: 1 }); expect(result2.exitCode).toBe(0); expect(result2.passed).toBe(1); @@ -386,7 +381,7 @@ test('should work with https enabled', async ({ runInlineTest }) => { test('list compilation cache should not clash with the run one', async ({ runInlineTest }) => { const listResult = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/button.tsx': ` @@ -414,7 +409,7 @@ test('should retain deps when test changes', async ({ runInlineTest }, testInfo) await test.step('original test', async () => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/button.tsx': ` @@ -456,10 +451,10 @@ test('should retain deps when test changes', async ({ runInlineTest }, testInfo) const metainfo = JSON.parse(fs.readFileSync(testInfo.outputPath('playwright/.cache/metainfo.json'), 'utf-8')); expect(metainfo.components).toEqual([{ - id: expect.stringContaining('playwright_test_src_button_tsx_Button'), + id: expect.stringContaining('button_tsx_Button'), remoteName: 'Button', - importPath: expect.stringContaining('button.tsx'), - isModuleOrAlias: false, + importSource: expect.stringContaining('button.tsx'), + filename: expect.stringContaining('button.test.tsx'), }]); for (const [, value] of Object.entries(metainfo.deps)) @@ -478,7 +473,7 @@ test('should retain deps when test changes', async ({ runInlineTest }, testInfo) test('should render component via re-export', async ({ runInlineTest }, testInfo) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/button.tsx': ` @@ -504,7 +499,7 @@ test('should render component via re-export', async ({ runInlineTest }, testInfo test('should render component exported via fixture', async ({ runInlineTest }, testInfo) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/button.tsx': ` @@ -534,7 +529,7 @@ test('should render component exported via fixture', async ({ runInlineTest }, t test('should pass imported images from test to component', async ({ runInlineTest }, testInfo) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/image.png': Buffer.from('iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAACMElEQVRYw+1XT0tCQRD/9Qci0Cw7mp1C6BMYnt5niMhPEEFCh07evNk54XnuGkhFehA/QxHkqYMEFWXpscMTipri7fqeu+vbfY+EoBkQ3Zn5zTo7MzsL/NNfoClkUUQNN3jCJ/ETfavRSpYkkSmFQzz8wMr4gaSp8OBJ2HCU4Iwd0kqGgd9GPxCccZ+0jWgWVW1wxlWy0qR51I3hv7lOllq7b4SC/+aGzr+QBadjEKgAykvzJGXwr/Lj4JfRk5hUSLKIa00HPUJRki0xeMWSWxVXmi5sddXKymqTyxdwquXAUVV3WREeLx3gTcNFWQY/jXtB8QIzgt4qTvAR4OCe0ATKCmrnmFMEM0Pp2BvrIisaFUdUjgKKZgYWSjjDLR5J+x13lATHuHSti6JBzQP+gq2QHXjfRaiJojbPgYqbmGFow0VpiyIW0/VIF9QKLzeBWA2MHmwCu8QJQV++Ps/joHQQH4HpuO0uobUeVztgIcr4Vnf4we9orWfUIWKHbEVyYKkPmaVpIVKICuo0ZYXWjHTITXWhsVYxkIDpUoKsla1i2Oz2QjvYG9fshu36GbFQ8DGyHNOuvRdOKZSDUtCFM7wyHeSM4XN8e7bOpd9F2gg+TRYal753bGkbuEjzMg0YW/yDV1czUDm+e43Byz86OnRwsYDMKXlmkYbeAOwffrtU/nGpXpwkXfPhVza+D9AiMAtrtOMYfVr0q8Wr1nh8n8ADZCJPqAk8AifyjP2n36cvkA6/Wln9MokAAAAASUVORK5CYII=', 'base64'), @@ -554,7 +549,7 @@ test('should pass imported images from test to component', async ({ runInlineTes test('should pass dates, regex, urls and bigints', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/button.tsx': ` @@ -591,7 +586,7 @@ test('should pass dates, regex, urls and bigints', async ({ runInlineTest }) => test('should pass undefined value as param', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/component.tsx': ` diff --git a/playwright/tests/playwright-test/playwright.ct-react.spec.ts b/playwright/tests/playwright-test/playwright.ct-react.spec.ts index f565c80ea2..a92f8297f1 100644 --- a/playwright/tests/playwright-test/playwright.ct-react.spec.ts +++ b/playwright/tests/playwright-test/playwright.ct-react.spec.ts @@ -14,16 +14,11 @@ * limitations under the License. */ -import { test, expect } from './playwright-test-fixtures'; - -const playwrightConfig = ` - import { defineConfig } from '@playwright/experimental-ct-react'; - export default defineConfig({ projects: [{name: 'foo'}] }); -`; +import { test, expect, playwrightCtConfigText } from './playwright-test-fixtures'; test('should work with TSX', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ` `, @@ -47,7 +42,7 @@ test('should work with TSX', async ({ runInlineTest }) => { test('should work with JSX', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.js': ` `, @@ -73,7 +68,7 @@ test('should work with JSX', async ({ runInlineTest }) => { test('should work with JSX in JS', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.js': ` `, @@ -99,7 +94,7 @@ test('should work with JSX in JS', async ({ runInlineTest }) => { test('should work with JSX in JS and in JSX', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.js': ` `, @@ -136,7 +131,7 @@ test('should work with JSX in JS and in JSX', async ({ runInlineTest }) => { test('should work with stray TSX import', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ` `, @@ -167,7 +162,7 @@ test('should work with stray TSX import', async ({ runInlineTest }) => { test('should work with stray JSX import', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.js': ` `, @@ -198,7 +193,7 @@ test('should work with stray JSX import', async ({ runInlineTest }) => { test('should work with stray JS import', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.js': ` `, @@ -229,7 +224,7 @@ test('should work with stray JS import', async ({ runInlineTest }) => { test('should work with JSX in variable', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.js': ` `, @@ -257,7 +252,7 @@ test('should work with JSX in variable', async ({ runInlineTest }) => { test('should return root locator for fragments', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.js': ``, @@ -283,7 +278,7 @@ test('should return root locator for fragments', async ({ runInlineTest }) => { test('should respect default property values', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/label.tsx': ` @@ -307,7 +302,7 @@ test('should respect default property values', async ({ runInlineTest }) => { test('should bundle public folder', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ` `, @@ -340,7 +335,7 @@ test('should bundle public folder', async ({ runInlineTest }) => { test('should work with property expressions in JSX', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ` `, @@ -420,7 +415,8 @@ test('should handle the vite host config', async ({ runInlineTest }) => { test('pass component', async ({ page, mount }) => { const component = await mount(); - await expect(page).toHaveURL('http://127.0.0.1:3100/'); + const host = await page.evaluate(() => window.location.hostname); + await expect(host).toBe('127.0.0.1'); }); `, }, { workers: 1 }); @@ -464,7 +460,7 @@ test('should prioritize the vite host config over the baseUrl config', async ({ test('should normalize children', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/component.tsx': ` @@ -498,7 +494,7 @@ test('should normalize children', async ({ runInlineTest }) => { test('should allow props children', async ({ runInlineTest }) => { const result = await runInlineTest({ - 'playwright.config.ts': playwrightConfig, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/component.spec.tsx': ` diff --git a/playwright/tests/playwright-test/playwright.spec.ts b/playwright/tests/playwright-test/playwright.spec.ts index 3fa9da5533..1c0212bf79 100644 --- a/playwright/tests/playwright-test/playwright.spec.ts +++ b/playwright/tests/playwright-test/playwright.spec.ts @@ -745,25 +745,6 @@ test('should skip on mobile', async ({ runInlineTest }) => { expect(result.passed).toBe(1); }); -test('fulfill with return path of the entry', async ({ runInlineTest }) => { - const storeDir = path.join(test.info().outputPath(), 'playwright'); - const file = path.join(storeDir, 'foo/body.json'); - await fs.promises.mkdir(path.dirname(file), { recursive: true }); - await fs.promises.writeFile(file, JSON.stringify({ 'a': 2023 })); - const result = await runInlineTest({ - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should read value from path', async ({ page }) => { - await page.route('**/*', route => route.fulfill({ path: _store.path('foo/body.json')})) - await page.goto('http://example.com'); - expect(await page.textContent('body')).toBe(JSON.stringify({ 'a': 2023 })) - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(1); -}); - test('should use actionTimeout for APIRequestContext', async ({ runInlineTest, server }) => { server.setRoute('/stall', (req, res) => {}); const result = await runInlineTest({ diff --git a/playwright/tests/playwright-test/playwright.trace.spec.ts b/playwright/tests/playwright-test/playwright.trace.spec.ts index c62e7c98ca..69f1912752 100644 --- a/playwright/tests/playwright-test/playwright.trace.spec.ts +++ b/playwright/tests/playwright-test/playwright.trace.spec.ts @@ -129,6 +129,7 @@ test('should record api trace', async ({ runInlineTest, server }, testInfo) => { ' fixture: context', ' fixture: request', ' apiRequestContext.dispose', + ' fixture: browser', ]); }); @@ -326,13 +327,13 @@ test('should not override trace file in afterAll', async ({ runInlineTest, serve 'After Hooks', ' fixture: page', ' fixture: context', - ' attach \"trace\"', ' afterAll hook', ' fixture: request', ' apiRequest.newContext', ' apiRequestContext.get', - ' fixture: request', - ' apiRequestContext.dispose', + ' fixture: request', + ' apiRequestContext.dispose', + ' fixture: browser', ]); expect(trace1.errors).toEqual([`'oh no!'`]); @@ -407,18 +408,16 @@ for (const mode of ['off', 'retain-on-failure', 'on-first-retry', 'on-all-retrie 'a.spec.ts': ` import { test as base, expect } from '@playwright/test'; import fs from 'fs'; - const test = base.extend<{ - locale: string | undefined, - _artifactsDir: () => string, - }>({ - // Override locale fixture to check in teardown that no temporary trace zip was created. - locale: [async ({ locale, _artifactsDir }, use) => { - await use(locale); - const entries = fs.readdirSync(_artifactsDir); + let artifactsDir; + const test = base.extend({ + workerAuto: [async ({}, use) => { + await use(); + const entries = fs.readdirSync(artifactsDir); expect(entries.filter(e => e.endsWith('.zip'))).toEqual([]); - }, { option: true }], + }, { scope: 'worker', auto: true }], }); test('passing test', async ({ page }) => { + artifactsDir = test.info()._tracing.artifactsDir(); await page.goto('about:blank'); }); `, @@ -432,18 +431,16 @@ for (const mode of ['off', 'retain-on-failure', 'on-first-retry', 'on-all-retrie 'a.spec.ts': ` import { test as base, expect } from '@playwright/test'; import fs from 'fs'; - const test = base.extend<{ - locale: string | undefined, - _artifactsDir: () => string, - }>({ - // Override locale fixture to check in teardown that no temporary trace zip was created. - locale: [async ({ locale, _artifactsDir }, use) => { - await use(locale); - const entries = fs.readdirSync(_artifactsDir); + let artifactsDir; + const test = base.extend({ + workerAuto: [async ({}, use) => { + await use(); + const entries = fs.readdirSync(artifactsDir); expect(entries.filter(e => e.endsWith('.zip'))).toEqual([]); - }, { option: true }], + }, { scope: 'worker', auto: true }], }); test('passing test', async ({ request }) => { + artifactsDir = test.info()._tracing.artifactsDir(); expect(await request.get('${server.EMPTY_PAGE}')).toBeOK(); }); `, @@ -671,10 +668,51 @@ test('should show non-expect error in trace', async ({ runInlineTest }, testInfo 'After Hooks', ' fixture: page', ' fixture: context', + ' fixture: browser', ]); expect(trace.errors).toEqual(['ReferenceError: undefinedVariable1 is not defined']); }); +test('should show error from beforeAll in trace', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { use: { trace: { mode: 'on' } } }; + `, + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test.beforeAll(async () => { + throw new Error('Oh my!'); + }) + test('fail', async ({ page }) => { + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + const trace = await parseTrace(testInfo.outputPath('test-results', 'a-fail', 'trace.zip')); + expect(trace.errors).toEqual(['Error: Oh my!']); +}); + +test('should throw when trace fixture is a function', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test.use({ + trace: async ({}, use) => { + await use('on'); + }, + }); + test('skipped', async ({ page }) => { + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + expect(result.output).toContain('Error: "trace" option cannot be a function'); +}); + test('should not throw when attachment is missing', async ({ runInlineTest }, testInfo) => { const result = await runInlineTest({ 'playwright.config.ts': ` @@ -799,3 +837,200 @@ test('should not throw when merging traces multiple times', async ({ runInlineTe expect(result.passed).toBe(1); expect(fs.existsSync(testInfo.outputPath('test-results', 'a-foo', 'trace.zip'))).toBe(true); }); + +test('should record nested steps, even after timeout', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { + use: { trace: { mode: 'on' } }, + timeout: 5000, + }; + `, + 'a.spec.ts': ` + import { test as base, expect } from '@playwright/test'; + const test = base.extend({ + fooPage: async ({ page }, use) => { + expect(1, 'fooPage setup').toBe(1); + await new Promise(f => setTimeout(f, 1)); // To avoid same-wall-time sorting issues. + await page.setContent('hello'); + await test.step('step in fooPage setup', async () => { + await page.setContent('bar'); + }); + await use(page); + expect(1, 'fooPage teardown').toBe(1); + await new Promise(f => setTimeout(f, 1)); // To avoid same-wall-time sorting issues. + await page.setContent('hi'); + await test.step('step in fooPage teardown', async () => { + await page.setContent('bar'); + }); + }, + barPage: async ({ browser }, use) => { + expect(1, 'barPage setup').toBe(1); + await new Promise(f => setTimeout(f, 1)); // To avoid same-wall-time sorting issues. + const page = await browser.newPage(); + await test.step('step in barPage setup', async () => { + await page.setContent('bar'); + }); + await use(page); + expect(1, 'barPage teardown').toBe(1); + await new Promise(f => setTimeout(f, 1)); // To avoid same-wall-time sorting issues. + await test.step('step in barPage teardown', async () => { + await page.close(); + }); + }, + }); + + test.beforeAll(async ({ barPage }) => { + expect(1, 'beforeAll start').toBe(1); + await new Promise(f => setTimeout(f, 1)); // To avoid same-wall-time sorting issues. + await barPage.setContent('hello'); + await test.step('step in beforeAll', async () => { + await barPage.setContent('bar'); + }); + }); + + test.beforeEach(async ({ fooPage }) => { + expect(1, 'beforeEach start').toBe(1); + await new Promise(f => setTimeout(f, 1)); // To avoid same-wall-time sorting issues. + await fooPage.setContent('hello'); + await test.step('step in beforeEach', async () => { + await fooPage.setContent('hi'); + // Next line times out. We make sure that after hooks steps + // form the expected step tree even when some previous steps have not finished. + await new Promise(() => {}); + }); + }); + + test('example', async ({ fooPage }) => { + }); + + test.afterEach(async ({ fooPage }) => { + expect(1, 'afterEach start').toBe(1); + await new Promise(f => setTimeout(f, 1)); // To avoid same-wall-time sorting issues. + await fooPage.setContent('hello'); + await test.step('step in afterEach', async () => { + await fooPage.setContent('bar'); + }); + }); + + test.afterAll(async ({ barPage }) => { + expect(1, 'afterAll start').toBe(1); + await new Promise(f => setTimeout(f, 1)); // To avoid same-wall-time sorting issues. + await barPage.setContent('hello'); + await test.step('step in afterAll', async () => { + await barPage.setContent('bar'); + }); + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + const trace = await parseTrace(testInfo.outputPath('test-results', 'a-example', 'trace.zip')); + expect(trace.actionTree).toEqual([ + 'Before Hooks', + ' beforeAll hook', + ' fixture: browser', + ' browserType.launch', + ' fixture: barPage', + ' barPage setup', + ' browser.newPage', + ' step in barPage setup', + ' page.setContent', + ' beforeAll start', + ' page.setContent', + ' step in beforeAll', + ' page.setContent', + ' fixture: barPage', + ' barPage teardown', + ' step in barPage teardown', + ' page.close', + ' beforeEach hook', + ' fixture: context', + ' browser.newContext', + ' fixture: page', + ' browserContext.newPage', + ' fixture: fooPage', + ' fooPage setup', + ' page.setContent', + ' step in fooPage setup', + ' page.setContent', + ' beforeEach start', + ' page.setContent', + ' step in beforeEach', + ' page.setContent', + 'After Hooks', + ' afterEach hook', + ' afterEach start', + ' page.setContent', + ' step in afterEach', + ' page.setContent', + ' fixture: fooPage', + ' fooPage teardown', + ' page.setContent', + ' step in fooPage teardown', + ' page.setContent', + ' fixture: page', + ' fixture: context', + ' afterAll hook', + ' fixture: barPage', + ' barPage setup', + ' browser.newPage', + ' step in barPage setup', + ' page.setContent', + ' afterAll start', + ' page.setContent', + ' step in afterAll', + ' page.setContent', + ' fixture: barPage', + ' barPage teardown', + ' step in barPage teardown', + ' page.close', + ' fixture: browser', + ]); +}); + +test('should attribute worker fixture teardown to the right test', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { + use: { trace: { mode: 'on' } }, + }; + `, + 'a.spec.ts': ` + import { test as base, expect } from '@playwright/test'; + const test = base.extend({ + foo: [async ({}, use) => { + expect(1, 'step in foo setup').toBe(1); + await use('foo'); + expect(1, 'step in foo teardown').toBe(1); + }, { scope: 'worker' }], + }); + + test('one', async ({ foo }) => { + }); + + test('two', async ({ foo }) => { + throw new Error('failure'); + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(1); + expect(result.passed).toBe(1); + expect(result.failed).toBe(1); + const trace1 = await parseTrace(testInfo.outputPath('test-results', 'a-one', 'trace.zip')); + expect(trace1.actionTree).toEqual([ + 'Before Hooks', + ' fixture: foo', + ' step in foo setup', + 'After Hooks', + ]); + const trace2 = await parseTrace(testInfo.outputPath('test-results', 'a-two', 'trace.zip')); + expect(trace2.actionTree).toEqual([ + 'Before Hooks', + 'After Hooks', + ' fixture: foo', + ' step in foo teardown', + ]); +}); diff --git a/playwright/tests/playwright-test/reporter-attachment.spec.ts b/playwright/tests/playwright-test/reporter-attachment.spec.ts index d761c0a4da..fd938be437 100644 --- a/playwright/tests/playwright-test/reporter-attachment.spec.ts +++ b/playwright/tests/playwright-test/reporter-attachment.spec.ts @@ -66,7 +66,7 @@ test('render trace attachment', async ({ runInlineTest }) => { test('one', async ({}, testInfo) => { testInfo.attachments.push({ name: 'trace', - path: testInfo.outputPath('trace.zip'), + path: testInfo.outputPath('my dir with space', 'trace.zip'), contentType: 'application/zip' }); expect(1).toBe(0); @@ -75,8 +75,8 @@ test('render trace attachment', async ({ runInlineTest }) => { }, { reporter: 'line' }); const text = result.output.replace(/\\/g, '/'); expect(text).toContain(' attachment #1: trace (application/zip) ─────────────────────────────────────────────────────────'); - expect(text).toContain(' test-results/a-one/trace.zip'); - expect(text).toContain('npx playwright show-trace test-results/a-one/trace.zip'); + expect(text).toContain(' test-results/a-one/my dir with space/trace.zip'); + expect(text).toContain('npx playwright show-trace "test-results/a-one/my dir with space/trace.zip"'); expect(text).toContain(' ────────────────────────────────────────────────────────────────────────────────────────────────'); expect(result.exitCode).toBe(1); }); diff --git a/playwright/tests/playwright-test/reporter-blob.spec.ts b/playwright/tests/playwright-test/reporter-blob.spec.ts index e69789fb45..f336e916ff 100644 --- a/playwright/tests/playwright-test/reporter-blob.spec.ts +++ b/playwright/tests/playwright-test/reporter-blob.spec.ts @@ -904,7 +904,7 @@ test('onError in the report', async ({ runInlineTest, mergeReports, showReport, test.skip('skipped 3', async ({}) => {}); ` }; - const result = await runInlineTest(files, { shard: `1/3` }); + const result = await runInlineTest(files, { shard: `1/3` }, { PWTEST_BOT_NAME: 'macos-node16-ttest' }); expect(result.exitCode).toBe(1); const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] }); @@ -917,6 +917,7 @@ test('onError in the report', async ({ runInlineTest, mergeReports, showReport, await expect(page.locator('.subnav-item:has-text("Failed") .counter')).toHaveText('0'); await expect(page.locator('.subnav-item:has-text("Flaky") .counter')).toHaveText('0'); await expect(page.locator('.subnav-item:has-text("Skipped") .counter')).toHaveText('1'); + await expect(page.getByTestId('report-errors')).toContainText('(macos-node16-ttest) Error: Error in teardown'); }); test('preserve config fields', async ({ runInlineTest, mergeReports }) => { @@ -1676,3 +1677,34 @@ test('merge reports without --config preserves path separators', async ({ runInl expect(output).toContain(`test: ${test.info().outputPath('dir1', 'tests2', 'b.test.js').replaceAll(path.sep, otherSeparator)}`); expect(output).toContain(`test title: ${'tests2' + otherSeparator + 'b.test.js'}`); }); + +test('TestSuite.project() should return owning project', async ({ runInlineTest, mergeReports }) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/29173' }); + const files1 = { + 'echo-reporter.js': ` + export default class EchoReporter { + onTestBegin(test) { + console.log('test project:', test.parent?.project()?.name); + } + }; + `, + 'merge.config.ts': `module.exports = { + testDir: 'mergeRoot', + reporter: './echo-reporter.js' + };`, + 'playwright.config.ts': `module.exports = { + testDir: 'tests', + reporter: [['blob', { outputDir: 'blob-report' }]], + projects: [{name: 'my-project'}] + };`, + 'tests/a.test.js': ` + import { test, expect } from '@playwright/test'; + test('math 1', async ({}) => { }); + `, + }; + await runInlineTest(files1); + + const { exitCode, output } = await mergeReports(test.info().outputPath('blob-report'), undefined, { additionalArgs: ['--config', 'merge.config.ts'] }); + expect(exitCode).toBe(0); + expect(output).toContain(`test project: my-project`); +}); diff --git a/playwright/tests/playwright-test/reporter-html.spec.ts b/playwright/tests/playwright-test/reporter-html.spec.ts index 0225b6c0e2..84f37be292 100644 --- a/playwright/tests/playwright-test/reporter-html.spec.ts +++ b/playwright/tests/playwright-test/reporter-html.spec.ts @@ -883,7 +883,8 @@ for (const useIntermediateMergeReport of [false, true] as const) { 'playwright.config.ts': ` import { gitCommitInfo } from 'playwright/lib/plugins'; import { test, expect } from '@playwright/test'; - export default { _plugins: [gitCommitInfo()] }; + const plugins = [gitCommitInfo()]; + export default { '@playwright/test': { plugins } }; `, 'example.spec.ts': ` import { test, expect } from '@playwright/test'; @@ -945,7 +946,7 @@ for (const useIntermediateMergeReport of [false, true] as const) { 'revision.email': 'shakespeare@example.local', }, }); - export default { _plugins: [plugin] }; + export default { '@playwright/test': { plugins: [plugin] } }; `, 'example.spec.ts': ` import { gitCommitInfo } from 'playwright/lib/plugins'; @@ -1126,7 +1127,7 @@ for (const useIntermediateMergeReport of [false, true] as const) { `, 'c.test.js': ` const { expect, test } = require('@playwright/test'); - test('@regression @failed failed', async ({}) => { + test('@regression @failed failed', { tag: '@foo' }, async ({}) => { expect(1).toBe(2); }); test('@regression @flaky flaky', async ({}, testInfo) => { @@ -1135,7 +1136,7 @@ for (const useIntermediateMergeReport of [false, true] as const) { else expect(1).toBe(2); }); - test.skip('@regression skipped', async ({}) => { + test.skip('@regression skipped', { tag: ['@foo', '@bar'] }, async ({}) => { expect(1).toBe(2); }); `, @@ -1147,58 +1148,66 @@ for (const useIntermediateMergeReport of [false, true] as const) { await showReport(); - await expect(page.locator('.test-file-test .label')).toHaveCount(42); await expect(page.locator('.test-file-test', { has: page.getByText('@regression @failed failed', { exact: true }) }).locator('.label')).toHaveText([ 'chromium', - 'failed', 'regression', - 'firefox', 'failed', + 'foo', + 'firefox', 'regression', + 'failed', + 'foo', 'webkit', + 'regression', 'failed', - 'regression' + 'foo', ]); await expect(page.locator('.test-file-test', { has: page.getByText('@regression @flaky flaky', { exact: true }) }).locator('.label')).toHaveText([ 'chromium', - 'flaky', 'regression', - 'firefox', 'flaky', + 'firefox', 'regression', - 'webkit', 'flaky', + 'webkit', 'regression', + 'flaky', ]); await expect(page.locator('.test-file-test', { has: page.getByText('@regression skipped', { exact: true }) }).locator('.label')).toHaveText([ 'chromium', 'regression', + 'foo', + 'bar', 'firefox', 'regression', + 'foo', + 'bar', 'webkit', 'regression', + 'foo', + 'bar', ]); await expect(page.locator('.test-file-test', { has: page.getByText('@smoke @passed passed', { exact: true }) }).locator('.label')).toHaveText([ 'chromium', - 'passed', 'smoke', - 'firefox', 'passed', + 'firefox', 'smoke', + 'passed', 'webkit', + 'smoke', 'passed', - 'smoke' ]); await expect(page.locator('.test-file-test', { has: page.getByText('@smoke @failed failed', { exact: true }) }).locator('.label')).toHaveText([ 'chromium', - 'failed', 'smoke', - 'firefox', 'failed', + 'firefox', 'smoke', - 'webkit', 'failed', + 'webkit', 'smoke', + 'failed', ]); }); @@ -1952,7 +1961,7 @@ for (const useIntermediateMergeReport of [false, true] as const) { await expect(page).toHaveURL(/testId/); await expect(page.locator('.test-case-path')).toHaveText('Root describe › @Notifications'); await expect(page.locator('.test-case-title')).toHaveText('Test failed -- @call @call-details @e2e @regression #VQ458'); - await expect(page.locator('.label')).toHaveText(['chromium', 'call', 'call-details', 'e2e', 'Notifications', 'regression']); + await expect(page.locator('.label')).toHaveText(['chromium', 'Notifications', 'call', 'call-details', 'e2e', 'regression']); await page.goBack(); await expect(page).not.toHaveURL(/testId/); @@ -1964,7 +1973,7 @@ for (const useIntermediateMergeReport of [false, true] as const) { await expect(page).toHaveURL(/testId/); await expect(page.locator('.test-case-path')).toHaveText('Root describe › @Monitoring'); await expect(page.locator('.test-case-title')).toHaveText('Test passed -- @call @call-details @e2e @regression #VQ457'); - await expect(page.locator('.label')).toHaveText(['firefox', 'call', 'call-details', 'e2e', 'Monitoring', 'regression']); + await expect(page.locator('.label')).toHaveText(['firefox', 'Monitoring', 'call', 'call-details', 'e2e', 'regression']); }); }); diff --git a/playwright/tests/playwright-test/reporter-json.spec.ts b/playwright/tests/playwright-test/reporter-json.spec.ts index f86f22d4fd..904fc92759 100644 --- a/playwright/tests/playwright-test/reporter-json.spec.ts +++ b/playwright/tests/playwright-test/reporter-json.spec.ts @@ -160,29 +160,15 @@ test('should display tags separately from title', async ({ runInlineTest }) => { const result = await runInlineTest({ 'a.test.js': ` import { test, expect } from '@playwright/test'; - test('math works! @USR-MATH-001 @USR-MATH-002', async ({}) => { - expect(1 + 1).toBe(2); - await test.step('math works in a step', async () => { - expect(2 + 2).toBe(4); - await test.step('nested step', async () => { - expect(2 + 2).toBe(4); - await test.step('deeply nested step', async () => { - expect(2 + 2).toBe(4); - }); - }) - }) + test('math works! @USR-MATH-001 @USR-MATH-002', { tag: '@foo' }, async ({}) => { + test.info().annotations.push({ type: 'issue', description: 'issue-link' }); + test.info().annotations.push({ type: 'novalue' }); }); ` }); expect(result.exitCode).toBe(0); - expect(result.report.suites.length).toBe(1); - expect(result.report.suites[0].specs.length).toBe(1); - // Ensure the length is as expected - expect(result.report.suites[0].specs[0].tags.length).toBe(2); - // Ensure that the '@' value is stripped - expect(result.report.suites[0].specs[0].tags[0]).toBe('USR-MATH-001'); - expect(result.report.suites[0].specs[0].tags[1]).toBe('USR-MATH-002'); + expect(result.report.suites[0].specs[0].tags).toEqual(['USR-MATH-001', 'USR-MATH-002', 'foo']); }); test('should have relative always-posix paths', async ({ runInlineTest }) => { diff --git a/playwright/tests/playwright-test/reporter.spec.ts b/playwright/tests/playwright-test/reporter.spec.ts index 4157310dd9..aaa24f7fbd 100644 --- a/playwright/tests/playwright-test/reporter.spec.ts +++ b/playwright/tests/playwright-test/reporter.spec.ts @@ -797,4 +797,123 @@ var import_test = __toModule(require("@playwright/test")); 4 | `); }); }); -} \ No newline at end of file +} + +test('should report a stable test.id', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'reporter.ts': ` + class Reporter { + onTestBegin(test) { + console.log('\\n%%testbegin-' + test.id); + } + } + export default Reporter; + `, + 'playwright.config.ts': ` + module.exports = { reporter: [[ './reporter.ts' ]] }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('example test', async ({}) => { + }); + ` + }, { reporter: '', workers: 1 }); + + expect(result.exitCode).toBe(0); + expect(result.outputLines).toEqual([ + 'testbegin-20289bcdad95a5e18c38-8b63c3695b9c8bd62d98', + ]); +}); + +test('should report annotations from test declaration', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'reporter.ts': ` + export default class Reporter { + onBegin(config, suite) { + const visit = suite => { + for (const test of suite.tests || []) { + const annotations = test.annotations.map(a => { + return a.description ? a.type + '=' + a.description : a.type; + }); + console.log('\\n%%title=' + test.title + ', annotations=' + annotations.join(',')); + } + for (const child of suite.suites || []) + visit(child); + }; + visit(suite); + } + onError(error) { + console.log(error); + } + } + `, + 'playwright.config.ts': ` + module.exports = { + reporter: './reporter', + }; + `, + 'stdio.spec.js': ` + import { test, expect } from '@playwright/test'; + test('none', () => { + expect(test.info().annotations).toEqual([]); + }); + test('foo', { annotation: { type: 'foo' } }, () => { + expect(test.info().annotations).toEqual([{ type: 'foo' }]); + }); + test('foo-bar', { + annotation: [ + { type: 'foo', description: 'desc' }, + { type: 'bar' }, + ], + }, () => { + expect(test.info().annotations).toEqual([ + { type: 'foo', description: 'desc' }, + { type: 'bar' }, + ]); + }); + test.skip('skip-foo', { annotation: { type: 'foo' } }, () => { + }); + test.fixme('fixme-bar', { annotation: { type: 'bar' } }, () => { + }); + test.fail('fail-foo-bar', { + annotation: [ + { type: 'foo' }, + { type: 'bar', description: 'desc' }, + ], + }, () => { + expect(1).toBe(2); + }); + test.describe('suite', { annotation: { type: 'foo' } }, () => { + test('foo-suite', () => { + expect(test.info().annotations).toEqual([{ type: 'foo' }]); + }); + test.describe('inner', { annotation: { type: 'bar' } }, () => { + test('foo-bar-suite', () => { + expect(test.info().annotations).toEqual([{ type: 'foo' }, { type: 'bar' }]); + }); + }); + }); + test.describe.skip('skip-foo-suite', { annotation: { type: 'foo' } }, () => { + test('skip-foo-suite', () => { + }); + }); + test.describe.fixme('fixme-bar-suite', { annotation: { type: 'bar' } }, () => { + test('fixme-bar-suite', () => { + }); + }); + ` + }); + expect(result.exitCode).toBe(0); + expect(result.outputLines).toEqual([ + `title=none, annotations=`, + `title=foo, annotations=foo`, + `title=foo-bar, annotations=foo=desc,bar`, + `title=skip-foo, annotations=foo,skip`, + `title=fixme-bar, annotations=bar,fixme`, + `title=fail-foo-bar, annotations=foo,bar=desc,fail`, + `title=foo-suite, annotations=foo`, + `title=foo-bar-suite, annotations=foo,bar`, + `title=skip-foo-suite, annotations=foo,skip`, + `title=fixme-bar-suite, annotations=bar,fixme`, + ]); +}); diff --git a/playwright/tests/playwright-test/resolver.spec.ts b/playwright/tests/playwright-test/resolver.spec.ts index f0e95e91ca..778efefe1f 100644 --- a/playwright/tests/playwright-test/resolver.spec.ts +++ b/playwright/tests/playwright-test/resolver.spec.ts @@ -570,3 +570,39 @@ test('should import packages with non-index main script through path resolver', expect(result.output).not.toContain(`find module`); expect(result.output).toContain(`foo=42`); }); + +test('should respect tsconfig project references', async ({ runInlineTest }) => { + test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/29256' }); + + const result = await runInlineTest({ + 'playwright.config.ts': `export default { projects: [{name: 'foo'}], };`, + 'tsconfig.json': `{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.test.json" } + ] + }`, + 'tsconfig.test.json': `{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "util/*": ["./foo/bar/util/*"], + }, + }, + }`, + 'foo/bar/util/b.ts': ` + export const foo: string = 'foo'; + `, + 'a.test.ts': ` + import { foo } from 'util/b'; + import { test, expect } from '@playwright/test'; + test('test', ({}, testInfo) => { + expect(foo).toBe('foo'); + }); + `, + }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); +}); diff --git a/playwright/tests/playwright-test/runner.spec.ts b/playwright/tests/playwright-test/runner.spec.ts index 51a8c9346e..bc157158e3 100644 --- a/playwright/tests/playwright-test/runner.spec.ts +++ b/playwright/tests/playwright-test/runner.spec.ts @@ -450,8 +450,8 @@ test('sigint should stop plugins', async ({ interactWithTestRunner }) => { const testProcess = await interactWithTestRunner({ 'playwright.config.ts': ` - const _plugins = []; - _plugins.push(() => ({ + const plugins = []; + plugins.push(() => ({ setup: async () => { console.log('Plugin1 setup'); console.log('%%SEND-SIGINT%%'); @@ -462,7 +462,7 @@ test('sigint should stop plugins', async ({ interactWithTestRunner }) => { } })); - _plugins.push(() => ({ + plugins.push(() => ({ setup: async () => { console.log('Plugin2 setup'); }, @@ -471,7 +471,7 @@ test('sigint should stop plugins', async ({ interactWithTestRunner }) => { } })); module.exports = { - _plugins + '@playwright/test': { plugins } }; `, 'a.spec.js': ` @@ -500,8 +500,8 @@ test('sigint should stop plugins 2', async ({ interactWithTestRunner }) => { const testProcess = await interactWithTestRunner({ 'playwright.config.ts': ` - const _plugins = []; - _plugins.push(() => ({ + const plugins = []; + plugins.push(() => ({ setup: async () => { console.log('Plugin1 setup'); }, @@ -510,7 +510,7 @@ test('sigint should stop plugins 2', async ({ interactWithTestRunner }) => { } })); - _plugins.push(() => ({ + plugins.push(() => ({ setup: async () => { console.log('Plugin2 setup'); console.log('%%SEND-SIGINT%%'); @@ -520,7 +520,7 @@ test('sigint should stop plugins 2', async ({ interactWithTestRunner }) => { console.log('Plugin2 teardown'); } })); - module.exports = { _plugins }; + module.exports = { '@playwright/test': { plugins } }; `, 'a.spec.js': ` import { test, expect } from '@playwright/test'; diff --git a/playwright/tests/playwright-test/stable-test-runner/package-lock.json b/playwright/tests/playwright-test/stable-test-runner/package-lock.json index 2c24fc38a3..2594cfcffd 100644 --- a/playwright/tests/playwright-test/stable-test-runner/package-lock.json +++ b/playwright/tests/playwright-test/stable-test-runner/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "dependencies": { - "@playwright/test": "1.41.0-alpha-1701898456000" + "@playwright/test": "1.41.0-beta-1705101589000" } }, "node_modules/@playwright/test": { - "version": "1.41.0-alpha-1701898456000", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.0-alpha-1701898456000.tgz", - "integrity": "sha512-46ficGy9xq2/sxxBmcr18sR5vT8n+bbeVLfBzdeULWNv7x6XKaaD39Ph0hrKOItB233ugwe3zTcZL8lOCLq0rg==", + "version": "1.41.0-beta-1705101589000", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.0-beta-1705101589000.tgz", + "integrity": "sha512-tpaEm+ih0PhJdk6yrRtk40I9ahErhGYZT2lR65ZZ6Il7tMnUgzxF5OOw3XLGJ59T29nMyI1+hKGqQnf8nUKkQw==", "dependencies": { - "playwright": "1.41.0-alpha-1701898456000" + "playwright": "1.41.0-beta-1705101589000" }, "bin": { "playwright": "cli.js" @@ -36,11 +36,11 @@ } }, "node_modules/playwright": { - "version": "1.41.0-alpha-1701898456000", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0-alpha-1701898456000.tgz", - "integrity": "sha512-dpHKXrb3zoi+4aT0X7uhu7Dk7xc5X19WapnbKE6SKOJ3oukFBwQWU6QpjI66gBkE3Ay6+TElRnkloMI9Fz1NnQ==", + "version": "1.41.0-beta-1705101589000", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0-beta-1705101589000.tgz", + "integrity": "sha512-3mMpZXmkw+fGIb+wBpvDZ4OEm7L1QYptgJgTdP8/OEitYjV5Q05MRZWbgelF/9ameoRpMz7rbkZTWUh/1UtrIw==", "dependencies": { - "playwright-core": "1.41.0-alpha-1701898456000" + "playwright-core": "1.41.0-beta-1705101589000" }, "bin": { "playwright": "cli.js" @@ -53,9 +53,9 @@ } }, "node_modules/playwright-core": { - "version": "1.41.0-alpha-1701898456000", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0-alpha-1701898456000.tgz", - "integrity": "sha512-zXy9rWMbQSdXeIYwxGQyYLuSZwHGTwQX7aXQO1Wtk7Zq/zGgZISSD4L9AwL36D44/N02KB9eFtmS6iyOBUpCzQ==", + "version": "1.41.0-beta-1705101589000", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0-beta-1705101589000.tgz", + "integrity": "sha512-w3aDw2Kp/ZwAUSqLZdmd+mq6khl3ufb2csM51b85r9t8S4m2JYoz1WHFNGNHqFWHBXSrjSBXzFLNgKUmwizRuA==", "bin": { "playwright-core": "cli.js" }, @@ -66,11 +66,11 @@ }, "dependencies": { "@playwright/test": { - "version": "1.41.0-alpha-1701898456000", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.0-alpha-1701898456000.tgz", - "integrity": "sha512-46ficGy9xq2/sxxBmcr18sR5vT8n+bbeVLfBzdeULWNv7x6XKaaD39Ph0hrKOItB233ugwe3zTcZL8lOCLq0rg==", + "version": "1.41.0-beta-1705101589000", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.0-beta-1705101589000.tgz", + "integrity": "sha512-tpaEm+ih0PhJdk6yrRtk40I9ahErhGYZT2lR65ZZ6Il7tMnUgzxF5OOw3XLGJ59T29nMyI1+hKGqQnf8nUKkQw==", "requires": { - "playwright": "1.41.0-alpha-1701898456000" + "playwright": "1.41.0-beta-1705101589000" } }, "fsevents": { @@ -80,18 +80,18 @@ "optional": true }, "playwright": { - "version": "1.41.0-alpha-1701898456000", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0-alpha-1701898456000.tgz", - "integrity": "sha512-dpHKXrb3zoi+4aT0X7uhu7Dk7xc5X19WapnbKE6SKOJ3oukFBwQWU6QpjI66gBkE3Ay6+TElRnkloMI9Fz1NnQ==", + "version": "1.41.0-beta-1705101589000", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0-beta-1705101589000.tgz", + "integrity": "sha512-3mMpZXmkw+fGIb+wBpvDZ4OEm7L1QYptgJgTdP8/OEitYjV5Q05MRZWbgelF/9ameoRpMz7rbkZTWUh/1UtrIw==", "requires": { "fsevents": "2.3.2", - "playwright-core": "1.41.0-alpha-1701898456000" + "playwright-core": "1.41.0-beta-1705101589000" } }, "playwright-core": { - "version": "1.41.0-alpha-1701898456000", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0-alpha-1701898456000.tgz", - "integrity": "sha512-zXy9rWMbQSdXeIYwxGQyYLuSZwHGTwQX7aXQO1Wtk7Zq/zGgZISSD4L9AwL36D44/N02KB9eFtmS6iyOBUpCzQ==" + "version": "1.41.0-beta-1705101589000", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0-beta-1705101589000.tgz", + "integrity": "sha512-w3aDw2Kp/ZwAUSqLZdmd+mq6khl3ufb2csM51b85r9t8S4m2JYoz1WHFNGNHqFWHBXSrjSBXzFLNgKUmwizRuA==" } } } diff --git a/playwright/tests/playwright-test/stable-test-runner/package.json b/playwright/tests/playwright-test/stable-test-runner/package.json index 81da603642..17ce2477d8 100644 --- a/playwright/tests/playwright-test/stable-test-runner/package.json +++ b/playwright/tests/playwright-test/stable-test-runner/package.json @@ -1,6 +1,6 @@ { "private": true, "dependencies": { - "@playwright/test": "1.41.0-alpha-1701898456000" + "@playwright/test": "1.41.0-beta-1705101589000" } } diff --git a/playwright/tests/playwright-test/store.spec.ts b/playwright/tests/playwright-test/store.spec.ts deleted file mode 100644 index ddf25638fa..0000000000 --- a/playwright/tests/playwright-test/store.spec.ts +++ /dev/null @@ -1,337 +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 fs from 'fs'; -import path from 'path'; -import { expect, test } from './playwright-test-fixtures'; - -test('should provide _store fixture', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'playwright.config.js': ` - module.exports = {}; - `, - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should _store number', async ({ }) => { - expect(_store).toBeTruthy(); - expect(await _store.get('number.json')).toBe(undefined); - await _store.set('number.json', 2022) - expect(await _store.get('number.json')).toBe(2022); - }); - test('should _store object', async ({ }) => { - expect(_store).toBeTruthy(); - expect(await _store.get('object.json')).toBe(undefined); - await _store.set('object.json', { 'a': 2022 }) - expect(await _store.get('object.json')).toEqual({ 'a': 2022 }); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(2); -}); - -test('should share _store state between project setup and tests', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'playwright.config.js': ` - module.exports = { - projects: [ - { - name: 'p1', - testMatch: /.*_store.setup.ts/ - }, - { - name: 'p2', - dependencies: ['p1'], - testMatch: /.*.test.ts/ - } - ] - }; - `, - '_store.setup.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should initialize _store', async ({ }) => { - expect(await _store.get('number.json')).toBe(undefined); - await _store.set('number.json', 2022) - expect(await _store.get('number.json')).toBe(2022); - - expect(await _store.get('object.json')).toBe(undefined); - await _store.set('object.json', { 'a': 2022 }) - expect(await _store.get('object.json')).toEqual({ 'a': 2022 }); - }); - `, - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should get data from setup', async ({ }) => { - expect(await _store.get('number.json')).toBe(2022); - expect(await _store.get('object.json')).toEqual({ 'a': 2022 }); - }); - `, - 'b.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should get data from setup', async ({ }) => { - expect(await _store.get('number.json')).toBe(2022); - expect(await _store.get('object.json')).toEqual({ 'a': 2022 }); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(3); -}); - -test('should persist _store state between project runs', async ({ runInlineTest }) => { - const files = { - 'playwright.config.js': ` - module.exports = { }; - `, - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should have no data on first run', async ({ }) => { - expect(await _store.get('number.json')).toBe(undefined); - await _store.set('number.json', 2022) - expect(await _store.get('object.json')).toBe(undefined); - await _store.set('object.json', { 'a': 2022 }) - }); - `, - 'b.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should get data from previous run', async ({ }) => { - expect(await _store.get('number.json')).toBe(2022); - expect(await _store.get('object.json')).toEqual({ 'a': 2022 }); - }); - `, - }; - { - const result = await runInlineTest(files, { grep: 'should have no data on first run' }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(1); - } - { - const result = await runInlineTest(files, { grep: 'should get data from previous run' }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(1); - } -}); - -test('should load context storageState from _store', async ({ runInlineTest, server }) => { - server.setRoute('/setcookie.html', (req, res) => { - res.setHeader('Set-Cookie', ['a=v1']); - res.end(); - }); - const result = await runInlineTest({ - 'playwright.config.js': ` - module.exports = { - projects: [ - { - name: 'setup', - testMatch: /.*_store.setup.ts/ - }, - { - name: 'p2', - dependencies: ['setup'], - testMatch: /.*.test.ts/ - } - ] - }; - `, - '_store.setup.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should save storageState', async ({ page, context }) => { - expect(await _store.get('user')).toBe(undefined); - await page.goto('${server.PREFIX}/setcookie.html'); - const state = await page.context().storageState(); - await _store.set('user.json', state); - }); - `, - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test.use({ - storageState: async ({}, use) => use(_store.get('user.json')) - }) - test('should get data from setup', async ({ page }) => { - await page.goto('${server.EMPTY_PAGE}'); - const cookies = await page.evaluate(() => document.cookie); - expect(cookies).toBe('a=v1'); - }); - `, - 'b.test.ts': ` - import { test, expect } from '@playwright/test'; - test('should not get data from setup if not configured', async ({ page }) => { - await page.goto('${server.EMPTY_PAGE}'); - const cookies = await page.evaluate(() => document.cookie); - expect(cookies).toBe(''); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(3); -}); - -test('should load value from filesystem', async ({ runInlineTest }) => { - const _storeDir = test.info().outputPath('playwright'); - const file = path.join(_storeDir, 'foo/bar.json'); - await fs.promises.mkdir(path.dirname(file), { recursive: true }); - await fs.promises.writeFile(file, JSON.stringify({ 'a': 2023 })); - const result = await runInlineTest({ - 'playwright.config.js': ` - module.exports = {}; - `, - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should _store number', async ({ }) => { - expect(await _store.get('foo/bar.json')).toEqual({ 'a': 2023 }); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(1); -}); - -test('should return root path', async ({ runInlineTest }) => { - const _storeDir = test.info().outputPath('playwright'); - const result = await runInlineTest({ - 'playwright.config.js': ` - module.exports = {}; - `, - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should _store number', async ({ }) => { - expect(_store.root()).toBe('${_storeDir.replace(/\\/g, '\\\\')}'); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(1); -}); - -test('should work in global setup and teardown', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'playwright.config.ts': ` - import * as path from 'path'; - module.exports = { - globalSetup: 'globalSetup.ts', - globalTeardown: 'globalTeardown.ts', - }; - `, - 'globalSetup.ts': ` - import { _store, expect } from '@playwright/test'; - module.exports = async () => { - expect(_store).toBeTruthy(); - await _store.set('foo/bar.json', {'a': 2023}); - }; - `, - 'globalTeardown.ts': ` - import { _store, expect } from '@playwright/test'; - module.exports = async () => { - const val = await _store.get('foo/bar.json'); - console.log('teardown=' + val); - }; - `, - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should read value from global setup', async ({ }) => { - expect(await _store.get('foo/bar.json')).toEqual({ 'a': 2023 }); - await _store.set('foo/bar.json', 'from test'); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(1); -}); - -test('_store root can be changed with TestConfig._storeDir', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'playwright.config.ts': ` - import * as path from 'path'; - module.exports = { - _storeDir: 'my/_store/dir', - }; - `, - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should _store value', async ({ }) => { - await _store.set('foo/bar.json', {'a': 2023}); - }); - test('should read value', async ({ }) => { - expect(await _store.get('foo/bar.json')).toEqual({ 'a': 2023 }); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(2); - const file = path.join(test.info().outputPath(), 'my/_store/dir/foo/bar.json'); - expect(JSON.parse(await fs.promises.readFile(file, 'utf-8'))).toEqual({ 'a': 2023 }); -}); - -test('should delete value', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('should _store value', async ({ }) => { - await _store.set('foo/bar.json', {'a': 2023}); - expect(await _store.get('foo/bar.json')).toEqual({ 'a': 2023 }); - await _store.delete('foo/bar.json'); - expect(await _store.get('foo/bar.json')).toBe(undefined); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(1); -}); - -test('should support text, json and binary values', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('json', async ({ }) => { - await _store.set('key.json', {'a': 2023}); - expect(await _store.get('key.json')).toEqual({ 'a': 2023 }); - }); - test('text', async ({ }) => { - await _store.set('key.txt', 'Hello'); - expect(await _store.get('key.txt')).toEqual('Hello'); - }); - test('binary', async ({ }) => { - const buf = Buffer.alloc(256); - for (let i = 0; i < 256; i++) - buf[i] = i; - await _store.set('key.png', buf); - expect(await _store.get('key.png')).toEqual(buf); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(3); -}); - -test('should throw on unsupported value type for given key extension', async ({ runInlineTest }) => { - const result = await runInlineTest({ - 'a.test.ts': ` - import { test, _store, expect } from '@playwright/test'; - test('json', async ({ }) => { - const buf = Buffer.alloc(5); - await _store.set('key.json', buf); - }); - test('text', async ({ }) => { - await _store.set('key.txt', {}); - }); - test('binary', async ({ }) => { - await _store.set('key.png', {}); - }); - `, - }, { workers: 1 }); - expect(result.exitCode).toBe(1); - expect(result.failed).toBe(3); -}); diff --git a/playwright/tests/playwright-test/test-modifiers.spec.ts b/playwright/tests/playwright-test/test-modifiers.spec.ts index ce9a1aff9a..0b94769107 100644 --- a/playwright/tests/playwright-test/test-modifiers.spec.ts +++ b/playwright/tests/playwright-test/test-modifiers.spec.ts @@ -143,6 +143,8 @@ test.describe('test modifier annotations', () => { test('skip inner', () => { test.skip(); }); test.fixme('fixme wrap', () => {}); test('fixme inner', () => { test.fixme(); }); + test.fail('fail wrap', () => { expect(1).toBe(2); }); + test('fail inner', () => { test.fail(); expect(1).toBe(2); }); }); test('example', () => {}); @@ -151,13 +153,15 @@ test.describe('test modifier annotations', () => { const expectTest = expectTestHelper(result); expect(result.exitCode).toBe(0); - expect(result.passed).toBe(2); + expect(result.passed).toBe(4); expect(result.skipped).toBe(4); expectTest('no marker', 'passed', 'expected', []); expectTest('skip wrap', 'skipped', 'skipped', ['skip']); expectTest('skip inner', 'skipped', 'skipped', ['skip']); expectTest('fixme wrap', 'skipped', 'skipped', ['fixme']); expectTest('fixme inner', 'skipped', 'skipped', ['fixme']); + expectTest('fail wrap', 'failed', 'expected', ['fail']); + expectTest('fail inner', 'failed', 'expected', ['fail']); expectTest('example', 'passed', 'expected', []); }); @@ -185,7 +189,7 @@ test.describe('test modifier annotations', () => { expect(result.passed).toBe(0); expect(result.skipped).toBe(6); expectTest('no marker', 'skipped', 'skipped', ['fixme']); - expectTest('skip wrap', 'skipped', 'skipped', ['skip', 'fixme']); + expectTest('skip wrap', 'skipped', 'skipped', ['fixme', 'skip']); expectTest('skip inner', 'skipped', 'skipped', ['fixme']); expectTest('fixme wrap', 'skipped', 'skipped', ['fixme', 'fixme']); expectTest('fixme inner', 'skipped', 'skipped', ['fixme']); @@ -216,7 +220,7 @@ test.describe('test modifier annotations', () => { expectTest('no marker', 'skipped', 'skipped', ['skip']); expectTest('skip wrap', 'skipped', 'skipped', ['skip', 'skip']); expectTest('skip inner', 'skipped', 'skipped', ['skip']); - expectTest('fixme wrap', 'skipped', 'skipped', ['fixme', 'skip']); + expectTest('fixme wrap', 'skipped', 'skipped', ['skip', 'fixme']); expectTest('fixme inner', 'skipped', 'skipped', ['skip']); expectTest('example', 'passed', 'expected', []); }); @@ -247,7 +251,7 @@ test.describe('test modifier annotations', () => { expect(result.exitCode).toBe(0); expect(result.passed).toBe(0); expect(result.skipped).toBe(2); - expectTest('fixme wrap', 'skipped', 'skipped', ['fixme', 'fixme', 'skip', 'skip', 'fixme']); + expectTest('fixme wrap', 'skipped', 'skipped', ['fixme', 'skip', 'skip', 'fixme', 'fixme']); expectTest('fixme inner', 'skipped', 'skipped', ['fixme', 'skip', 'skip', 'fixme']); }); @@ -517,7 +521,7 @@ test('modifier timeout should be reported', async ({ runInlineTest }) => { expect(result.output).toContain('3 | test.skip(async () => new Promise(() => {}));'); }); -test('should not run hooks if modifier throws', async ({ runInlineTest }) => { +test('should run beforeAll/afterAll hooks if modifier throws', async ({ runInlineTest }) => { const result = await runInlineTest({ 'a.test.ts': ` import { test, expect } from '@playwright/test'; @@ -526,7 +530,7 @@ test('should not run hooks if modifier throws', async ({ runInlineTest }) => { throw new Error('Oh my'); }); test.beforeAll(() => { - console.log('%%beforeEach'); + console.log('%%beforeAll'); }); test.beforeEach(() => { console.log('%%beforeEach'); @@ -535,7 +539,7 @@ test('should not run hooks if modifier throws', async ({ runInlineTest }) => { console.log('%%afterEach'); }); test.afterAll(() => { - console.log('%%beforeEach'); + console.log('%%afterAll'); }); test('skipped1', () => { console.log('%%skipped1'); @@ -546,9 +550,48 @@ test('should not run hooks if modifier throws', async ({ runInlineTest }) => { expect(result.failed).toBe(1); expect(result.outputLines).toEqual([ 'modifier', + 'beforeAll', + 'afterAll', ]); }); +test('should skip all tests from beforeAll', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test.beforeAll(() => { + console.log('%%beforeAll'); + test.skip(true, 'reason'); + }); + test.beforeAll(() => { + console.log('%%beforeAll2'); + }); + test.beforeEach(() => { + console.log('%%beforeEach'); + }); + test.afterEach(() => { + console.log('%%afterEach'); + }); + test.afterAll(() => { + console.log('%%afterAll'); + }); + test('skipped1', () => { + console.log('%%skipped1'); + }); + test('skipped2', () => { + console.log('%%skipped2'); + }); + `, + }); + expect(result.exitCode).toBe(0); + expect(result.outputLines).toEqual([ + 'beforeAll', + 'afterAll', + ]); + expect(result.report.suites[0].specs[0].tests[0].annotations).toEqual([{ type: 'skip', description: 'reason' }]); + expect(result.report.suites[0].specs[1].tests[0].annotations).toEqual([{ type: 'skip', description: 'reason' }]); +}); + test('should report skipped tests in-order with correct properties', async ({ runInlineTest }) => { const result = await runInlineTest({ 'reporter.ts': ` diff --git a/playwright/tests/playwright-test/test-step.spec.ts b/playwright/tests/playwright-test/test-step.spec.ts index 6799ee5b55..c00521e9ae 100644 --- a/playwright/tests/playwright-test/test-step.spec.ts +++ b/playwright/tests/playwright-test/test-step.spec.ts @@ -47,8 +47,8 @@ class Reporter { distillError(error) { return { error: { - message: stripAnsi(error.message), - stack: stripAnsi(error.stack), + message: stripAnsi(error.message || ''), + stack: stripAnsi(error.stack || ''), } }; } @@ -351,6 +351,12 @@ test('should not report nested after hooks', async ({ runInlineTest }) => { }, ], }, + { + error: { + message: 'Test timeout of 2000ms exceeded.', + stack: 'Test timeout of 2000ms exceeded.', + }, + }, ]); }); @@ -972,6 +978,18 @@ test('should not mark page.close as failed when page.click fails', async ({ runI }, ], }, + { + error: { + message: 'Test timeout of 2000ms exceeded.', + stack: 'Test timeout of 2000ms exceeded.', + }, + }, + { + error: { + message: expect.stringContaining('Error: page.click'), + stack: expect.stringContaining('Error: page.click'), + }, + }, ]); }); @@ -1172,7 +1190,7 @@ test('should propagate nested soft errors', async ({ runInlineTest }) => { expect.soft(1).toBe(2); }); }); - + await test.step('second outer', async () => { await test.step('second inner', async () => { expect(1).toBe(2); @@ -1267,7 +1285,7 @@ test('should not propagate nested hard errors', async ({ runInlineTest }) => { } }); }); - + await test.step('second outer', async () => { await test.step('second inner', async () => { expect(1).toBe(2); diff --git a/playwright/tests/playwright-test/test-tag.spec.ts b/playwright/tests/playwright-test/test-tag.spec.ts new file mode 100644 index 0000000000..e442c2acaa --- /dev/null +++ b/playwright/tests/playwright-test/test-tag.spec.ts @@ -0,0 +1,148 @@ +/** + * 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 { test, expect } from './playwright-test-fixtures'; + +test('should have correct tags', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'reporter.ts': ` + export default class Reporter { + onBegin(config, suite) { + const visit = suite => { + for (const test of suite.tests || []) + console.log('\\n%%title=' + test.title + ', tags=' + test.tags.join(',')); + for (const child of suite.suites || []) + visit(child); + }; + visit(suite); + } + onError(error) { + console.log(error); + } + } + `, + 'playwright.config.ts': ` + module.exports = { + reporter: './reporter', + }; + `, + 'stdio.spec.js': ` + import { test, expect } from '@playwright/test'; + test('no-tags', () => { + }); + test('foo-tag @inline', { tag: '@foo' }, () => { + }); + test('foo-bar-tags', { tag: ['@foo', '@bar'] }, () => { + }); + test.skip('skip-foo-tag', { tag: '@foo' }, () => { + }); + test.fixme('fixme-bar-tag', { tag: '@bar' }, () => { + }); + test.fail('fail-foo-bar-tags', { tag: ['@foo', '@bar'] }, () => { + expect(1).toBe(2); + }); + test.describe('suite @inline', { tag: '@foo' }, () => { + test('foo-suite', () => { + }); + test.describe('inner', { tag: '@bar' }, () => { + test('foo-bar-suite', () => { + }); + }); + }); + test.describe.skip('skip-foo-suite', { tag: '@foo' }, () => { + test('skip-foo-suite', () => { + }); + }); + test.describe.fixme('fixme-bar-suite', { tag: '@bar' }, () => { + test('fixme-bar-suite', () => { + }); + }); + ` + }); + expect(result.exitCode).toBe(0); + expect(result.outputLines).toEqual([ + `title=no-tags, tags=`, + `title=foo-tag @inline, tags=@inline,@foo`, + `title=foo-bar-tags, tags=@foo,@bar`, + `title=skip-foo-tag, tags=@foo`, + `title=fixme-bar-tag, tags=@bar`, + `title=fail-foo-bar-tags, tags=@foo,@bar`, + `title=foo-suite, tags=@inline,@foo`, + `title=foo-bar-suite, tags=@inline,@foo,@bar`, + `title=skip-foo-suite, tags=@foo`, + `title=fixme-bar-suite, tags=@bar`, + ]); +}); + +test('config.grep should work', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { grep: /@tag1/ }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('test1', { tag: '@tag1' }, async () => { console.log('\\n%% test1'); }); + test('test2', async () => { console.log('\\n%% test2'); }); + `, + }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + expect(result.outputLines).toEqual(['test1']); +}); + +test('config.project.grep should work', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { projects: [ + { name: 'p1' }, + { name: 'p2', grep: /@tag1/ } + ] }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('test1', { tag: '@tag1' }, async () => { console.log('\\n%% test1-' + test.info().project.name); }); + test('test2', async () => { console.log('\\n%% test2-' + test.info().project.name); }); + `, + }, { workers: 1 }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(3); + expect(result.outputLines).toEqual(['test1-p1', 'test2-p1', 'test1-p2']); +}); + +test('--grep should work', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('test1', { tag: '@tag1' }, async () => { console.log('\\n%% test1'); }); + test('test2', async () => { console.log('\\n%% test2'); }); + `, + }, { grep: '@tag1' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + expect(result.outputLines).toEqual(['test1']); +}); + +test('should enforce @ symbol', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'stdio.spec.js': ` + import { test, expect } from '@playwright/test'; + test('test1', { tag: 'foo' }, () => { + }); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.output).toContain(`Error: Tag must start with "@" symbol, got "foo" instead.`); +}); diff --git a/playwright/tests/playwright-test/to-have-screenshot.spec.ts b/playwright/tests/playwright-test/to-have-screenshot.spec.ts index 1a7774c840..95a95e4cb1 100644 --- a/playwright/tests/playwright-test/to-have-screenshot.spec.ts +++ b/playwright/tests/playwright-test/to-have-screenshot.spec.ts @@ -74,6 +74,56 @@ test('should disable animations by default', async ({ runInlineTest }, testInfo) expect(result.exitCode).toBe(0); }); +test('should not retry missing expectation errors', async ({ runInlineTest }, testInfo) => { + const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html')); + const result = await runInlineTest({ + ...playwrightConfig({ + retries: 2, + }), + 'a.spec.js': ` + const { test, expect } = require('@playwright/test'); + test('is a test', async ({ page }) => { + await page.goto('${cssTransitionURL}'); + await expect(page).toHaveScreenshot('foo.png', { timeout: 1000 }); + await expect(page).toHaveScreenshot('bar.png', { timeout: 1000 }); + }); + ` + }); + expect(result.output).not.toContain(`retry #`); + expect(result.output).toMatch(/A snapshot doesn't exist.*foo.*, writing actual./); + expect(result.output).toMatch(/A snapshot doesn't exist.*bar.*, writing actual./); + expect(result.exitCode).toBe(1); +}); + +test('should not retry serial mode suites with missing expectation errors', async ({ runInlineTest }, testInfo) => { + const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html')); + const result = await runInlineTest({ + ...playwrightConfig({ + retries: 2, + }), + 'a.spec.js': ` + const { test, expect } = require('@playwright/test'); + test.describe.serial('outer', () => { + test('last', async ({ page }) => { + }); + test.describe('nested', () => { + test('is a test', async ({ page }) => { + await page.goto('${cssTransitionURL}'); + await expect(page).toHaveScreenshot({ timeout: 1000 }); + await expect(page).toHaveScreenshot({ timeout: 1000 }); + }); + test('last', async ({ page }) => { + }); + }); + }); + ` + }); + expect(result.output).not.toContain(`retry #`); + expect(result.output).toMatch(/A snapshot doesn't exist.*1.*, writing actual./); + expect(result.output).toMatch(/A snapshot doesn't exist.*2.*, writing actual./); + expect(result.exitCode).toBe(1); +}); + test.describe('expect config animations option', () => { test('disabled', async ({ runInlineTest }, testInfo) => { const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html')); @@ -370,7 +420,7 @@ test('should fail to screenshot an element that keeps moving', async ({ runInlin }); expect(result.exitCode).toBe(1); expect(result.output).toContain(`Timeout 2000ms exceeded`); - expect(result.output).toContain(`element is not stable - waiting`); + expect(result.output).toContain(`element is not stable`); expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-actual.png'))).toBe(false); expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-expected.png'))).toBe(false); expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-diff.png'))).toBe(false); @@ -1222,6 +1272,7 @@ test('should support stylePath option', async ({ runInlineTest }) => { snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}', }), '__screenshots__/tests/a.spec.js/snapshot.png': createImage(IMG_WIDTH, IMG_HEIGHT, 0, 255, 0), + '__screenshots__/tests/a.spec.js/png-1.png': createImage(IMG_WIDTH, IMG_HEIGHT, 0, 255, 0), 'screenshot.css': 'body { background: #00FF00; }', 'tests/a.spec.js': ` const { test, expect } = require('@playwright/test'); @@ -1230,6 +1281,9 @@ test('should support stylePath option', async ({ runInlineTest }) => { await expect(page).toHaveScreenshot('snapshot.png', { stylePath: './screenshot.css', }); + await expect(page).toHaveScreenshot({ + stylePath: './screenshot.css', + }); }); `, }); diff --git a/playwright/tests/playwright-test/types-2.spec.ts b/playwright/tests/playwright-test/types-2.spec.ts index f463f21d60..e61d4870ed 100644 --- a/playwright/tests/playwright-test/types-2.spec.ts +++ b/playwright/tests/playwright-test/types-2.spec.ts @@ -32,6 +32,7 @@ test('basics should work', async ({ runTSC }) => { }); test.skip('my test', async () => {}); test.fixme('my test', async () => {}); + test.fail('my test', async () => {}); }); test.describe(() => { test('my test', () => {}); @@ -46,6 +47,25 @@ test('basics should work', async ({ runTSC }) => { test.foo(); test.describe.configure({ mode: 'parallel' }); test.describe.configure({ retries: 3, timeout: 123 }); + test('title', { tag: '@foo' }, () => {}); + test('title', { tag: ['@foo', '@bar'] }, () => {}); + test('title', { annotation: { type: 'issue' } }, () => {}); + test('title', { annotation: [{ type: 'issue' }, { type: 'foo', description: 'bar' }] }, () => {}); + test('title', { + tag: '@foo', + annotation: { type: 'issue' }, + }, () => {}); + test.skip('title', { tag: '@foo' }, () => {}); + test.fixme('title', { tag: '@foo' }, () => {}); + test.only('title', { tag: '@foo' }, () => {}); + test.fail('title', { tag: '@foo' }, () => {}); + test.describe('title', { tag: '@foo' }, () => {}); + test.describe('title', { annotation: { type: 'issue' } }, () => {}); + // @ts-expect-error + test.describe({ tag: '@foo' }, () => {}); + test.describe.skip('title', { tag: '@foo' }, () => {}); + test.describe.fixme('title', { tag: '@foo' }, () => {}); + test.describe.only('title', { tag: '@foo' }, () => {}); ` }); expect(result.exitCode).toBe(0); diff --git a/playwright/tests/playwright-test/ui-mode-test-ct.spec.ts b/playwright/tests/playwright-test/ui-mode-test-ct.spec.ts index 12c3cda430..c4cc2492a9 100644 --- a/playwright/tests/playwright-test/ui-mode-test-ct.spec.ts +++ b/playwright/tests/playwright-test/ui-mode-test-ct.spec.ts @@ -21,7 +21,11 @@ test.describe.configure({ mode: 'parallel', retries }); const basicTestTree = { 'playwright.config.ts': ` import { defineConfig } from '@playwright/experimental-ct-react'; - export default defineConfig({}); + export default defineConfig({ + use: { + ctPort: ${3200 + (+process.env.TEST_PARALLEL_INDEX)} + } + }); `, 'playwright/index.html': ``, 'playwright/index.ts': ``, diff --git a/playwright/tests/playwright-test/ui-mode-test-progress.spec.ts b/playwright/tests/playwright-test/ui-mode-test-progress.spec.ts index 38091dcd93..f87eaa8fbc 100644 --- a/playwright/tests/playwright-test/ui-mode-test-progress.spec.ts +++ b/playwright/tests/playwright-test/ui-mode-test-progress.spec.ts @@ -288,3 +288,63 @@ test('should show live trace for serial', async ({ runUITest, server, createLatc /expect.not.toBeCheckedlocator\('input'\)[\d.]/, ]); }); + +test('should show live trace from hooks', async ({ runUITest, createLatch }) => { + const latch1 = createLatch(); + const latch2 = createLatch(); + + const { page } = await runUITest({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test.beforeAll(async ({ browser }) => { + const page = await browser.newPage(); + ${latch1.blockingCode} + await page.close(); + }); + test.beforeEach(async ({ browser }) => { + const page = await browser.newPage(); + ${latch2.blockingCode} + await page.close(); + }); + test('test one', async ({ page }) => { + await page.setContent('Page content'); + }); + `, + }); + + await expect.poll(dumpTestTree(page)).toBe(` + ▼ ◯ a.test.ts + ◯ test one + `); + await page.getByText('test one').dblclick(); + + const listItem = page.getByTestId('actions-tree').getByRole('listitem'); + await expect( + listItem, + 'action list' + ).toHaveText([ + /Before Hooks/, + /beforeAll hook/, + /fixture: browser/, + /browser.newPage/, + ]); + latch1.open(); + await expect( + listItem, + 'action list' + ).toHaveText([ + /Before Hooks/, + /beforeAll hook/, + /beforeEach hook/, + /browser.newPage/, + ]); + latch2.open(); + await expect( + listItem, + 'action list' + ).toHaveText([ + /Before Hooks/, + /page.setContent/, + /After Hooks/, + ]); +}); diff --git a/playwright/tests/playwright-test/ui-mode-trace.spec.ts b/playwright/tests/playwright-test/ui-mode-trace.spec.ts index a10af7a1fa..215afe1d2b 100644 --- a/playwright/tests/playwright-test/ui-mode-trace.spec.ts +++ b/playwright/tests/playwright-test/ui-mode-trace.spec.ts @@ -258,3 +258,29 @@ test('should not show caught errors in the errors tab', async ({ runUITest }, te await page.getByText('Errors', { exact: true }).click(); await expect(page.locator('.tab-errors')).toHaveText('No errors'); }); + +test('should reveal errors in the sourcetab', async ({ runUITest }) => { + const { page } = await runUITest({ + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('pass', async ({ page }) => { + throw new Error('Oh my'); + }); + `, + }); + + await page.getByText('pass').dblclick(); + const listItem = page.getByTestId('actions-tree').getByRole('listitem'); + + await expect( + listItem, + 'action list' + ).toContainText([ + /Before Hooks/, + /After Hooks/, + ]); + + await page.getByText('Errors', { exact: true }).click(); + await page.getByText('a.spec.ts:4', { exact: true }).click(); + await expect(page.locator('.source-line-running')).toContainText(`throw new Error('Oh my');`); +}); diff --git a/playwright/tests/playwright-test/watch.spec.ts b/playwright/tests/playwright-test/watch.spec.ts index 5239d9fd97..c4b978819f 100644 --- a/playwright/tests/playwright-test/watch.spec.ts +++ b/playwright/tests/playwright-test/watch.spec.ts @@ -15,7 +15,7 @@ */ import path from 'path'; -import { test, expect } from './playwright-test-fixtures'; +import { test, expect, playwrightCtConfigText } from './playwright-test-fixtures'; test.describe.configure({ mode: 'parallel' }); @@ -563,10 +563,7 @@ test('should not watch unfiltered files', async ({ runWatchTest, writeFiles }) = test('should run CT on changed deps', async ({ runWatchTest, writeFiles }) => { const testProcess = await runWatchTest({ - 'playwright.config.ts': ` - import { defineConfig } from '@playwright/experimental-ct-react'; - export default defineConfig({ projects: [{name: 'default'}] }); - `, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/button.tsx': ` @@ -606,10 +603,7 @@ test('should run CT on changed deps', async ({ runWatchTest, writeFiles }) => { test('should run CT on indirect deps change', async ({ runWatchTest, writeFiles }) => { const testProcess = await runWatchTest({ - 'playwright.config.ts': ` - import { defineConfig } from '@playwright/experimental-ct-react'; - export default defineConfig({ projects: [{name: 'default'}] }); - `, + 'playwright.config.ts': playwrightCtConfigText, 'playwright/index.html': ``, 'playwright/index.ts': ``, 'src/button.css': ` @@ -652,10 +646,7 @@ test('should run CT on indirect deps change', async ({ runWatchTest, writeFiles test('should run CT on indirect deps change ESM mode', async ({ runWatchTest, writeFiles }) => { const testProcess = await runWatchTest({ - 'playwright.config.ts': ` - import { defineConfig } from '@playwright/experimental-ct-react'; - export default defineConfig({ projects: [{name: 'default'}] }); - `, + 'playwright.config.ts': playwrightCtConfigText, 'package.json': `{ "type": "module" }`, 'playwright/index.html': ``, 'playwright/index.ts': ``, diff --git a/playwright/tests/playwright-test/web-server.spec.ts b/playwright/tests/playwright-test/web-server.spec.ts index 5fac838dac..d2eacb2d55 100644 --- a/playwright/tests/playwright-test/web-server.spec.ts +++ b/playwright/tests/playwright-test/web-server.spec.ts @@ -438,7 +438,8 @@ test(`should support self signed certificate`, async ({ runInlineTest, httpsServ test('should send Accept header', async ({ runInlineTest, server }) => { let acceptHeader: string | undefined | null = null; server.setRoute('/hello', (req, res) => { - if (acceptHeader === null) acceptHeader = req.headers.accept; + if (acceptHeader === null) + acceptHeader = req.headers.accept; res.end('hello'); }); const result = await runInlineTest({ @@ -661,7 +662,7 @@ test('should check ipv4 and ipv6 with happy eyeballs when URL is passed', async expect(result.exitCode).toBe(0); expect(result.passed).toBe(1); expect(result.output).toContain('Process started'); - expect(result.output).toContain(`HTTP GET: http://localhost:${port}/`); + expect(result.output).toContain(`HTTP HEAD: http://localhost:${port}/`); expect(result.output).toContain('WebServer available'); }); diff --git a/playwright/tests/third_party/proxy/LICENSE b/playwright/tests/third_party/proxy/LICENSE new file mode 100644 index 0000000000..008728cb51 --- /dev/null +++ b/playwright/tests/third_party/proxy/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/playwright/tests/third_party/proxy/README.md b/playwright/tests/third_party/proxy/README.md new file mode 100644 index 0000000000..56e3febfab --- /dev/null +++ b/playwright/tests/third_party/proxy/README.md @@ -0,0 +1,4 @@ +This folder contains the [proxy](https://github.com/TooTallNate/proxy-agents) +library vendored at commit `c881a1804197b89580320b87082971c3c6a61746` with the following modifications: + +- https://github.com/TooTallNate/proxy-agents/pull/270 \ No newline at end of file diff --git a/playwright/tests/third_party/proxy/index.ts b/playwright/tests/third_party/proxy/index.ts new file mode 100644 index 0000000000..2fc842fa2e --- /dev/null +++ b/playwright/tests/third_party/proxy/index.ts @@ -0,0 +1,468 @@ +import assert from 'assert'; +import * as net from 'net'; +import * as url from 'url'; +import * as http from 'http'; +import * as os from 'os'; + +const pkg = { version: '1.0.0' } + +import createDebug from 'debug'; + +// log levels +const debug = { + request: createDebug('proxy ← ← ←'), + response: createDebug('proxy → → →'), + proxyRequest: createDebug('proxy ↑ ↑ ↑'), + proxyResponse: createDebug('proxy ↓ ↓ ↓'), +}; + +// hostname +const hostname = os.hostname(); + +export interface ProxyServer extends http.Server { + authenticate?: (req: http.IncomingMessage) => boolean | Promise; + localAddress?: string; +} + +/** + * Sets up an `http.Server` or `https.Server` instance with the necessary + * "request" and "connect" event listeners in order to make the server act + * as an HTTP proxy. + */ +export function createProxy(server?: http.Server): ProxyServer { + if (!server) server = http.createServer(); + server.on('request', onrequest); + server.on('connect', onconnect); + return server; +} + +/** + * 13.5.1 End-to-end and Hop-by-hop Headers + * + * Hop-by-hop headers must be removed by the proxy before passing it on to the + * next endpoint. Per-request basis hop-by-hop headers MUST be listed in a + * Connection header, (section 14.10) to be introduced into HTTP/1.1 (or later). + */ +const hopByHopHeaders = [ + 'Connection', + 'Keep-Alive', + 'Proxy-Authenticate', + 'Proxy-Authorization', + 'TE', + 'Trailers', + 'Transfer-Encoding', + 'Upgrade', +]; + +// create a case-insensitive RegExp to match "hop by hop" headers +const isHopByHop = new RegExp('^(' + hopByHopHeaders.join('|') + ')$', 'i'); + +/** + * Iterator function for the request/response's "headers". + */ +function* eachHeader(obj: http.IncomingMessage) { + // every even entry is a "key", every odd entry is a "value" + let key: string | null = null; + for (const v of obj.rawHeaders) { + if (key === null) { + key = v; + } else { + yield [key, v]; + key = null; + } + } +} + +/** + * HTTP GET/POST/DELETE/PUT, etc. proxy requests. + */ +async function onrequest( + this: ProxyServer, + req: http.IncomingMessage, + res: http.ServerResponse +) { + debug.request('%s %s HTTP/%s ', req.method, req.url, req.httpVersion); + const socket = req.socket; + + // pause the socket during authentication so no data is lost + socket.pause(); + + try { + const success = await authenticate(this, req); + if (!success) return requestAuthorization(req, res); + } catch (_err: unknown) { + const err = _err as Error; + // an error occured during login! + res.writeHead(500); + res.end((err.stack || err.message || err) + '\n'); + return; + } + + socket.resume(); + const parsed = url.parse(req.url || '/'); + + // setup outbound proxy request HTTP headers + const headers: http.OutgoingHttpHeaders = {}; + let hasXForwardedFor = false; + let hasVia = false; + const via = '1.1 ' + hostname + ' (proxy/' + pkg.version + ')'; + + for (const header of eachHeader(req)) { + debug.request('Request Header: %o', header); + const key = header[0]; + let value = header[1]; + const keyLower = key.toLowerCase(); + + if (!hasXForwardedFor && 'x-forwarded-for' === keyLower) { + // append to existing "X-Forwarded-For" header + // http://en.wikipedia.org/wiki/X-Forwarded-For + hasXForwardedFor = true; + if (typeof socket.remoteAddress === 'string') { + value += ', ' + socket.remoteAddress; + debug.proxyRequest( + 'appending to existing "%s" header: "%s"', + key, + value + ); + } + } + + if (!hasVia && 'via' === keyLower) { + // append to existing "Via" header + hasVia = true; + value += ', ' + via; + debug.proxyRequest( + 'appending to existing "%s" header: "%s"', + key, + value + ); + } + + if (isHopByHop.test(key)) { + debug.proxyRequest('ignoring hop-by-hop header "%s"', key); + } else { + const v = headers[key] as string; + if (Array.isArray(v)) { + v.push(value); + } else if (null != v) { + headers[key] = [v, value]; + } else { + headers[key] = value; + } + } + } + + // add "X-Forwarded-For" header if it's still not here by now + // http://en.wikipedia.org/wiki/X-Forwarded-For + if (!hasXForwardedFor && typeof socket.remoteAddress === 'string') { + headers['X-Forwarded-For'] = socket.remoteAddress; + debug.proxyRequest( + 'adding new "X-Forwarded-For" header: "%s"', + headers['X-Forwarded-For'] + ); + } + + // add "Via" header if still not set by now + if (!hasVia) { + headers.Via = via; + debug.proxyRequest('adding new "Via" header: "%s"', headers.Via); + } + + // custom `http.Agent` support, set `server.agent` + //let agent = server.agent; + //if (null != agent) { + // debug.proxyRequest( + // 'setting custom `http.Agent` option for proxy request: %s', + // agent + // ); + // parsed.agent = agent; + // agent = null; + //} + + //if (!parsed.port) { + // // default the port number if not specified, for >= node v0.11.6... + // // https://github.com/joyent/node/issues/6199 + // parsed.port = 80; + //} + + if (parsed.protocol !== 'http:') { + // only "http://" is supported, "https://" should use CONNECT method + res.writeHead(400); + res.end( + `Only "http:" protocol prefix is supported (got: "${parsed.protocol}")\n` + ); + return; + } + + let gotResponse = false; + const proxyReq = http.request({ + ...parsed, + method: req.method, + headers, + localAddress: this.localAddress, + }); + debug.proxyRequest('%s %s HTTP/1.1 ', proxyReq.method, proxyReq.path); + + proxyReq.on('response', function (proxyRes) { + debug.proxyResponse('HTTP/1.1 %s', proxyRes.statusCode); + gotResponse = true; + + const headers: http.OutgoingHttpHeaders = {}; + for (const [key, value] of eachHeader(proxyRes)) { + debug.proxyResponse('Proxy Response Header: "%s: %s"', key, value); + if (isHopByHop.test(key)) { + debug.response('ignoring hop-by-hop header "%s"', key); + } else { + const v = headers[key] as string; + if (Array.isArray(v)) { + v.push(value); + } else if (null != v) { + headers[key] = [v, value]; + } else { + headers[key] = value; + } + } + } + + debug.response('HTTP/1.1 %s', proxyRes.statusCode); + res.writeHead(proxyRes.statusCode || 200, headers); + proxyRes.pipe(res); + res.on('finish', onfinish); + }); + + proxyReq.on('error', function (err: NodeJS.ErrnoException) { + debug.proxyResponse( + 'proxy HTTP request "error" event\n%s', + err.stack || err + ); + cleanup(); + if (gotResponse) { + debug.response( + 'already sent a response, just destroying the socket...' + ); + socket.destroy(); + } else if ('ENOTFOUND' == err.code) { + debug.response('HTTP/1.1 404 Not Found'); + res.writeHead(404); + res.end(); + } else { + debug.response('HTTP/1.1 500 Internal Server Error'); + res.writeHead(500); + res.end(); + } + }); + + // if the client closes the connection prematurely, + // then close the upstream socket + function onclose() { + debug.request( + 'client socket "close" event, aborting HTTP request to "%s"', + req.url + ); + proxyReq.abort(); + cleanup(); + } + socket.on('close', onclose); + + function onfinish() { + debug.response('"finish" event'); + cleanup(); + } + + function cleanup() { + debug.response('cleanup'); + socket.removeListener('close', onclose); + res.removeListener('finish', onfinish); + } + + req.pipe(proxyReq); +} + +/** + * HTTP CONNECT proxy requests. + */ +async function onconnect( + this: ProxyServer, + req: http.IncomingMessage, + socket: net.Socket, + head: Buffer +) { + debug.request('%s %s HTTP/%s ', req.method, req.url, req.httpVersion); + assert( + !head || 0 == head.length, + '"head" should be empty for proxy requests' + ); + + let res: http.ServerResponse | null; + let gotResponse = false; + + // define request socket event listeners + socket.on('close', function onclientclose() { + debug.request('HTTP request %s socket "close" event', req.url); + }); + + socket.on('end', function onclientend() { + debug.request('HTTP request %s socket "end" event', req.url); + }); + + socket.on('error', function onclienterror(err) { + debug.request( + 'HTTP request %s socket "error" event:\n%s', + req.url, + err.stack || err + ); + }); + + // define target socket event listeners + function ontargetclose() { + debug.proxyResponse('proxy target %s "close" event', req.url); + socket.destroy(); + } + + function ontargetend() { + debug.proxyResponse('proxy target %s "end" event', req.url); + } + + function ontargeterror(err: NodeJS.ErrnoException) { + debug.proxyResponse( + 'proxy target %s "error" event:\n%s', + req.url, + err.stack || err + ); + if (gotResponse) { + debug.response( + 'already sent a response, just destroying the socket...' + ); + socket.destroy(); + } else if (err.code === 'ENOTFOUND') { + debug.response('HTTP/1.1 404 Not Found'); + if (res) { + res.writeHead(404); + res.end(); + } + } else { + debug.response('HTTP/1.1 500 Internal Server Error'); + if (res) { + res.writeHead(500); + res.end(); + } + } + } + + function ontargetconnect() { + debug.proxyResponse('proxy target %s "connect" event', req.url); + debug.response('HTTP/1.1 200 Connection established'); + gotResponse = true; + + if (res) { + res.removeListener('finish', onfinish); + + res.writeHead(200, 'Connection established'); + res.flushHeaders(); + + // relinquish control of the `socket` from the ServerResponse instance + res.detachSocket(socket); + + // nullify the ServerResponse object, so that it can be cleaned + // up before this socket proxying is completed + res = null; + } + + socket.on('end', () => target.destroy()); + socket.pipe(target); + target.pipe(socket); + } + + // create the `res` instance for this request since Node.js + // doesn't provide us with one :( + res = new http.ServerResponse(req); + res.shouldKeepAlive = false; + res.chunkedEncoding = false; + res.useChunkedEncodingByDefault = false; + res.assignSocket(socket); + + // called for the ServerResponse's "finish" event + // XXX: normally, node's "http" module has a "finish" event listener that would + // take care of closing the socket once the HTTP response has completed, but + // since we're making this ServerResponse instance manually, that event handler + // never gets hooked up, so we must manually close the socket... + function onfinish() { + debug.response('response "finish" event'); + if (res) { + res.detachSocket(socket); + } + socket.end(); + } + res.once('finish', onfinish); + + // pause the socket during authentication so no data is lost + socket.pause(); + + try { + const success = await authenticate(this, req); + if (!success) return requestAuthorization(req, res); + } catch (_err) { + const err = _err as Error; + // an error occured during login! + res.writeHead(500); + res.end((err.stack || err.message || err) + '\n'); + return; + } + + socket.resume(); + + if (!req.url) { + throw new TypeError('No "url" provided'); + } + + // `req.url` should look like "example.com:443" + const lastColon = req.url.lastIndexOf(':'); + const host = req.url.substring(0, lastColon); + const port = parseInt(req.url.substring(lastColon + 1), 10); + const localAddress = this.localAddress; + const opts = { host: host.replace(/^\[|\]$/g, ''), port, localAddress }; + + debug.proxyRequest('connecting to proxy target %o', opts); + const target = net.connect(opts); + target.on('connect', ontargetconnect); + target.on('close', ontargetclose); + target.on('error', ontargeterror); + target.on('end', ontargetend); +} + +/** + * Checks `Proxy-Authorization` request headers. Same logic applied to CONNECT + * requests as well as regular HTTP requests. + */ +async function authenticate(server: ProxyServer, req: http.IncomingMessage) { + if (typeof server.authenticate === 'function') { + debug.request('authenticating request "%s %s"', req.method, req.url); + return server.authenticate(req); + } + // no `server.authenticate()` function, so just allow the request + return true; +} + +/** + * Sends a "407 Proxy Authentication Required" HTTP response to the `socket`. + */ +function requestAuthorization( + req: http.IncomingMessage, + res: http.ServerResponse +) { + // request Basic proxy authorization + debug.response( + 'requesting proxy authorization for "%s %s"', + req.method, + req.url + ); + + // TODO: make "realm" and "type" (Basic) be configurable... + const realm = 'proxy'; + + const headers = { + 'Proxy-Authenticate': 'Basic realm="' + realm + '"', + }; + res.writeHead(407, headers); + res.end('Proxy authorization required'); +} \ No newline at end of file diff --git a/playwright/tsconfig.json b/playwright/tsconfig.json index 913e039570..56d9996996 100644 --- a/playwright/tsconfig.json +++ b/playwright/tsconfig.json @@ -29,16 +29,11 @@ "noEmit": true, "noImplicitOverride": true, "useUnknownInCatchVariables": false, + "skipLibCheck": true, }, "compileOnSave": true, "include": ["packages"], "exclude": [ "packages/*/lib", - "packages/playwright-ct-react", - "packages/playwright-ct-react17", - "packages/playwright-ct-solid", - "packages/playwright-ct-svelte", - "packages/playwright-ct-vue", - "packages/playwright-ct-vue2" ], } diff --git a/playwright/utils/avd_install.sh b/playwright/utils/avd_install.sh index d677689d55..6beb8bf43f 100755 --- a/playwright/utils/avd_install.sh +++ b/playwright/utils/avd_install.sh @@ -2,29 +2,29 @@ set -e -SDKDIR=$PWD/.android-sdk -export ANDROID_SDK_ROOT=${SDKDIR} -export ANDROID_HOME=${SDKDIR} -export ANDROID_AVD_HOME=${SDKDIR}/avd +if [[ -n "${ANDROID_HOME}" ]]; then + echo "ANDROID_HOME is already set. This script is only for a local installation of the Android SDK." + exit 1 +fi -mkdir ${SDKDIR} -mkdir ${SDKDIR}/cmdline-tools +export ANDROID_HOME="$PWD/.android-sdk" + +mkdir -p ${ANDROID_HOME}/cmdline-tools echo Downloading Android SDK... -cd ${SDKDIR}/cmdline-tools -COMMAND_LINE_TOOLS_ZIP=${SDKDIR}/commandlinetools.zip -# https://developer.android.com/studio#command-tools -curl https://dl.google.com/android/repository/commandlinetools-mac-8512546_latest.zip -o ${COMMAND_LINE_TOOLS_ZIP} -unzip ${COMMAND_LINE_TOOLS_ZIP} +cd ${ANDROID_HOME}/cmdline-tools +COMMAND_LINE_TOOLS_ZIP=${ANDROID_HOME}/commandlinetools.zip +# https://developer.android.com/studio +curl https://dl.google.com/android/repository/commandlinetools-mac-11076708_latest.zip -o ${COMMAND_LINE_TOOLS_ZIP} +unzip ${COMMAND_LINE_TOOLS_ZIP} rm ${COMMAND_LINE_TOOLS_ZIP} mv cmdline-tools latest -ln -s ${SDKDIR}/cmdline-tools/latest ${SDKDIR}/tools echo Installing emulator... -yes | ${ANDROID_HOME}/tools/bin/sdkmanager --install platform-tools emulator +yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install platform-tools emulator echo Installing platform SDK... -yes | ${ANDROID_HOME}/tools/bin/sdkmanager --install "platforms;android-33" +yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-33" echo Starting ADB... ${ANDROID_HOME}/platform-tools/adb devices diff --git a/playwright/utils/avd_recreate.sh b/playwright/utils/avd_recreate.sh index e16cf408f0..0877e76ed1 100755 --- a/playwright/utils/avd_recreate.sh +++ b/playwright/utils/avd_recreate.sh @@ -3,9 +3,7 @@ set -e if [[ -z "${ANDROID_HOME}" ]]; then - export SDKDIR=$PWD/.android-sdk - export ANDROID_HOME=${SDKDIR} - export ANDROID_SDK_ROOT=${SDKDIR} + export ANDROID_HOME="$PWD/.android-sdk" fi ANDROID_ARCH="x86_64" @@ -15,7 +13,7 @@ if [[ "$(uname -m)" == "arm64" ]]; then ANDROID_ARCH="arm64-v8a" fi -${ANDROID_HOME}/tools/bin/avdmanager delete avd --name android33 || true -echo "y" | ${ANDROID_HOME}/tools/bin/sdkmanager --install "system-images;android-33;google_apis;$ANDROID_ARCH" -echo "no" | ${ANDROID_HOME}/tools/bin/avdmanager create avd --force --name android33 --device "Nexus 5X" --package "system-images;android-33;google_apis;$ANDROID_ARCH" +${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager delete avd --name android33 || true +yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "system-images;android-33;google_apis;$ANDROID_ARCH" platform-tools emulator +echo "no" | ${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager create avd --force --name android33 --device "Nexus 5X" --package "system-images;android-33;google_apis;$ANDROID_ARCH" ${ANDROID_HOME}/emulator/emulator -list-avds diff --git a/playwright/utils/avd_start.sh b/playwright/utils/avd_start.sh index 85788bbeb5..67acb1c63e 100755 --- a/playwright/utils/avd_start.sh +++ b/playwright/utils/avd_start.sh @@ -3,9 +3,7 @@ set -e if [[ -z "${ANDROID_HOME}" ]]; then - SDKDIR=$PWD/.android-sdk - export ANDROID_HOME=${SDKDIR} - export ANDROID_SDK_ROOT=${SDKDIR} + export ANDROID_HOME="$PWD/.android-sdk" fi bash $PWD/utils/avd_stop.sh diff --git a/playwright/utils/avd_stop.sh b/playwright/utils/avd_stop.sh index 71110c1b8d..eabc557ac5 100755 --- a/playwright/utils/avd_stop.sh +++ b/playwright/utils/avd_stop.sh @@ -3,9 +3,7 @@ set -e if [[ -z "${ANDROID_HOME}" ]]; then - SDKDIR=$PWD/.android-sdk - export ANDROID_HOME=${SDKDIR} - export ANDROID_SDK_ROOT=${SDKDIR} + export ANDROID_HOME="$PWD/.android-sdk" fi echo "Killing previous emulators" diff --git a/playwright/utils/build/run-driver-posix.sh b/playwright/utils/build/run-driver-posix.sh index d00a15ba42..af4ed059c5 100755 --- a/playwright/utils/build/run-driver-posix.sh +++ b/playwright/utils/build/run-driver-posix.sh @@ -3,4 +3,4 @@ SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd -P)" if [ -z "$PLAYWRIGHT_NODEJS_PATH" ]; then PLAYWRIGHT_NODEJS_PATH="$SCRIPT_PATH/node" fi -"$PLAYWRIGHT_NODEJS_PATH" "$SCRIPT_PATH/package/lib/cli/cli.js" "$@" +"$PLAYWRIGHT_NODEJS_PATH" "$SCRIPT_PATH/package/cli.js" "$@" diff --git a/playwright/utils/build/run-driver-win.cmd b/playwright/utils/build/run-driver-win.cmd index 4ea002a667..69e98709a0 100755 --- a/playwright/utils/build/run-driver-win.cmd +++ b/playwright/utils/build/run-driver-win.cmd @@ -1,4 +1,4 @@ @echo off setlocal if not defined PLAYWRIGHT_NODEJS_PATH set PLAYWRIGHT_NODEJS_PATH=%~dp0node.exe -"%PLAYWRIGHT_NODEJS_PATH%" "%~dp0package\lib\cli\cli.js" %* \ No newline at end of file +"%PLAYWRIGHT_NODEJS_PATH%" "%~dp0package\cli.js" %* \ No newline at end of file diff --git a/playwright/utils/build/update_canary_version.js b/playwright/utils/build/update_canary_version.js index b9289c00d1..7a92860905 100755 --- a/playwright/utils/build/update_canary_version.js +++ b/playwright/utils/build/update_canary_version.js @@ -34,11 +34,8 @@ if (process.argv[2] === '--alpha') { let newVersion; if (process.argv[3] === '--today-date') { - const date = new Date(); - const month = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'][date.getMonth()]; - const day = date.getDate(); - const year = date.getFullYear(); - newVersion = `${baseVersion}-${prefix}-${month}-${day}-${year}`; + const isoDate = new Date().toISOString().split('T')[0]; + newVersion = `${baseVersion}-${prefix}-${isoDate}`; } else if (process.argv[3] === '--commit-timestamp') { const timestamp = execSync('git show -s --format=%ct HEAD', { stdio: ['ignore', 'pipe', 'ignore'] diff --git a/playwright/utils/docker/publish_docker.sh b/playwright/utils/docker/publish_docker.sh index dcb77e943e..8f2d1910fd 100755 --- a/playwright/utils/docker/publish_docker.sh +++ b/playwright/utils/docker/publish_docker.sh @@ -26,14 +26,8 @@ else exit 1 fi -if [[ -z "${GITHUB_SHA}" ]]; then - echo "ERROR: GITHUB_SHA env variable must be specified" - exit 1 -fi - FOCAL_TAGS=( "next" - "sha-${GITHUB_SHA}" "next-focal" "v${PW_VERSION}-focal" ) diff --git a/playwright/utils/doclint/documentation.js b/playwright/utils/doclint/documentation.js index de35df27ba..f0527c4120 100644 --- a/playwright/utils/doclint/documentation.js +++ b/playwright/utils/doclint/documentation.js @@ -566,7 +566,7 @@ class Type { return type; } - if (parsedType.args) { + if (parsedType.args || parsedType.retType) { const type = new Type('function'); type.args = []; // @ts-ignore @@ -737,7 +737,8 @@ function parseTypeExpression(type) { if (type[i] === '(') { name = type.substring(0, i); const matching = matchingBracket(type.substring(i), '(', ')'); - args = parseTypeExpression(type.substring(i + 1, i + matching - 1)); + const argsString = type.substring(i + 1, i + matching - 1); + args = argsString ? parseTypeExpression(argsString) : null; i = i + matching; if (type[i] === ':') { retType = parseTypeExpression(type.substring(i + 1)); diff --git a/playwright/utils/doclint/generateDotnetApi.js b/playwright/utils/doclint/generateDotnetApi.js index 368740feb2..ed801b4057 100644 --- a/playwright/utils/doclint/generateDotnetApi.js +++ b/playwright/utils/doclint/generateDotnetApi.js @@ -709,6 +709,9 @@ function translateType(type, parent, generateNameCallback = t => t.name, optiona if (type.expression === '[null]|[Error]') return 'void'; + if (type.name == 'Promise' && type.templates?.[0].name === 'any') + return 'Task'; + if (type.union) { if (type.union[0].name === 'null' && type.union.length === 2) return translateType(type.union[1], parent, generateNameCallback, true, isReturnType); @@ -799,6 +802,8 @@ function translateType(type, parent, generateNameCallback = t => t.name, optiona if (returnType === null) throw new Error('Unexpected null as return type.'); + if (!argsList) + return `Func<${returnType}>`; return `Func<${argsList}, ${returnType}>`; } } diff --git a/playwright/utils/doclint/linting-code-snippets/cli.js b/playwright/utils/doclint/linting-code-snippets/cli.js index 42e78fdcab..8bde076b7c 100644 --- a/playwright/utils/doclint/linting-code-snippets/cli.js +++ b/playwright/utils/doclint/linting-code-snippets/cli.js @@ -180,7 +180,10 @@ class JSLintingService extends LintingService { * @returns {Promise} */ async lint(snippets) { - return Promise.all(snippets.map(async snippet => this._lintSnippet(snippet))); + const result = []; + for (let i = 0; i < snippets.length; ++i) + result.push(await this._lintSnippet(snippets[i])); + return result; } } diff --git a/playwright/utils/generate_injected.js b/playwright/utils/generate_injected.js index c07d9a4877..eccd7ddcad 100644 --- a/playwright/utils/generate_injected.js +++ b/playwright/utils/generate_injected.js @@ -45,7 +45,7 @@ const injectedScripts = [ true, ], [ - path.join(ROOT, 'packages', 'playwright-core', 'src', 'server', 'injected', 'recorder.ts'), + path.join(ROOT, 'packages', 'playwright-core', 'src', 'server', 'injected', 'recorder', 'recorder.ts'), path.join(ROOT, 'packages', 'playwright-core', 'lib', 'server', 'injected', 'packed'), path.join(ROOT, 'packages', 'playwright-core', 'src', 'generated'), true, diff --git a/playwright/utils/generate_types/index.js b/playwright/utils/generate_types/index.js index 263ef1fb8d..e16f6df772 100644 --- a/playwright/utils/generate_types/index.js +++ b/playwright/utils/generate_types/index.js @@ -98,7 +98,12 @@ class TypesGenerator { }, (className, methodName, overloadIndex) => { if (className === 'SuiteFunction' && methodName === '__call') { const cls = this.documentation.classes.get('Test'); - const method = cls.membersArray.find(m => m.alias === 'describe' && m.overloadIndex === overloadIndex); + const method = cls.membersArray.find(m => m.alias === 'describe'); + return this.memberJSDOC(method, ' ').trimLeft(); + } + if (className === 'TestFunction' && methodName === '__call') { + const cls = this.documentation.classes.get('Test'); + const method = cls.membersArray.find(m => m.alias === '(call)'); return this.memberJSDOC(method, ' ').trimLeft(); } @@ -613,12 +618,10 @@ class TypesGenerator { const existing = fs.readFileSync(filePath, 'utf8'); if (existing === content) return; - hadChanges = true; console.error(`Writing //${path.relative(PROJECT_DIR, filePath)}`); fs.writeFileSync(filePath, content, 'utf8'); } - let hadChanges = false; const coreTypesDir = path.join(PROJECT_DIR, 'packages', 'playwright-core', 'types'); if (!fs.existsSync(coreTypesDir)) fs.mkdirSync(coreTypesDir) @@ -629,7 +632,7 @@ class TypesGenerator { writeFile(path.join(coreTypesDir, 'types.d.ts'), await generateCoreTypes(false), true); writeFile(path.join(playwrightTypesDir, 'test.d.ts'), await generateTestTypes(false), true); writeFile(path.join(playwrightTypesDir, 'testReporter.d.ts'), await generateReporterTypes(false), true); - process.exit(hadChanges && process.argv.includes('--check-clean') ? 1 : 0); + process.exit(0); })().catch(e => { console.error(e); process.exit(1); diff --git a/playwright/utils/generate_types/overrides-test.d.ts b/playwright/utils/generate_types/overrides-test.d.ts index 5f29548676..fd88e84dca 100644 --- a/playwright/utils/generate_types/overrides-test.d.ts +++ b/playwright/utils/generate_types/overrides-test.d.ts @@ -111,13 +111,25 @@ export interface TestInfo { project: FullProject; } +type TestDetailsAnnotation = { + type: string; + description?: string; +}; + +export type TestDetails = { + tag?: string | string[]; + annotation?: TestDetailsAnnotation | TestDetailsAnnotation[]; +} + interface SuiteFunction { (title: string, callback: () => void): void; (callback: () => void): void; + (title: string, details: TestDetails, callback: () => void): void; } interface TestFunction { - (title: string, testFunction: (args: TestArgs, testInfo: TestInfo) => Promise | void): void; + (title: string, body: (args: TestArgs, testInfo: TestInfo) => Promise | void): void; + (title: string, details: TestDetails, body: (args: TestArgs, testInfo: TestInfo) => Promise | void): void; } export interface TestType extends TestFunction { @@ -134,17 +146,21 @@ export interface TestType void; }; - skip(title: string, testFunction: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | void): void; + skip(title: string, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | void): void; + skip(title: string, details: TestDetails, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | void): void; skip(): void; skip(condition: boolean, description?: string): void; skip(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; - fixme(title: string, testFunction: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | void): void; + fixme(title: string, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | void): void; + fixme(title: string, details: TestDetails, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | void): void; fixme(): void; fixme(condition: boolean, description?: string): void; fixme(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; - fail(): void; + fail(title: string, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | void): void; + fail(title: string, details: TestDetails, body: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | void): void; fail(condition: boolean, description?: string): void; fail(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; + fail(): void; slow(): void; slow(condition: boolean, description?: string): void; slow(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; diff --git a/playwright/utils/generate_types/test/test.ts b/playwright/utils/generate_types/test/test.ts index 0bffaab281..05ede91adf 100644 --- a/playwright/utils/generate_types/test/test.ts +++ b/playwright/utils/generate_types/test/test.ts @@ -154,6 +154,12 @@ playwright.chromium.launch().then(async browser => { return 'something random for no reason'; }); + await page.addLocatorHandler(page.locator(''), () => {}); + await page.addLocatorHandler(page.locator(''), () => 42); + await page.addLocatorHandler(page.locator(''), async () => { }); + await page.addLocatorHandler(page.locator(''), async () => 42); + await page.addLocatorHandler(page.locator(''), () => Promise.resolve(42)); + await page.keyboard.type('Hello'); // Types instantly await page.keyboard.type('World', { delay: 100 }); // Types slower, like a user diff --git a/playwright/utils/pack_package.js b/playwright/utils/pack_package.js index 5989b49867..8ef6f4cee7 100644 --- a/playwright/utils/pack_package.js +++ b/playwright/utils/pack_package.js @@ -19,6 +19,7 @@ const os = require('os'); const path = require('path'); const { spawnSync } = require('child_process'); +const SCRIPT_NAME = path.basename(__filename); // 1. Parse CLI arguments const args = process.argv.slice(2); if (args.some(arg => arg === '--help')) { diff --git a/playwright/utils/workspace.js b/playwright/utils/workspace.js index a63fb79ce1..f1e2aaa4d9 100755 --- a/playwright/utils/workspace.js +++ b/playwright/utils/workspace.js @@ -22,6 +22,7 @@ */ const fs = require('fs'); const path = require('path'); +const child_process = require('child_process'); const readJSON = async (filePath) => JSON.parse(await fs.promises.readFile(filePath, 'utf8')); const writeJSON = async (filePath, json) => { @@ -33,7 +34,6 @@ class PWPackage { this.name = descriptor.name; this.path = descriptor.path; this.files = descriptor.files; - this.noConsistent = descriptor.noConsistent; this.packageJSONPath = path.join(this.path, 'package.json'); this.packageJSON = JSON.parse(fs.readFileSync(this.packageJSONPath, 'utf8')); this.isPrivate = !!this.packageJSON.private; @@ -107,13 +107,8 @@ class Workspace { await fs.promises.copyFile(fromPath, toPath); } - // 2. Make sure package-lock and package's package.json are consistent. - // All manual package-lock management is a workaround for - // https://github.com/npm/cli/issues/3940 - const pkgLockEntry = packageLock['packages']['packages/' + path.basename(pkg.path)]; - const depLockEntry = packageLock['dependencies'][pkg.name]; + // 2. Make sure package's package.jsons are consistent. if (!pkg.isPrivate) { - pkgLockEntry.version = version; pkg.packageJSON.version = version; pkg.packageJSON.repository = workspacePackageJSON.repository; pkg.packageJSON.engines = workspacePackageJSON.engines; @@ -122,16 +117,7 @@ class Workspace { pkg.packageJSON.license = workspacePackageJSON.license; } - if (pkg.noConsistent) - continue; - for (const otherPackage of this._packages) { - if (pkgLockEntry.dependencies && pkgLockEntry.dependencies[otherPackage.name]) - pkgLockEntry.dependencies[otherPackage.name] = version; - if (pkgLockEntry.devDependencies && pkgLockEntry.devDependencies[otherPackage.name]) - pkgLockEntry.devDependencies[otherPackage.name] = version; - if (depLockEntry.requires && depLockEntry.requires[otherPackage.name]) - depLockEntry.requires[otherPackage.name] = version; if (pkg.packageJSON.dependencies && pkg.packageJSON.dependencies[otherPackage.name]) pkg.packageJSON.dependencies[otherPackage.name] = version; if (pkg.packageJSON.devDependencies && pkg.packageJSON.devDependencies[otherPackage.name]) @@ -139,7 +125,9 @@ class Workspace { } await maybeWriteJSON(pkg.packageJSONPath, pkg.packageJSON); } - await maybeWriteJSON(packageLockPath, packageLock); + + // Re-run npm i to make package-lock dirty. + child_process.execSync('npm i'); return hasChanges; } } @@ -248,6 +236,10 @@ async function parseCLI() { const hasChanges = await workspace.ensureConsistent(); if (hasChanges) die(`\n ERROR: workspace is inconsistent! Run '//utils/workspace.js --ensure-consistent' and commit changes!`); + // Ensure lockfileVersion is 3 + const packageLock = require(ROOT_PATH + '/package-lock.json'); + if (packageLock.lockfileVersion !== 3) + die(`\n ERROR: package-lock.json lockfileVersion must be 3`); }, '--list-public-package-paths': () => { for (const pkg of workspace.packages()) {