Merge branch 'main' into lbo-relative-hosting

Signed-off-by: Lukas Bockstaller <lukas.bockstaller@everest-erp.com>
This commit is contained in:
Lukas Bockstaller 2024-05-07 14:47:59 +02:00 committed by GitHub
commit 966d4661ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
579 changed files with 18408 additions and 11827 deletions

View file

@ -7,8 +7,6 @@ test/assets/modernizr.js
/packages/playwright-core/types/*
/packages/playwright-ct-core/src/generated/*
/index.d.ts
utils/generate_types/overrides.d.ts
utils/generate_types/test/test.ts
node_modules/
browser_patches/*/checkout/
browser_patches/chromium/output/

View file

@ -4,12 +4,10 @@ inputs:
namePrefix:
description: 'Name prefix of the artifacts to download'
required: true
type: string
default: 'blob-report'
path:
description: 'Directory with downloaded artifacts'
required: true
type: string
default: '.'
runs:
using: "composite"
@ -36,7 +34,7 @@ runs:
artifact_id: artifact.id,
archive_format: 'zip'
});
console.log('downloaded artifact', result);
console.log(`Downloaded ${artifact.name}.zip (${result.data.byteLength} bytes)`);
fs.writeFileSync(`${{ inputs.path }}/artifacts/${artifact.name}.zip`, Buffer.from(result.data));
}
- name: Unzip artifacts

View file

@ -1,29 +0,0 @@
name: 'Download blob report from Azure'
description: 'Download blob report from Azure blob storage'
inputs:
blob_prefix:
description: 'Name of the Azure blob storage directory containing blob report'
required: true
type: string
output_dir:
description: 'Output directory where downloaded blobs will be stored'
required: true
type: string
default: 'blob-report'
connection_string:
description: 'Azure connection string'
required: true
type: string
runs:
using: "composite"
steps:
- name: Download Blob Reports from Azure Blob Storage
shell: bash
run: |
OUTPUT_DIR='${{ inputs.output_dir }}'
mkdir -p $OUTPUT_DIR
LIST=$(az storage blob list -c '$web' --prefix ${{ inputs.blob_prefix }} --connection-string "${{ inputs.connection_string }}")
for name in $(echo $LIST | jq --raw-output '.[].name | select(test("report-.*\\.zip$"))');
do
az storage blob download -c '$web' --name $name -f $OUTPUT_DIR/$(basename $name) --connection-string "${{ inputs.connection_string }}"
done

87
.github/actions/run-test/action.yml vendored Normal file
View file

@ -0,0 +1,87 @@
name: 'Run browser tests'
description: 'Run browser tests'
inputs:
command:
description: 'Command to run tests'
required: true
node-version:
description: 'Node.js version to use'
required: false
default: '18'
browsers-to-install:
description: 'Browser to install. Default is all browsers.'
required: false
default: ''
bot-name:
description: 'Bot name'
required: true
shell:
description: 'Shell to use'
required: false
default: 'bash'
flakiness-client-id:
description: 'Azure Flakiness Dashboard Client ID'
required: false
flakiness-tenant-id:
description: 'Azure Flakiness Dashboard Tenant ID'
required: false
flakiness-subscription-id:
description: 'Azure Flakiness Dashboard Subscription ID'
required: false
runs:
using: composite
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
# https://github.com/actions/runner-images/issues/9330
- name: Allow microphone access to all apps (macOS 14)
shell: bash
run: |
if [[ "$(uname)" == "Darwin" && "$(sw_vers -productVersion | cut -d. -f1)" == "14" ]]; then
echo "Allowing microphone access to all apps"
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159,NULL,NULL,'UNUSED',1687786159);"
fi
- run: npm ci
shell: bash
env:
DEBUG: pw:install
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
- run: npm run build
shell: bash
- run: npx playwright install --with-deps ${{ inputs.browsers-to-install }}
shell: bash
- name: Run tests
if: inputs.shell == 'bash'
run: |
if [[ "$(uname)" == "Linux" ]]; then
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- ${{ inputs.command }}
else
${{ inputs.command }}
fi
shell: bash
env:
PWTEST_BOT_NAME: ${{ inputs.bot-name }}
- name: Run tests
if: inputs.shell != 'bash'
run: ${{ inputs.command }}
shell: ${{ inputs.shell }}
env:
PWTEST_BOT_NAME: ${{ inputs.bot-name }}
- name: Azure Login
uses: azure/login@v2
if: ${{ !cancelled() && github.event_name == 'push' && github.repository == 'microsoft/playwright' }}
with:
client-id: ${{ inputs.flakiness-client-id }}
tenant-id: ${{ inputs.flakiness-tenant-id }}
subscription-id: ${{ inputs.flakiness-subscription-id }}
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: ${{ !cancelled() }}
shell: bash
- name: Upload blob report
if: ${{ !cancelled() }}
uses: ./.github/actions/upload-blob-report
with:
report_dir: blob-report
job_name: ${{ inputs.bot-name }}

View file

@ -4,29 +4,30 @@ inputs:
report_dir:
description: 'Directory containing blob report'
required: true
type: string
default: 'test-results/blob-report'
job_name:
description: 'Unique job name'
required: true
type: string
default: ''
runs:
using: "composite"
steps:
- name: Integrity check
shell: bash
run: find "${{ inputs.report_dir }}" -name "*.zip" -exec unzip -t {} \;
- name: Upload blob report to GitHub
if: always() && github.event_name == 'pull_request'
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ inputs.job_name }}
path: ${{ inputs.report_dir }}/**
retention-days: 7
- name: Write triggering pull request number in a file
if: always() && github.event_name == 'pull_request'
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
shell: bash
run: echo '${{ github.event.number }}' > pull_request_number.txt;
- name: Upload artifact with the pull request number
if: always() && github.event_name == 'pull_request'
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v4
with:
name: pull-request-${{ inputs.job_name }}

View file

@ -1,7 +0,0 @@
{
"name": "playwright-chromium",
"version": "1.0.0",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"license": "Apache-2.0"
}

View file

@ -1,7 +0,0 @@
{
"name": "playwright-core",
"version": "1.0.0",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"license": "Apache-2.0"
}

View file

@ -1,7 +0,0 @@
{
"name": "playwright-firefox",
"version": "1.0.0",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"license": "Apache-2.0"
}

View file

@ -1,7 +0,0 @@
{
"name": "@playwright/test",
"version": "1.0.0",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"license": "Apache-2.0"
}

View file

@ -1,7 +0,0 @@
{
"name": "playwright-webkit",
"version": "1.0.0",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"license": "Apache-2.0"
}

View file

@ -1,7 +0,0 @@
{
"name": "playwright",
"version": "1.0.0",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"license": "Apache-2.0"
}

View file

@ -9,6 +9,8 @@ jobs:
permissions:
pull-requests: write
checks: write
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
if: ${{ github.event.workflow_run.event == 'pull_request' }}
runs-on: ubuntu-latest
steps:
@ -16,7 +18,6 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci
env:
DEBUG: pw:install
@ -35,16 +36,20 @@ jobs:
env:
NODE_OPTIONS: --max-old-space-size=4096
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_BLOB_REPORTS_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_BLOB_REPORTS_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_BLOB_REPORTS_SUBSCRIPTION_ID }}
- name: Upload HTML report to Azure
run: |
REPORT_DIR='run-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}-${{ github.sha }}'
azcopy cp --recursive "./playwright-report/*" "https://mspwblobreport.blob.core.windows.net/\$web/$REPORT_DIR"
echo "Report url: https://mspwblobreport.z1.web.core.windows.net/$REPORT_DIR/index.html"
env:
AZCOPY_AUTO_LOGIN_TYPE: SPN
AZCOPY_SPA_APPLICATION_ID: '${{ secrets.AZCOPY_SPA_APPLICATION_ID }}'
AZCOPY_SPA_CLIENT_SECRET: '${{ secrets.AZCOPY_SPA_CLIENT_SECRET }}'
AZCOPY_TENANT_ID: '${{ secrets.AZCOPY_TENANT_ID }}'
AZCOPY_AUTO_LOGIN_TYPE: AZCLI
- name: Read pull request number
uses: ./.github/actions/download-artifact

View file

@ -17,8 +17,9 @@ jobs:
runs-on: ubuntu-20.04
if: github.repository == 'microsoft/playwright'
permissions:
contents: read
id-token: write
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
environment: allow-publish-driver-to-cdn # This is required for OIDC login (azure/login)
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
@ -49,11 +50,15 @@ jobs:
utils/publish_all_packages.sh --beta
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_PW_CDN_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_PW_CDN_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_PW_CDN_SUBSCRIPTION_ID }}
- name: build & publish driver
env:
AZ_UPLOAD_FOLDER: driver/next
AZ_ACCOUNT_KEY: ${{ secrets.AZ_ACCOUNT_KEY }}
AZ_ACCOUNT_NAME: ${{ secrets.AZ_ACCOUNT_NAME }}
run: |
utils/build/build-playwright-driver.sh
utils/build/upload-playwright-driver.sh

View file

@ -18,18 +18,17 @@ jobs:
publish-docker-release:
name: "publish to DockerHub"
runs-on: ubuntu-22.04
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
if: github.repository == 'microsoft/playwright'
environment: allow-publishing-docker-to-acr
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
- 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@v3
with:
@ -37,6 +36,14 @@ jobs:
- run: npm ci
- run: npm run build
- run: npx playwright install-deps
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_DOCKER_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_DOCKER_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_DOCKER_SUBSCRIPTION_ID }}
- name: Login to ACR via OIDC
run: az acr login --name playwright
- run: ./utils/docker/publish_docker.sh stable
if: (github.event_name != 'workflow_dispatch' && !github.event.release.prerelease) || (github.event_name == 'workflow_dispatch' && github.event.inputs.is_release == 'true')
- run: ./utils/docker/publish_docker.sh canary

View file

@ -12,6 +12,10 @@ jobs:
name: "publish playwright driver to CDN"
runs-on: ubuntu-20.04
if: github.repository == 'microsoft/playwright'
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
environment: allow-publish-driver-to-cdn # This is required for OIDC login (azure/login)
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
@ -22,8 +26,12 @@ jobs:
- run: npm run build
- run: npx playwright install-deps
- run: utils/build/build-playwright-driver.sh
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_PW_CDN_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_PW_CDN_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_PW_CDN_SUBSCRIPTION_ID }}
- run: utils/build/upload-playwright-driver.sh
env:
AZ_UPLOAD_FOLDER: driver
AZ_ACCOUNT_KEY: ${{ secrets.AZ_ACCOUNT_KEY }}
AZ_ACCOUNT_NAME: ${{ secrets.AZ_ACCOUNT_NAME }}

View file

@ -25,10 +25,10 @@ jobs:
- run: npm run build
- run: npx playwright install-deps
- run: utils/publish_all_packages.sh --release-candidate
if: "github.event.release.prerelease"
if: ${{ github.event.release.prerelease }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: utils/publish_all_packages.sh --release
if: "!github.event.release.prerelease"
if: ${{ !github.event.release.prerelease }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View file

@ -9,6 +9,8 @@ jobs:
name: Trigger Roll
runs-on: ubuntu-22.04
if: github.repository == 'microsoft/playwright'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4

View file

@ -26,10 +26,10 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [18]
include:
- os: ubuntu-latest
node-version: 16
- os: ubuntu-latest
node-version: 20
- os: ubuntu-latest
node-version: 22
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4

View file

@ -17,15 +17,18 @@ on:
env:
# Force terminal colors. @see https://www.npmjs.com/package/colors
FORCE_COLOR: 1
FLAKINESS_CONNECTION_STRING: ${{ secrets.FLAKINESS_CONNECTION_STRING }}
jobs:
test_electron:
name: ${{ matrix.os }}
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@ -41,8 +44,13 @@ jobs:
if: matrix.os == 'ubuntu-latest'
- run: npm run etest
if: matrix.os != 'ubuntu-latest'
- run: node tests/config/checkCoverage.js electron
if: always() && matrix.os == 'ubuntu-latest'
- name: Azure Login
uses: azure/login@v2
if: ${{ !cancelled() && github.event_name == 'push' && github.repository == 'microsoft/playwright' }}
with:
client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: always()
if: ${{ !cancelled() }}
shell: bash

View file

@ -22,12 +22,12 @@ concurrency:
env:
# Force terminal colors. @see https://www.npmjs.com/package/colors
FORCE_COLOR: 1
FLAKINESS_CONNECTION_STRING: ${{ secrets.FLAKINESS_CONNECTION_STRING }}
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
jobs:
test_linux:
name: ${{ matrix.os }} (${{ matrix.browser }} - Node.js ${{ matrix.node-version }})
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
strategy:
fail-fast: false
matrix:
@ -35,74 +35,55 @@ jobs:
os: [ubuntu-22.04]
node-version: [18]
include:
- os: ubuntu-22.04
node-version: 16
browser: chromium
- os: ubuntu-22.04
node-version: 20
browser: chromium
- os: ubuntu-22.04
node-version: 22
browser: chromium
runs-on: ${{ matrix.os }}
env:
PWTEST_BOT_NAME: "${{ matrix.browser }}-${{ matrix.os }}-node${{ matrix.node-version }}"
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: ./.github/actions/run-test
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
env:
DEBUG: pw:install
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build
- run: npx playwright install --with-deps ${{ matrix.browser }} chromium
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test -- --project=${{ matrix.browser }}-*
- run: node tests/config/checkCoverage.js ${{ matrix.browser }}
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: always()
shell: bash
- name: Upload blob report
if: always()
uses: ./.github/actions/upload-blob-report
with:
report_dir: blob-report
job_name: ${{ env.PWTEST_BOT_NAME }}
browsers-to-install: ${{ matrix.browser }} chromium
command: npm run test -- --project=${{ matrix.browser }}-*
bot-name: "${{ matrix.browser }}-${{ matrix.os }}-node${{ matrix.node-version }}"
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
test_linux_chromium_tot:
name: ${{ matrix.os }} (chromium tip-of-tree)
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04]
runs-on: ${{ matrix.os }}
env:
PWTEST_BOT_NAME: "${{ matrix.os }}-chromium-tip-of-tree"
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: ./.github/actions/run-test
with:
node-version: 18
- run: npm ci
env:
DEBUG: pw:install
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build
- run: npx playwright install --with-deps chromium-tip-of-tree
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test -- --project=chromium-*
browsers-to-install: chromium-tip-of-tree
command: npm run test -- --project=chromium-*
bot-name: "${{ matrix.os }}-chromium-tip-of-tree"
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
env:
PWTEST_CHANNEL: chromium-tip-of-tree
PWTEST_BOT_NAME: "${{ matrix.os }}-chromium-tip-of-tree"
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: always()
shell: bash
- name: Upload blob report
if: always()
uses: ./.github/actions/upload-blob-report
with:
report_dir: blob-report
job_name: ${{ env.PWTEST_BOT_NAME }}
test_test_runner:
name: Test Runner
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
strategy:
fail-fast: false
matrix:
@ -112,47 +93,37 @@ jobs:
shardTotal: [2]
include:
- os: ubuntu-latest
node-version: 16
node-version: 20
shardIndex: 1
shardTotal: 2
- os: ubuntu-latest
node-version: 16
node-version: 20
shardIndex: 2
shardTotal: 2
- os: ubuntu-latest
node-version: 20
node-version: 22
shardIndex: 1
shardTotal: 2
- os: ubuntu-latest
node-version: 20
node-version: 22
shardIndex: 2
shardTotal: 2
runs-on: ${{ matrix.os }}
env:
PWTEST_BOT_NAME: "${{ matrix.os }}-node${{ matrix.node-version }}-${{ matrix.shardIndex }}"
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: ./.github/actions/run-test
with:
node-version: ${{matrix.node-version}}
- run: npm ci
command: npm run ttest -- --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
bot-name: "${{ matrix.os }}-node${{ matrix.node-version }}-${{ matrix.shardIndex }}"
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
env:
DEBUG: pw:install
- run: npm run build
- run: npx playwright install --with-deps
- run: npm run ttest -- --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
if: matrix.os != 'ubuntu-latest'
- run: xvfb-run npm run ttest -- --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
if: matrix.os == 'ubuntu-latest'
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: always()
shell: bash
- name: Upload blob report
if: always()
uses: ./.github/actions/upload-blob-report
with:
report_dir: blob-report
job_name: ${{ env.PWTEST_BOT_NAME }}
PWTEST_CHANNEL: firefox-beta
test_web_components:
name: Web Components
@ -172,18 +143,18 @@ jobs:
env:
PWTEST_BOT_NAME: "web-components-html-reporter"
- name: Upload blob report
if: always()
if: ${{ !cancelled() }}
uses: ./.github/actions/upload-blob-report
with:
report_dir: packages/html-reporter/blob-report
job_name: "web-components-html-reporter"
- run: npm run test-web
if: always()
if: ${{ !cancelled() }}
env:
PWTEST_BOT_NAME: "web-components-web"
- name: Upload blob report
if: always()
if: ${{ !cancelled() }}
uses: ./.github/actions/upload-blob-report
with:
report_dir: packages/web/blob-report
@ -219,7 +190,7 @@ jobs:
run: npm run test -- --workers=1
working-directory: ./playwright-vscode
- name: Upload blob report
if: always()
if: ${{ !cancelled() }}
uses: ./.github/actions/upload-blob-report
with:
report_dir: playwright-vscode/blob-report
@ -227,6 +198,7 @@ jobs:
test_package_installations:
name: "Installation Test ${{ matrix.os }}"
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
strategy:
fail-fast: false
matrix:
@ -236,30 +208,19 @@ jobs:
- windows-latest
runs-on: ${{ matrix.os }}
timeout-minutes: 30
env:
PWTEST_BOT_NAME: "package-installations-${{ matrix.os }}"
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci
env:
DEBUG: pw:install
- run: npm run build
- run: npx playwright install --with-deps
- run: npm install -g yarn@1
- run: npm install -g pnpm@8
- run: npm run itest
if: matrix.os != 'ubuntu-latest'
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run itest
if: matrix.os == 'ubuntu-latest'
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: always()
shell: bash
- name: Upload blob report
if: always()
uses: ./.github/actions/upload-blob-report
- uses: ./.github/actions/run-test
with:
report_dir: blob-report
job_name: ${{ env.PWTEST_BOT_NAME }}
command: npm run itest
bot-name: "package-installations-${{ matrix.os }}"
# TODO: figure out why itest fails with 'bash' on Windows.
shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash' }}
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,7 @@ jobs:
PLAYWRIGHT_SERVICE_OS: ${{ matrix.service-os }}
PLAYWRIGHT_SERVICE_RUN_ID: ${{ github.run_id }}-${{ github.run_attempt }}-${{ github.sha }}
- name: Upload blob report to GitHub
if: always()
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v3
with:
name: all-blob-reports
@ -43,7 +43,7 @@ jobs:
merge_reports:
name: "Merge reports"
needs: [test]
if: always()
if: ${{ !cancelled() }}
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

View file

@ -30,29 +30,27 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version: 20
- run: npm ci
- run: npm run build
- run: npx playwright install --with-deps
- run: npx playwright install firefox-asan
if: matrix.os != 'windows-latest'
- run: npm run stest contexts -- --project=chromium
if: always()
if: ${{ !cancelled() }}
- run: npm run stest browsers -- --project=chromium
if: always()
if: ${{ !cancelled() }}
- run: npm run stest frames -- --project=chromium
if: always()
if: ${{ !cancelled() }}
- run: npm run stest contexts -- --project=webkit
if: always()
if: ${{ !cancelled() }}
- run: npm run stest browsers -- --project=webkit
if: always()
if: ${{ !cancelled() }}
- run: npm run stest frames -- --project=webkit
if: always()
if: ${{ !cancelled() }}
- run: npm run stest contexts -- --project=firefox
if: always()
if: ${{ !cancelled() }}
- run: npm run stest browsers -- --project=firefox
if: always()
if: ${{ !cancelled() }}
- run: npm run stest frames -- --project=firefox
if: always()
if: ${{ !cancelled() }}
- run: npm run stest heap -- --project=chromium
if: always()
if: ${{ !cancelled() }}

View file

@ -9,17 +9,20 @@ on:
env:
# Force terminal colors. @see https://www.npmjs.com/package/colors
FORCE_COLOR: 1
FLAKINESS_CONNECTION_STRING: ${{ secrets.FLAKINESS_CONNECTION_STRING }}
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
jobs:
video_linux:
name: "Video Linux"
environment: allow-uploading-flakiness-results
strategy:
fail-fast: false
matrix:
browser: [chromium, firefox, webkit]
os: [ubuntu-20.04, ubuntu-22.04]
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@ -35,6 +38,13 @@ jobs:
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test -- --project=${{ matrix.browser }}-*
env:
PWTEST_VIDEO: 1
- name: Azure Login
uses: azure/login@v2
if: ${{ !cancelled() && github.event_name == 'push' && github.repository == 'microsoft/playwright' }}
with:
client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: always()
if: ${{ !cancelled() }}
shell: bash

View file

@ -17,13 +17,16 @@ on:
env:
# Force terminal colors. @see https://www.npmjs.com/package/colors
FORCE_COLOR: 1
FLAKINESS_CONNECTION_STRING: ${{ secrets.FLAKINESS_CONNECTION_STRING }}
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
jobs:
test_webview2:
name: WebView2
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
runs-on: windows-2022
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
@ -38,7 +41,20 @@ jobs:
- run: npm run build
- run: dotnet build
working-directory: tests/webview2/webview2-app/
- name: Update to Evergreen WebView2 Runtime
shell: pwsh
run: |
# See here: https://developer.microsoft.com/en-us/microsoft-edge/webview2/
Invoke-WebRequest -Uri 'https://go.microsoft.com/fwlink/p/?LinkId=2124703' -OutFile 'setup.exe'
Start-Process -FilePath setup.exe -Verb RunAs -Wait
- run: npm run webview2test
- name: Azure Login
uses: azure/login@v2
if: ${{ !cancelled() && github.event_name == 'push' && github.repository == 'microsoft/playwright' }}
with:
client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: always()
if: ${{ !cancelled() }}
shell: bash

1
.gitignore vendored
View file

@ -7,6 +7,7 @@ node_modules/
*.swp
*.pyc
.vscode
.mono
.idea
yarn.lock
/packages/playwright-core/src/generated

View file

@ -1,6 +1,6 @@
# 🎭 Playwright
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-123.0.6312.22-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-123.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-17.4-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop -->
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-125.0.6422.26-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-125.0.1-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-17.4-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop -->
## [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 <!-- GEN:chromium-version -->123.0.6312.22<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->125.0.6422.26<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->123.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->125.0.1<!-- GEN:stop --> | :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.

View file

@ -1,3 +1,3 @@
REMOTE_URL="https://github.com/mozilla/gecko-dev"
BASE_BRANCH="release"
BASE_REVISION="a32b8662993085139ac91212a297123b632fc1c0"
BASE_REVISION="f8704c84a751716bad093b9bdc482db53fe5b3ea"

View file

@ -553,7 +553,11 @@ class NetworkRequest {
_sendOnRequestFinished() {
const pageNetwork = this._pageNetwork;
if (pageNetwork) {
// Undefined |responseEndTime| means there has been no response yet.
// This happens when request interception API is used to redirect
// the request to a different URL.
// In this case, we should not emit "requestFinished" event.
if (pageNetwork && this.httpChannel.responseEndTime !== undefined) {
let protocolVersion = undefined;
try {
protocolVersion = this.httpChannel.protocolVersion;

View file

@ -21,6 +21,8 @@ const ALL_PERMISSIONS = [
'desktop-notification',
];
let globalTabAndWindowActivationChain = Promise.resolve();
class DownloadInterceptor {
constructor(registry) {
this._registry = registry
@ -384,7 +386,7 @@ class PageTarget {
const tabBrowser = ownerWindow.gBrowser;
// Serialize all tab-switching commands per tabbed browser
// to disallow concurrent tab switching.
const result = (tabBrowser.__serializedChain ?? Promise.resolve()).then(async () => {
const result = globalTabAndWindowActivationChain.then(async () => {
this._window.focus();
if (tabBrowser.selectedTab !== this._tab) {
const promise = helper.awaitEvent(ownerWindow, 'TabSwitchDone');
@ -399,7 +401,7 @@ class PageTarget {
notificationsPopup?.style.removeProperty('pointer-events');
}
});
tabBrowser.__serializedChain = result.catch(error => { /* swallow errors to keep chain running */ });
globalTabAndWindowActivationChain = result.catch(error => { /* swallow errors to keep chain running */ });
return result;
}

View file

@ -62,7 +62,6 @@ class PageAgent {
const docShell = frameTree.mainFrame().docShell();
this._docShell = docShell;
this._initialDPPX = docShell.contentViewer.overrideDPPX;
// Dispatch frameAttached events for all initial frames
for (const frame of this._frameTree.frames()) {
@ -383,6 +382,12 @@ class PageAgent {
throw new Error('Object is not input!');
const nsFiles = await Promise.all(files.map(filePath => File.createFromFileName(filePath)));
unsafeObject.mozSetFileArray(nsFiles);
const events = [
new (frame.domWindow().Event)('input', { bubbles: true, cancelable: true, composed: true }),
new (frame.domWindow().Event)('change', { bubbles: true, cancelable: true, composed: true }),
];
for (const event of events)
unsafeObject.dispatchEvent(event);
}
_getContentQuads({objectId, frameId}) {

View file

@ -89,10 +89,10 @@ index b40e0fceb567c0d217adf284e13f434e49cc8467..2c4e6d5fbf8da40954ad6a5b15e41249
DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT;
diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn
index 24a6f228c617c034ccc4926c238aa15e32c964f6..38c4ed9936f5033abe7a4ac7997aea2d92747733 100644
index f6d425f36a965f03ac82dbe3ab6cde06f12751ac..d60999ab2658b1e1e5f07a8aee530451c44f2957 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
@@ -73,6 +73,12 @@ browser/features/webcompat@mozilla.org/shims/empty-shim.txt
removed-files
#endif
@ -106,10 +106,10 @@ index 24a6f228c617c034ccc4926c238aa15e32c964f6..38c4ed9936f5033abe7a4ac7997aea2d
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 b3213b8c4498b0467d7863d53c5fc4240e4609be..5ca6b68c6c7aaebd41a81999974f69810876f82b 100644
index 4068c0c165fcebd0a72f4d780bc0cbd680fe7a9c..fec7f8505d22485fa6ad4193d59387f493bd1ef8 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -195,6 +195,9 @@
@@ -197,6 +197,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 211f83a476caa08281b3a0158da42cb08b01a374..47d78411e67d3f9b67c48ff10379e117b8631ab6 100644
index 0ef5e02d2ae365b4e7b30fd49771e547c030bcfc..0787518696fc90bba3dce8c1e1c00387c3e7a6c9 100644
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -114,6 +114,20 @@ struct ParamTraits<mozilla::dom::PrefersColorSchemeOverride>
@ -191,7 +191,7 @@ index 211f83a476caa08281b3a0158da42cb08b01a374..47d78411e67d3f9b67c48ff10379e117
template <>
struct ParamTraits<mozilla::dom::ExplicitActiveStatus>
: public ContiguousEnumSerializer<
@@ -2781,6 +2795,40 @@ void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
@@ -2793,6 +2807,40 @@ void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
PresContextAffectingFieldChanged();
}
@ -233,7 +233,7 @@ index 211f83a476caa08281b3a0158da42cb08b01a374..47d78411e67d3f9b67c48ff10379e117
nsString&& aOldValue) {
MOZ_ASSERT(IsTop());
diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h
index 84532563e1db1941abca5afa74a7d68473d391ef..84d6f605d5f70d11b355ba76b0f5af868864ac18 100644
index 7554128cf49ce929c973aeddf5f50ede01829c28..81ae7ec9523b944654c048af6a9f6844799eb4f3 100644
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -200,10 +200,10 @@ struct EmbedderColorSchemes {
@ -260,7 +260,7 @@ index 84532563e1db1941abca5afa74a7d68473d391ef..84d6f605d5f70d11b355ba76b0f5af86
/* The number of entries added to the session history because of this \
* browsing context. */ \
FIELD(HistoryEntryCount, uint32_t) \
@@ -923,6 +927,14 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
@@ -933,6 +937,14 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return GetPrefersColorSchemeOverride();
}
@ -275,7 +275,7 @@ index 84532563e1db1941abca5afa74a7d68473d391ef..84d6f605d5f70d11b355ba76b0f5af86
bool IsInBFCache() const;
bool AllowJavascript() const { return GetAllowJavascript(); }
@@ -1087,6 +1099,23 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
@@ -1097,6 +1109,23 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void WalkPresContexts(Callback&&);
void PresContextAffectingFieldChanged();
@ -300,7 +300,7 @@ index 84532563e1db1941abca5afa74a7d68473d391ef..84d6f605d5f70d11b355ba76b0f5af86
bool CanSet(FieldIndex<IDX_SuspendMediaWhenInactive>, bool, ContentParent*) {
diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp
index 044df3a801ed55e08347464397821261bb6761c0..8aa8e4844e4ba5364cbb0547c5397ce695798861 100644
index d5f85b1e571a7840425164a3c7715e8c70ed5c8b..5ba7484b1e7f817b2bb21dd6b5b35c6ffe2c1ca5 100644
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -1466,6 +1466,12 @@ void CanonicalBrowsingContext::LoadURI(nsIURI* aURI,
@ -317,7 +317,7 @@ index 044df3a801ed55e08347464397821261bb6761c0..8aa8e4844e4ba5364cbb0547c5397ce6
}
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e8aa888bf 100644
index 0b8212fbf3f81ef6264f17fc8e84f91cde39c0f7..99d43d662978c0418231b8ea55d670f81b5618d7 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -15,6 +15,12 @@
@ -350,7 +350,7 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/ParentChannelWrapper.h"
@@ -111,6 +119,7 @@
#include "nsIDocShellTreeOwner.h"
#include "nsIDocumentViewer.h"
#include "mozilla/dom/Document.h"
#include "nsHTMLDocument.h"
+#include "mozilla/dom/Element.h"
@ -379,7 +379,7 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
mAllowAuth(mItemType == typeContent),
mAllowKeywordFixup(false),
mDisableMetaRefreshWhenInactive(false),
@@ -3147,6 +3164,214 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
@@ -3115,6 +3132,214 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
return NS_OK;
}
@ -594,7 +594,7 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
NS_IMETHODIMP
nsDocShell::GetIsNavigating(bool* aOut) {
*aOut = mIsNavigating;
@@ -4835,7 +5060,7 @@ nsDocShell::GetVisibility(bool* aVisibility) {
@@ -4803,7 +5028,7 @@ nsDocShell::GetVisibility(bool* aVisibility) {
}
void nsDocShell::ActivenessMaybeChanged() {
@ -603,7 +603,7 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
if (RefPtr<PresShell> presShell = GetPresShell()) {
presShell->ActivenessMaybeChanged();
}
@@ -6753,6 +6978,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
@@ -6722,6 +6947,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
return false; // no entry to save into
}
@ -613,8 +613,8 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
+
MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
"mOSHE cannot be non-null with SHIP");
nsCOMPtr<nsIContentViewer> viewer = mOSHE->GetContentViewer();
@@ -8536,6 +8765,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer();
@@ -8454,6 +8683,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
true, // aForceNoOpener
getter_AddRefs(newBC));
MOZ_ASSERT(!newBC);
@ -627,7 +627,7 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
return rv;
}
@@ -9611,6 +9846,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
@@ -9566,6 +9801,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
nsCOMPtr<nsIRequest> req;
@ -644,7 +644,7 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
if (NS_SUCCEEDED(rv)) {
@@ -12768,6 +13013,9 @@ class OnLinkClickEvent : public Runnable {
@@ -12714,6 +12959,9 @@ class OnLinkClickEvent : public Runnable {
mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
mTriggeringPrincipal);
}
@ -654,7 +654,7 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
return NS_OK;
}
@@ -12852,6 +13100,8 @@ nsresult nsDocShell::OnLinkClick(
@@ -12798,6 +13046,8 @@ nsresult nsDocShell::OnLinkClick(
nsCOMPtr<nsIRunnable> ev =
new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
aIsTrusted, aTriggeringPrincipal);
@ -664,7 +664,7 @@ index f4d8843e2df614b0037d2bfa47f02e328c7dff6c..596208d570d6625fa6b99fd75e7a037e
}
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index 237cf8dc1a54ed8d0523272aaaebfd6f10f9213f..73c096a2850850921aff97037457cde598ffef2a 100644
index 9f2d9a17dc0d54be4bd09f8e04da6e85f987fe34..8ff6e67fedef0bf73297c4cfed569b8f2ced36d9 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -15,6 +15,7 @@
@ -683,7 +683,7 @@ index 237cf8dc1a54ed8d0523272aaaebfd6f10f9213f..73c096a2850850921aff97037457cde5
class nsGlobalWindowOuter;
class FramingChecker;
@@ -408,6 +410,15 @@ class nsDocShell final : public nsDocLoader,
@@ -401,6 +403,15 @@ class nsDocShell final : public nsDocLoader,
void SetWillChangeProcess() { mWillChangeProcess = true; }
bool WillChangeProcess() { return mWillChangeProcess; }
@ -698,8 +698,8 @@ index 237cf8dc1a54ed8d0523272aaaebfd6f10f9213f..73c096a2850850921aff97037457cde5
+
// Create a content viewer within this nsDocShell for the given
// `WindowGlobalChild` actor.
nsresult CreateContentViewerForActor(
@@ -1010,6 +1021,8 @@ class nsDocShell final : public nsDocLoader,
nsresult CreateDocumentViewerForActor(
@@ -1003,6 +1014,8 @@ class nsDocShell final : public nsDocLoader,
bool CSSErrorReportingEnabled() const { return mCSSErrorReportingEnabled; }
@ -708,7 +708,7 @@ index 237cf8dc1a54ed8d0523272aaaebfd6f10f9213f..73c096a2850850921aff97037457cde5
// 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
@@ -1300,6 +1313,16 @@ class nsDocShell final : public nsDocLoader,
@@ -1294,6 +1307,16 @@ class nsDocShell final : public nsDocLoader,
bool mAllowDNSPrefetch : 1;
bool mAllowWindowControl : 1;
bool mCSSErrorReportingEnabled : 1;
@ -726,18 +726,18 @@ index 237cf8dc1a54ed8d0523272aaaebfd6f10f9213f..73c096a2850850921aff97037457cde5
bool mAllowKeywordFixup : 1;
bool mDisableMetaRefreshWhenInactive : 1;
diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
index a3ee2c3944018c64c93c02c5658c527ec5f9d2cb..272e5147568b95bf313ac7a940619382a0c42763 100644
index 9e79b6831a74c6de7c6a6c56d9a024d29c1c704b..db5cd5586b74ec65d788480f9c5fca93eb562c21 100644
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -44,6 +44,7 @@ interface nsIURI;
interface nsIChannel;
interface nsIContentViewer;
interface nsIContentSecurityPolicy;
interface nsIDocumentViewer;
+interface nsIDOMGeoPosition;
interface nsIEditor;
interface nsIEditingSession;
interface nsIInputStream;
@@ -760,6 +761,36 @@ interface nsIDocShell : nsIDocShellTreeItem
@@ -767,6 +768,36 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
void synchronizeLayoutHistoryState();
@ -775,10 +775,10 @@ index a3ee2c3944018c64c93c02c5658c527ec5f9d2cb..272e5147568b95bf313ac7a940619382
* 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 ff78b31699fe28e60915703b7642a72d540f6f99..dd8f4dd0283e3d0ce1b5ae8745312fcb64126c75 100644
index 0a2f5be08d0dcad40cd11f4b064a1287b8141e2f..a845203c6aaea8384e8835cbe005669767a1b196 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -3682,6 +3682,9 @@ void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
@@ -3705,6 +3705,9 @@ void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
}
void Document::ApplySettingsFromCSP(bool aSpeculative) {
@ -788,7 +788,7 @@ index ff78b31699fe28e60915703b7642a72d540f6f99..dd8f4dd0283e3d0ce1b5ae8745312fcb
nsresult rv = NS_OK;
if (!aSpeculative) {
// 1) apply settings from regular CSP
@@ -3739,6 +3742,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
@@ -3762,6 +3765,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
MOZ_ASSERT(!mScriptGlobalObject,
"CSP must be initialized before mScriptGlobalObject is set!");
@ -800,7 +800,7 @@ index ff78b31699fe28e60915703b7642a72d540f6f99..dd8f4dd0283e3d0ce1b5ae8745312fcb
// If this is a data document - no need to set CSP.
if (mLoadedAsData) {
return NS_OK;
@@ -4534,6 +4542,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
@@ -4557,6 +4565,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
return false;
}
@ -811,7 +811,7 @@ index ff78b31699fe28e60915703b7642a72d540f6f99..dd8f4dd0283e3d0ce1b5ae8745312fcb
if (!fm->IsInActiveWindow(bc)) {
return false;
}
@@ -18736,6 +18748,68 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
@@ -18878,6 +18890,68 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
return PreferenceSheet::PrefsFor(*this).mColorScheme;
}
@ -881,10 +881,10 @@ index ff78b31699fe28e60915703b7642a72d540f6f99..dd8f4dd0283e3d0ce1b5ae8745312fcb
if (!sLoadingForegroundTopLevelContentDocument) {
return false;
diff --git a/dom/base/Document.h b/dom/base/Document.h
index 55e4f71d5b33cca55454ac0d4e16d4a96137a616..8ad7117047ee6d0b5de2538cd086d60ae9837594 100644
index 3f8032594868b8aa1c8bec2d25b789518170eb0e..5017b426369378b892a224a88410f18e743da45f 100644
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -4030,6 +4030,9 @@ class Document : public nsINode,
@@ -4051,6 +4051,9 @@ class Document : public nsINode,
// color-scheme meta tag.
ColorScheme DefaultColorScheme() const;
@ -895,10 +895,10 @@ index 55e4f71d5b33cca55454ac0d4e16d4a96137a616..8ad7117047ee6d0b5de2538cd086d60a
static bool AutomaticStorageAccessPermissionCanBeGranted(
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index 7e54ac18ad9a646e981e7bc356e75de273c75845..1e673db0fd8adc9a4096a826184425e939d5e926 100644
index d4b04f809228fecc3e2dbf33dac42151ffc24b11..39dfddff7404e878cd9fcc8f3b9bb734ab72d743 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -331,14 +331,18 @@ void Navigator::GetAppName(nsAString& aAppName) const {
@@ -335,14 +335,18 @@ void Navigator::GetAppName(nsAString& aAppName) const {
* for more detail.
*/
/* static */
@ -919,7 +919,7 @@ index 7e54ac18ad9a646e981e7bc356e75de273c75845..1e673db0fd8adc9a4096a826184425e9
// Split values on commas.
for (nsDependentSubstring lang :
@@ -390,7 +394,13 @@ void Navigator::GetLanguage(nsAString& aLanguage) {
@@ -394,7 +398,13 @@ void Navigator::GetLanguage(nsAString& aLanguage) {
}
void Navigator::GetLanguages(nsTArray<nsString>& aLanguages) {
@ -935,10 +935,10 @@ index 7e54ac18ad9a646e981e7bc356e75de273c75845..1e673db0fd8adc9a4096a826184425e9
// 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
diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h
index 851b681570e35ba86341f56912684bc8ec7001f3..2fd4d6fd9deafaa42604428dc17798142ce77e19 100644
index 998328cfc6f36fd579e4fe26629a92a1ceefa9fa..c73e48d8dfe5a66fb9eb7f6bf49527f88cabc884 100644
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -214,7 +214,7 @@ class Navigator final : public nsISupports, public nsWrapperCache {
@@ -216,7 +216,7 @@ class Navigator final : public nsISupports, public nsWrapperCache {
StorageManager* Storage();
@ -948,10 +948,10 @@ index 851b681570e35ba86341f56912684bc8ec7001f3..2fd4d6fd9deafaa42604428dc1779814
dom::MediaCapabilities* MediaCapabilities();
dom::MediaSession* MediaSession();
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index a5fee753ce551826e5b974ca01aff1f541754f6b..f1a80d1bb25175c12345acbc27b82d96793e061d 100644
index 6d3a9f8937a7b37bf82a18362d7ee525e4e267f4..c598acdcda9d47cdd193011e8faa916988872a18 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8598,7 +8598,8 @@ nsresult nsContentUtils::SendMouseEvent(
@@ -8697,7 +8697,8 @@ nsresult nsContentUtils::SendMouseEvent(
bool aIgnoreRootScrollFrame, float aPressure,
unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
@ -961,7 +961,7 @@ index a5fee753ce551826e5b974ca01aff1f541754f6b..f1a80d1bb25175c12345acbc27b82d96
nsPoint offset;
nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
if (!widget) return NS_ERROR_FAILURE;
@@ -8606,6 +8607,7 @@ nsresult nsContentUtils::SendMouseEvent(
@@ -8705,6 +8706,7 @@ nsresult nsContentUtils::SendMouseEvent(
EventMessage msg;
Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
bool contextMenuKey = false;
@ -969,7 +969,7 @@ index a5fee753ce551826e5b974ca01aff1f541754f6b..f1a80d1bb25175c12345acbc27b82d96
if (aType.EqualsLiteral("mousedown")) {
msg = eMouseDown;
} else if (aType.EqualsLiteral("mouseup")) {
@@ -8630,6 +8632,12 @@ nsresult nsContentUtils::SendMouseEvent(
@@ -8729,6 +8731,12 @@ nsresult nsContentUtils::SendMouseEvent(
msg = eMouseHitTest;
} else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
msg = eMouseExploreByTouch;
@ -982,7 +982,7 @@ index a5fee753ce551826e5b974ca01aff1f541754f6b..f1a80d1bb25175c12345acbc27b82d96
} else {
return NS_ERROR_FAILURE;
}
@@ -8638,12 +8646,21 @@ nsresult nsContentUtils::SendMouseEvent(
@@ -8737,12 +8745,21 @@ nsresult nsContentUtils::SendMouseEvent(
aInputSourceArg = MouseEvent_Binding::MOZ_SOURCE_MOUSE;
}
@ -1006,7 +1006,7 @@ index a5fee753ce551826e5b974ca01aff1f541754f6b..f1a80d1bb25175c12345acbc27b82d96
event.pointerId = aIdentifier;
event.mModifiers = GetWidgetModifiers(aModifiers);
event.mButton = aButton;
@@ -8654,8 +8671,10 @@ nsresult nsContentUtils::SendMouseEvent(
@@ -8753,8 +8770,10 @@ nsresult nsContentUtils::SendMouseEvent(
event.mPressure = aPressure;
event.mInputSource = aInputSourceArg;
event.mClickCount = aClickCount;
@ -1018,10 +1018,10 @@ index a5fee753ce551826e5b974ca01aff1f541754f6b..f1a80d1bb25175c12345acbc27b82d96
nsPresContext* presContext = aPresShell->GetPresContext();
if (!presContext) return NS_ERROR_FAILURE;
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 36c1a4f4eeba0fa75d79715f79de0d705719e5c5..6ad5426612ef37d7326bafd771a4c2d6d9142cb5 100644
index db68b67c5a3e60ac214231ac82fd9f652a697858..529b49b2c6c5f693747d847bca85e93415b9159c 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2963,7 +2963,8 @@ class nsContentUtils {
@@ -2958,7 +2958,8 @@ class nsContentUtils {
int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure,
unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
mozilla::PreventDefaultResult* aPreventDefault,
@ -1032,10 +1032,10 @@ index 36c1a4f4eeba0fa75d79715f79de0d705719e5c5..6ad5426612ef37d7326bafd771a4c2d6
static void FirePageShowEventForFrameLoaderSwap(
nsIDocShellTreeItem* aItem,
diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp
index e6762018cce8e4c8691703ed15ea29e709f31cf2..4d0b679c4700e8fe18408a186b7e4d512a71dc30 100644
index 6c547a9fd8604ac7fd7b965be473bc4120b2fdf7..c9aa1951da7af70913f77c76bb7c68ba144adaeb 100644
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -684,6 +684,26 @@ nsDOMWindowUtils::GetPresShellId(uint32_t* aPresShellId) {
@@ -685,6 +685,26 @@ nsDOMWindowUtils::GetPresShellId(uint32_t* aPresShellId) {
return NS_ERROR_FAILURE;
}
@ -1062,7 +1062,7 @@ index e6762018cce8e4c8691703ed15ea29e709f31cf2..4d0b679c4700e8fe18408a186b7e4d51
NS_IMETHODIMP
nsDOMWindowUtils::SendMouseEvent(
const nsAString& aType, float aX, float aY, int32_t aButton,
@@ -698,7 +718,7 @@ nsDOMWindowUtils::SendMouseEvent(
@@ -699,7 +719,7 @@ nsDOMWindowUtils::SendMouseEvent(
aOptionalArgCount >= 7 ? aIdentifier : DEFAULT_MOUSE_POINTER_ID, false,
aPreventDefault, aOptionalArgCount >= 4 ? aIsDOMEventSynthesized : true,
aOptionalArgCount >= 5 ? aIsWidgetEventSynthesized : false,
@ -1071,7 +1071,7 @@ index e6762018cce8e4c8691703ed15ea29e709f31cf2..4d0b679c4700e8fe18408a186b7e4d51
}
NS_IMETHODIMP
@@ -716,7 +736,7 @@ nsDOMWindowUtils::SendMouseEventToWindow(
@@ -717,7 +737,7 @@ nsDOMWindowUtils::SendMouseEventToWindow(
aOptionalArgCount >= 7 ? aIdentifier : DEFAULT_MOUSE_POINTER_ID, true,
nullptr, aOptionalArgCount >= 4 ? aIsDOMEventSynthesized : true,
aOptionalArgCount >= 5 ? aIsWidgetEventSynthesized : false,
@ -1080,7 +1080,7 @@ index e6762018cce8e4c8691703ed15ea29e709f31cf2..4d0b679c4700e8fe18408a186b7e4d51
}
NS_IMETHODIMP
@@ -725,13 +745,13 @@ nsDOMWindowUtils::SendMouseEventCommon(
@@ -726,13 +746,13 @@ nsDOMWindowUtils::SendMouseEventCommon(
int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame,
float aPressure, unsigned short aInputSourceArg, uint32_t aPointerId,
bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized,
@ -1110,7 +1110,7 @@ index 63968c9b7a4e418e4c0de6e7a75fa215a36a9105..decf3ea3833ccdffd49a7aded2d600f9
MOZ_CAN_RUN_SCRIPT
nsresult SendTouchEventCommon(
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
index 0d9e4df42f7efcbb0068bc162dfbb17003cd3802..55c0e9e8cfa434c5b8c7dbd27651bf94d5b9a2d1 100644
index 60f45157cdfceb7ebf4f9a1681d0e59dc1822360..964d7cc1b847cd8ef21cef29be4fdc11168b18d8 100644
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1673,6 +1673,10 @@ Maybe<uint64_t> nsFocusManager::SetFocusInner(Element* aNewContent,
@ -1136,10 +1136,10 @@ index 0d9e4df42f7efcbb0068bc162dfbb17003cd3802..55c0e9e8cfa434c5b8c7dbd27651bf94
// 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 97d967175822e4f62079c21152f96e5540dbb98f..c5bf0d155649e6715aa3af6ca29c237b1dab6ead 100644
index a10808b95d5f7c81942d2a513f63a72c7821061e..027b9a3c87811b794816bddb90ff67bc271f7faa 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -2509,10 +2509,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
@@ -2510,10 +2510,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
}();
if (!isContentAboutBlankInChromeDocshell) {
@ -1160,7 +1160,7 @@ index 97d967175822e4f62079c21152f96e5540dbb98f..c5bf0d155649e6715aa3af6ca29c237b
}
}
@@ -2632,6 +2638,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
@@ -2633,6 +2639,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
}
}
@ -1193,10 +1193,10 @@ index 8337a7353fb8e97372f0b57bd0fd867506a9129f..e7dedd6d26125e481e1145337a0be6ab
// Outer windows only.
virtual void EnsureSizeAndPositionUpToDate() override;
diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp
index 369bb41fe459c8ef4c10c61c1530f52d51b3f6de..15ef3c6824cc70e756777612f0fd148e5678c795 100644
index 11ca350f483458ba11f0ee170ce38e9785e8c70f..6bb310f6e96239388b4c92bd7bdf2d698fac8139 100644
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1359,6 +1359,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
@@ -1362,6 +1362,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv);
}
@ -1259,10 +1259,10 @@ index 369bb41fe459c8ef4c10c61c1530f52d51b3f6de..15ef3c6824cc70e756777612f0fd148e
DOMQuad& aQuad, const GeometryNode& aFrom,
const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h
index 807deebaad78f531c59d18aac2c2833e38d13946..3754b573ef119854e2c41b51b0dd0a43d7c53315 100644
index 0ba44cb08ffffde3bf9616e7e3b1fb33317181b6..b698e309e7bf658739b3a69a1d68f657a6c84e6e 100644
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -2229,6 +2229,10 @@ class nsINode : public mozilla::dom::EventTarget {
@@ -2235,6 +2235,10 @@ class nsINode : public mozilla::dom::EventTarget {
nsTArray<RefPtr<DOMQuad>>& aResult,
ErrorResult& aRv);
@ -1274,7 +1274,7 @@ index 807deebaad78f531c59d18aac2c2833e38d13946..3754b573ef119854e2c41b51b0dd0a43
DOMQuad& aQuad, const TextOrElementOrDocument& aFrom,
const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp
index bb280d9a15aed8b7d82a93214e82222e9b5a14ae..d64cd52877c1643148ce5912eec26bd1569acdac 100644
index cf8037cd580013efe5eb578c43f45c0d21946c6a..583460796fdef633e8075013597f7c315ce4ab06 100644
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -177,6 +177,11 @@ bool nsJSUtils::GetScopeChainForElement(
@ -1290,7 +1290,7 @@ index bb280d9a15aed8b7d82a93214e82222e9b5a14ae..d64cd52877c1643148ce5912eec26bd1
void nsJSUtils::ResetTimeZone() { JS::ResetTimeZone(); }
diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h
index 36e906061588aab50dee129cc46dd2e4d3e153f8..c3591f98d4df19b166fc5c99332e559b1d499049 100644
index cceb725d393d5e5f83c8f87491089c3fa1d57cc3..e906a7fb7c3fd72554613f640dcc272e6984d929 100644
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -79,6 +79,7 @@ class nsJSUtils {
@ -1302,7 +1302,7 @@ index 36e906061588aab50dee129cc46dd2e4d3e153f8..c3591f98d4df19b166fc5c99332e559b
static bool DumpEnabled();
diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl
index 2e8abd50793dd1392a876e53e99ad806a256f755..695b9e8ffff846d663b645e3ca6fc83a39f6f126 100644
index 8600a844634eed78e0b8470eaf11144f8ebd230b..853a4c98b78092181ad15542ae48cd41e9c49a82 100644
--- a/dom/chrome-webidl/BrowsingContext.webidl
+++ b/dom/chrome-webidl/BrowsingContext.webidl
@@ -53,6 +53,24 @@ enum PrefersColorSchemeOverride {
@ -1443,7 +1443,7 @@ index 7e1af00d05fbafa2d828e2c7e4dcc5c82d115f5b..e85af9718d064e4d2865bc944e9d4ba1
~Geolocation();
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
index 09d57823aa37f596ac04261025862975f414bd7c..8cee0ae86ae1ed3aeda879cbf55f381a772dfa8b 100644
index ae2c4af82c3827a521a79f8879a6f941ea68feca..92ea48eba6fb575ea1415d8713b49707b895561b 100644
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -58,6 +58,7 @@
@ -1454,7 +1454,7 @@ index 09d57823aa37f596ac04261025862975f414bd7c..8cee0ae86ae1ed3aeda879cbf55f381a
#include "nsIFormControlFrame.h"
#include "nsITextControlFrame.h"
#include "nsIFrame.h"
@@ -783,6 +784,12 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
@@ -784,6 +785,12 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
return NS_ERROR_FAILURE;
}
@ -1468,7 +1468,7 @@ index 09d57823aa37f596ac04261025862975f414bd7c..8cee0ae86ae1ed3aeda879cbf55f381a
return NS_OK;
}
diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl
index 4170a79023a2503831d080a6e65d5e143f34f241..3af08d6ea5f1cfbdc373774764a0c45fe3aa0e27 100644
index 564b24e3f89e0ce6e683902a7fc4e1c3a6f5faac..e4264b251e580bb13aa2941b26227541c74d3371 100644
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -373,6 +373,26 @@ interface nsIDOMWindowUtils : nsISupports {
@ -1499,10 +1499,10 @@ index 4170a79023a2503831d080a6e65d5e143f34f241..3af08d6ea5f1cfbdc373774764a0c45f
* touchstart, touchend, touchmove, and touchcancel
*
diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp
index 2e69547612aa1ef91d726ee54475a5f8e638e214..2b7aae36d1b4b7a7a7a7a6117104d3455e1768e3 100644
index 830ad8579a39d262a1fd2c86479f2ddc77c01ae2..b840b68c9b5545fc974e1dafacac2e74372f4e41 100644
--- a/dom/ipc/BrowserChild.cpp
+++ b/dom/ipc/BrowserChild.cpp
@@ -1647,6 +1647,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
@@ -1651,6 +1651,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
if (postLayerization) {
postLayerization->Register();
}
@ -1539,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 532605b813d4e4a7693e6c82f6792075ef2565de..d225ffaf3439739d989b21f858c1763942beee9e 100644
index a966ff06d4a52e2ff70ce71df3a6a607a67e3eda..eb6bb16413df43217ddd85048c02d41d15e8431f 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,
@ -1644,7 +1644,7 @@ index 532605b813d4e4a7693e6c82f6792075ef2565de..d225ffaf3439739d989b21f858c17639
frameInfo.width * frameInfo.height * DesktopFrame::kBytesPerPixel;
diff --git a/dom/media/systemservices/video_engine/desktop_capture_impl.h b/dom/media/systemservices/video_engine/desktop_capture_impl.h
index 64eadb140131ea807db8b55431630523342a88ce..371e093fef6e225302da6aaf6e1ede05b93efd74 100644
index 7292f6c8a70298d4bf103080804843fa9bba1d15..7a50fee0d2fcaad475302d010892800a6e3c4e75 100644
--- a/dom/media/systemservices/video_engine/desktop_capture_impl.h
+++ b/dom/media/systemservices/video_engine/desktop_capture_impl.h
@@ -24,6 +24,7 @@
@ -1677,7 +1677,7 @@ index 64eadb140131ea807db8b55431630523342a88ce..371e093fef6e225302da6aaf6e1ede05
// simulate deviceInfo interface for video engine, bridge screen/application and
// real screen/application device info
@@ -155,13 +171,13 @@ class BrowserDeviceInfoImpl : public VideoCaptureModule::DeviceInfo {
@@ -158,13 +174,13 @@ class BrowserDeviceInfoImpl : public VideoCaptureModule::DeviceInfo {
// As with video, DesktopCaptureImpl is a proxy for screen sharing
// and follows the video pipeline design
class DesktopCaptureImpl : public DesktopCapturer::Callback,
@ -1694,7 +1694,7 @@ index 64eadb140131ea807db8b55431630523342a88ce..371e093fef6e225302da6aaf6e1ede05
[[nodiscard]] static std::shared_ptr<VideoCaptureModule::DeviceInfo>
CreateDeviceInfo(const int32_t aId,
@@ -175,6 +191,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
@@ -178,6 +194,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
void DeRegisterCaptureDataCallback(
rtc::VideoSinkInterface<VideoFrame>* aCallback) override;
int32_t StopCaptureIfAllClientsClose() override;
@ -1703,7 +1703,7 @@ index 64eadb140131ea807db8b55431630523342a88ce..371e093fef6e225302da6aaf6e1ede05
int32_t SetCaptureRotation(VideoRotation aRotation) override;
bool SetApplyRotation(bool aEnable) override;
@@ -197,7 +215,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
@@ -200,7 +218,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
protected:
DesktopCaptureImpl(const int32_t aId, const char* aUniqueId,
@ -1713,7 +1713,7 @@ index 64eadb140131ea807db8b55431630523342a88ce..371e093fef6e225302da6aaf6e1ede05
virtual ~DesktopCaptureImpl();
private:
@@ -205,6 +224,9 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
@@ -208,6 +227,9 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
static constexpr uint32_t kMaxDesktopCaptureCpuUsage = 50;
void InitOnThread(std::unique_ptr<DesktopCapturer> aCapturer, int aFramerate);
void ShutdownOnThread();
@ -1723,7 +1723,7 @@ index 64eadb140131ea807db8b55431630523342a88ce..371e093fef6e225302da6aaf6e1ede05
// DesktopCapturer::Callback interface.
void OnCaptureResult(DesktopCapturer::Result aResult,
std::unique_ptr<DesktopFrame> aFrame) override;
@@ -212,6 +234,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
@@ -215,6 +237,8 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
// Notifies all mCallbacks of OnFrame(). mCaptureThread only.
void NotifyOnFrame(const VideoFrame& aFrame);
@ -1777,7 +1777,7 @@ index 3b39538e51840cd9b1685b2efd2ff2e9ec83608a..c7bf4f2d53b58bbacb22b3ebebf6f3fc
return aGlobalOrNull;
diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp
index d1bd1d060d749de3ac16bd6e9fd3383383e4dd9a..e6262c210accf12c3d071d42031b432b2a6332b5 100644
index 50730b691b06409f47cb9172570866a7bb519abc..e5ae4f31b8f93f17943c24108a82c6370470e9f2 100644
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -22,6 +22,7 @@
@ -1824,7 +1824,7 @@ index 2f71b284ee5f7e11f117c447834b48355784448c..2640bd57123c2b03bf4b06a2419cd020
* returned quads are further translated relative to the window
* origin -- which is not the layout origin. Further translation
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
index 4fc3db17b202f768cadc7fa3921badc4374a9f7a..be58abf2b7906e7307dd8f106f5f8bd2206793bc 100644
index bcfbe33925afb97e68305394fb38e42481682540..47bc46f34a93991f112292c851c539a6bc97778f 100644
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -985,7 +985,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
@ -1902,7 +1902,7 @@ index d10dabb5c5ff8e17851edf2bd2efc08e74584d8e..53c4070c5fde43b27fb8fbfdcf4c23d8
bool IsWorkerGlobal(JSObject* global);
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index ed8a07bcdc25f77a93079c8b9f113c59b9befaf9..2f09a961603f28a658f2d29321a389b7623482b9 100644
index acef06ba3abb006932f26f8a96fd73028f015150..940210c603d906ae0469d826b81ba8f537e7fef5 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -679,6 +679,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
@ -1924,7 +1924,7 @@ index ed8a07bcdc25f77a93079c8b9f113c59b9befaf9..2f09a961603f28a658f2d29321a389b7
class UpdateLanguagesRunnable final : public WorkerRunnable {
nsTArray<nsString> mLanguages;
@@ -1949,6 +1961,16 @@ void WorkerPrivate::UpdateContextOptions(
@@ -1977,6 +1989,16 @@ void WorkerPrivate::UpdateContextOptions(
}
}
@ -1941,7 +1941,7 @@ index ed8a07bcdc25f77a93079c8b9f113c59b9befaf9..2f09a961603f28a658f2d29321a389b7
void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) {
AssertIsOnParentThread();
@@ -5450,6 +5472,15 @@ void WorkerPrivate::UpdateContextOptionsInternal(
@@ -5480,6 +5502,15 @@ void WorkerPrivate::UpdateContextOptionsInternal(
}
}
@ -1958,7 +1958,7 @@ index ed8a07bcdc25f77a93079c8b9f113c59b9befaf9..2f09a961603f28a658f2d29321a389b7
const nsTArray<nsString>& aLanguages) {
WorkerGlobalScope* globalScope = GlobalScope();
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
index a134d4846f9ad0f273f61fe2c2daa10af7b3c7a6..38bb391cb542a560f93a0c302a1d942aad8135a6 100644
index a670d009759d101cf9574cde3c2481b8aa2737f6..ac87b7f27dfcc060adb52387b146c45eed996fa7 100644
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -417,6 +417,8 @@ class WorkerPrivate final
@ -2019,10 +2019,10 @@ index 9d0423ef13958d5c443cc3531269603c4801c338..f0c4ba7c528d2be466e0f7669a1e37e8
* Set the default time zone.
*/
diff --git a/js/public/Date.h b/js/public/Date.h
index 422ea0b2c0daafd4447d0d83500ea73545c99b76..7f03adc302112e0de3d842d7ce6bc0909b50f76a 100644
index 523e84c8c93f4221701f90f2e8ee146ec8e1adbd..98d5b1176e5378431b859a2dbd4d4e778d236e78 100644
--- a/js/public/Date.h
+++ b/js/public/Date.h
@@ -54,6 +54,8 @@ namespace JS {
@@ -55,6 +55,8 @@ namespace JS {
*/
extern JS_PUBLIC_API void ResetTimeZone();
@ -2032,10 +2032,10 @@ index 422ea0b2c0daafd4447d0d83500ea73545c99b76..7f03adc302112e0de3d842d7ce6bc090
inline ClippedTime TimeClip(double time);
diff --git a/js/src/debugger/Object.cpp b/js/src/debugger/Object.cpp
index 2cb72597fdf2a10fdc53bf03d6cc98d9bc16af58..b3ce8a99c37fc8c503206ea2b47b7e000fc4bf3b 100644
index c5a4f1f6dcf95922b5c8506d6bd24ad4fd2f1efc..a79bb0ad42d1bbd0434cda27c118cdd099013ab2 100644
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -2446,7 +2446,11 @@ Maybe<Completion> DebuggerObject::call(JSContext* cx,
@@ -2450,7 +2450,11 @@ Maybe<Completion> DebuggerObject::call(JSContext* cx,
invokeArgs[i].set(args2[i]);
}
@ -2202,10 +2202,10 @@ index dac899f7558b26d6848da8b98ed8a93555c8751a..2a07d67fa1c2840b25085566e84dc3b2
// No boxes to return
return;
diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
index 6e27e39f432d678376cec3bfd8491e5f20ebd893..a69211527a0a2411b6ef4a69de0183428114139d 100644
index 4afd2b10dfdab527b63f17919c52a559b3ac3de6..787283c004d9baa7227f00c338e0322dca88f949 100644
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -10906,7 +10906,9 @@ bool PresShell::ComputeActiveness() const {
@@ -10963,7 +10963,9 @@ bool PresShell::ComputeActiveness() const {
if (!browserChild->IsVisible()) {
MOZ_LOG(gLog, LogLevel::Debug,
(" > BrowserChild %p is not visible", browserChild));
@ -2217,10 +2217,10 @@ index 6e27e39f432d678376cec3bfd8491e5f20ebd893..a69211527a0a2411b6ef4a69de018342
// 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 327b3d587844c0ad81f5b9c2c622bf52315380bf..d5eefde23dfa3d5666f04af90b7dda1ffa38f6bd 100644
index 7bb839ae18835d128dc9285b7f9dc5b5e06335af..09e3979d07447522ace740daf2b818a6c551ceba 100644
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -632,6 +632,7 @@ float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*);
@@ -625,6 +625,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*);
@ -2229,10 +2229,10 @@ index 327b3d587844c0ad81f5b9c2c622bf52315380bf..d5eefde23dfa3d5666f04af90b7dda1f
const mozilla::dom::Document*);
mozilla::StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme(
diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp
index f10c779512c8d6a778ccd2fe98f069745a58adab..8534639ff3c256820846c6e014185783bb317efe 100644
index c99eecb4867c623b8ccc6e6fb42986d71f795cc0..d127837d7e069818daaeb73ef42497226ff95e26 100644
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -261,11 +261,11 @@ bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) {
@@ -257,11 +257,11 @@ bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) {
}
bool Gecko_MediaFeatures_PrefersReducedMotion(const Document* aDocument) {
@ -2250,10 +2250,10 @@ index f10c779512c8d6a778ccd2fe98f069745a58adab..8534639ff3c256820846c6e014185783
bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document* aDocument) {
diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
index 7e8b75f0156b059c19c9a2322087c2d79d28f961..d8899ca4ec66a1d1cafba5cb09205fa3b7d41384 100644
index b15f24fffd7ea5a8a00319451fa7ebf9df32ec05..0279f6626f2ac938b0456d370fd956f2a23a036b 100644
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -647,7 +647,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
@@ -653,7 +653,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
mInterceptionInfo(rhs.mInterceptionInfo),
mHasInjectedCookieForCookieBannerHandling(
rhs.mHasInjectedCookieForCookieBannerHandling),
@ -2263,7 +2263,7 @@ index 7e8b75f0156b059c19c9a2322087c2d79d28f961..d8899ca4ec66a1d1cafba5cb09205fa3
}
LoadInfo::LoadInfo(
@@ -2363,4 +2364,16 @@ LoadInfo::SetWasSchemelessInput(bool aWasSchemelessInput) {
@@ -2369,4 +2370,16 @@ LoadInfo::SetWasSchemelessInput(bool aWasSchemelessInput) {
return NS_OK;
}
@ -2314,10 +2314,10 @@ index 920e7623a7f912296fc23361f66ab35a30c35f1e..dfea0d0f7a72da9699615d7ff778e429
} // namespace net
} // namespace mozilla
diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl
index ccceadc42ce12bec8e9878124904f0ba3914a030..56ec55af399e7b831558ca5d2a71513d9c919839 100644
index ddfcb223e6126c943b58e9d198f0e2fa767c3de1..4280b836c55e5778ad4c94226ea537facb19c436 100644
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -1531,4 +1531,6 @@ interface nsILoadInfo : nsISupports
@@ -1532,4 +1532,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 wasSchemelessInput;
@ -2325,7 +2325,7 @@ index ccceadc42ce12bec8e9878124904f0ba3914a030..56ec55af399e7b831558ca5d2a71513d
+ [infallible] attribute unsigned long long jugglerLoadIdentifier;
};
diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl
index d72dc570dc82ff9d576942b9e7c23d8a74d68049..a5fcddc4b0e53a862e5a77120b4ccff8a27cfbab 100644
index 155daa5cd52c4e93e1cc4559875868f4faf2c37e..02e8a2614a27a4cc9e1de760d4c48617eba25d4d 100644
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -59,6 +59,7 @@ interface nsIInterceptedChannel : nsISupports
@ -2337,7 +2337,7 @@ 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 995560e4818c69f89959e36164fa9eba39925037..3d92280cfd73c5b09926151aac36857775f48e30 100644
index ca1f59e8847bd399fcf3018ede95d318265c374c..d114f0bcaddedc3553780ff7b97e395e28aff91e 100644
--- a/netwerk/ipc/DocumentLoadListener.cpp
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -167,6 +167,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
@ -2349,7 +2349,7 @@ index 995560e4818c69f89959e36164fa9eba39925037..3d92280cfd73c5b09926151aac368577
return loadInfo.forget();
}
diff --git a/netwerk/protocol/http/InterceptedHttpChannel.cpp b/netwerk/protocol/http/InterceptedHttpChannel.cpp
index 4bee70faf21db77daf381f40a562840470f788f4..93986ccb252bca67f2a0b291c915d523f5dad9eb 100644
index c58fbc96391f8dcb585bd00b5ae8cba9088abd82..c121c891b61c9d60df770020c4ad09521d2bbfe6 100644
--- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
+++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
@@ -727,6 +727,14 @@ NS_IMPL_ISUPPORTS(ResetInterceptionHeaderVisitor, nsIHttpHeaderVisitor)
@ -2367,7 +2367,7 @@ index 4bee70faf21db77daf381f40a562840470f788f4..93986ccb252bca67f2a0b291c915d523
NS_IMETHODIMP
InterceptedHttpChannel::ResetInterception(bool aBypass) {
INTERCEPTED_LOG(("InterceptedHttpChannel::ResetInterception [%p] bypass: %s",
@@ -1070,11 +1078,18 @@ InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) {
@@ -1140,11 +1148,18 @@ InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) {
GetCallback(mProgressSink);
}
@ -2387,10 +2387,10 @@ index 4bee70faf21db77daf381f40a562840470f788f4..93986ccb252bca67f2a0b291c915d523
if (mPump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
index 125728fa73180bbe57bfc1f53379ec637f108eb7..5fd60bebebbfa0f2de99cb14762cf0d52dbdb0cb 100644
index 3cb38dcf28a5cb3a8e70c336c8b1c822be59268f..4d9ae3ab666d4ba7b42730d50367720870f0183f 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -1376,6 +1376,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
@@ -1381,6 +1381,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@ -2475,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 73c83e526be1a3a252f995d0718e3975d50bffa7..4b05141c8ab8b084d977341127f36ea6d226aa9a 100644
index 73c83e526be1a3a252f995d0718e3975d50bffa7..db5977c54221e19e107a8325a0834302c2e84546 100644
--- a/services/settings/Utils.sys.mjs
+++ b/services/settings/Utils.sys.mjs
@@ -95,7 +95,7 @@ function _isUndefined(value) {
@ -2487,8 +2487,18 @@ index 73c83e526be1a3a252f995d0718e3975d50bffa7..4b05141c8ab8b084d977341127f36ea6
? lazy.gServerURL
: AppConstants.REMOTE_SETTINGS_SERVER_URL;
},
@@ -108,6 +108,9 @@ export var Utils = {
log,
get shouldSkipRemoteActivityDueToTests() {
+ // Playwright does not set Cu.isInAutomation, hence we just return true
+ // here in order to disable the remote activity.
+ return true;
return (
(lazy.isRunningTests || Cu.isInAutomation) &&
this.SERVER_URL == "data:,#remote-settings-dummy/v1"
diff --git a/servo/components/style/gecko/media_features.rs b/servo/components/style/gecko/media_features.rs
index 014248a26f7b070850edf1d7db0990fd8ef26a10..9846ae282d959c406f15052791aae3ec9351326f 100644
index c9ad30b28b069a122cf01c5e97b83dc68ef022ff..f32dbcfe26fe2f6b8a0d066d6dfd208f1afc510d 100644
--- a/servo/components/style/gecko/media_features.rs
+++ b/servo/components/style/gecko/media_features.rs
@@ -293,10 +293,15 @@ pub enum ForcedColors {
@ -2511,21 +2521,21 @@ index 014248a26f7b070850edf1d7db0990fd8ef26a10..9846ae282d959c406f15052791aae3ec
}
diff --git a/toolkit/components/browser/nsIWebBrowserChrome.idl b/toolkit/components/browser/nsIWebBrowserChrome.idl
index 54de3abab5757dd706e3d909ccef6a0bed5deacc..f5c5480cd052ede0c76e5eec733dbb9283389045 100644
index 517c87a285dd93220cb2654d6ba7bb05c66cdeb7..e3010421d8dbe5ed5ed72feedb9f15805b32503e 100644
--- a/toolkit/components/browser/nsIWebBrowserChrome.idl
+++ b/toolkit/components/browser/nsIWebBrowserChrome.idl
@@ -71,6 +71,9 @@ interface nsIWebBrowserChrome : nsISupports
@@ -74,6 +74,9 @@ interface nsIWebBrowserChrome : nsISupports
// Whether this window should use out-of-process cross-origin subframes.
const unsigned long CHROME_FISSION_WINDOW = 0x00200000;
const unsigned long CHROME_FISSION_WINDOW = 1 << 21;
+ // Whether this window has "width" or "height" defined in features
+ const unsigned long JUGGLER_WINDOW_EXPLICIT_SIZE = 0x00400000;
+
// Prevents new window animations on MacOS and Windows. Currently
// ignored for Linux.
const unsigned long CHROME_SUPPRESS_ANIMATION = 0x01000000;
const unsigned long CHROME_SUPPRESS_ANIMATION = 1 << 24;
diff --git a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
index 61eda006f090e391b3c0f209e9400920f480c115..8fea8caf17913a2736884caca8f6087f1fb48d15 100644
index 15fa2193c6116b5bd0cf2ee608eb5971f3d24370..aa92eb63b8f907e0df9ef8df60fd076aa3d6a472 100644
--- a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
+++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
@@ -108,6 +108,12 @@ EnterprisePoliciesManager.prototype = {
@ -2570,10 +2580,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 144924a7a6b3d11c0de3992d1e1f99a04f7611b4..b7646200f7843d42ef3fb1919e5a1d3057eaf14c 100644
index 209af157a35cf9c4586424421ee19b3f680796b6..1feb61e2f18e9a13631addc935f00da07739291e 100644
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -1880,7 +1880,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent(
@@ -1853,7 +1853,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent(
// Open a minimal popup.
*aIsPopupRequested = true;
@ -2587,7 +2597,7 @@ index 144924a7a6b3d11c0de3992d1e1f99a04f7611b4..b7646200f7843d42ef3fb1919e5a1d30
/**
diff --git a/toolkit/mozapps/update/UpdateService.sys.mjs b/toolkit/mozapps/update/UpdateService.sys.mjs
index fcb142e5d74ab40515fd6e3b7848a661771d459c..592d4b759958283b1c53c52a648f35f97110705b 100644
index 290344c58bf488b4aa955c2c58bdaa11048e2f48..2d419b90e6c5a910d3038380cd6c819eb8b0c768 100644
--- a/toolkit/mozapps/update/UpdateService.sys.mjs
+++ b/toolkit/mozapps/update/UpdateService.sys.mjs
@@ -3853,6 +3853,8 @@ UpdateService.prototype = {
@ -2600,15 +2610,15 @@ index fcb142e5d74ab40515fd6e3b7848a661771d459c..592d4b759958283b1c53c52a648f35f9
(Cu.isInAutomation ||
lazy.Marionette.running ||
diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild
index 2e3721e8a5807936c02cce6c21240df3b4c92c97..ecd1ca11d96c14ba6db4b09d4d9c10ef757e690b 100644
index f554b692f7e874dd21be1ef783e899ba602ee7f3..dd6c94f8723ca227611c8390d9dcd4f292939703 100644
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -155,6 +155,7 @@ if CONFIG['ENABLE_WEBDRIVER']:
'/remote',
'/testing/firefox-ui',
'/testing/marionette',
+ '/juggler',
'/toolkit/components/telemetry/tests/marionette',
@@ -155,6 +155,7 @@ if CONFIG["ENABLE_WEBDRIVER"]:
"/remote",
"/testing/firefox-ui",
"/testing/marionette",
+ "/juggler",
"/toolkit/components/telemetry/tests/marionette",
]
diff --git a/toolkit/xre/nsWindowsWMain.cpp b/toolkit/xre/nsWindowsWMain.cpp
@ -2647,23 +2657,22 @@ 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 d54be4a4d7697851cc5279a39a49b06c745b7630..abb455116db3011dc7f84df86631da5eb7030700 100644
index fe72a2715da8846146377e719559c16e6ef1f7ff..a5959143bac8f62ee359fa3883a844f3fe541685 100644
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -828,6 +828,13 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout,
("DocLoader:%p: Firing load event for document.open\n",
this));
@@ -813,6 +813,12 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout,
("DocLoader:%p: Firing load event for document.open\n",
this));
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ nsIPrincipal* principal = doc->NodePrincipal();
+ if (!principal->IsSystemPrincipal())
+ os->NotifyObservers(ToSupports(doc), "juggler-document-open-loaded", nullptr);
+ }
+
// This is a very cut-down version of
// nsDocumentViewer::LoadComplete that doesn't do various things
// that are not relevant here because this wasn't an actual
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ nsIPrincipal* principal = doc->NodePrincipal();
+ if (!principal->IsSystemPrincipal())
+ os->NotifyObservers(ToSupports(doc), "juggler-document-open-loaded", nullptr);
+ }
// This is a very cut-down version of
// nsDocumentViewer::LoadComplete that doesn't do various things
// that are not relevant here because this wasn't an actual
diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp
index 4573e28470c5112f5ac2c5dd53e7a9d1ceedb943..b53b86d8e39f1de4b0d0f1a8d5d7295ea050b878 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
@ -2877,7 +2886,7 @@ index 1c25e9d9a101233f71e92288a0f93125b81ac1c5..22cf67b0f6e3ddd2b3ed725a314ba6a9
}
#endif
diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h
index 7ad7d82cd68810d9557adaa31ad5c7cb80bfb079..7a3d0116ce924e1e26a1cc6976abe11ed6f4a62c 100644
index ea714704df53af78a55065ce7626798e0e674a23..a108e0459aeac9789adac7d7ec166abf94646454 100644
--- a/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -258,6 +258,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase,
@ -2926,7 +2935,7 @@ diff --git a/widget/cocoa/NativeKeyBindings.mm b/widget/cocoa/NativeKeyBindings.
index e4bdf715e2fb899e97a5bfeb2e147127460d6047..3554f919480278b7353617481c7ce8050630a1aa 100644
--- a/widget/cocoa/NativeKeyBindings.mm
+++ b/widget/cocoa/NativeKeyBindings.mm
@@ -528,6 +528,13 @@
@@ -528,6 +528,13 @@ void NativeKeyBindings::GetEditCommandsForTests(
break;
case KEY_NAME_INDEX_ArrowLeft:
if (aEvent.IsAlt()) {
@ -2940,7 +2949,7 @@ index e4bdf715e2fb899e97a5bfeb2e147127460d6047..3554f919480278b7353617481c7ce805
break;
}
if (aEvent.IsMeta() || (aEvent.IsControl() && aEvent.IsShift())) {
@@ -550,6 +557,13 @@
@@ -550,6 +557,13 @@ void NativeKeyBindings::GetEditCommandsForTests(
break;
case KEY_NAME_INDEX_ArrowRight:
if (aEvent.IsAlt()) {
@ -2954,7 +2963,7 @@ index e4bdf715e2fb899e97a5bfeb2e147127460d6047..3554f919480278b7353617481c7ce805
break;
}
if (aEvent.IsMeta() || (aEvent.IsControl() && aEvent.IsShift())) {
@@ -572,6 +586,10 @@
@@ -572,6 +586,10 @@ void NativeKeyBindings::GetEditCommandsForTests(
break;
case KEY_NAME_INDEX_ArrowUp:
if (aEvent.IsControl()) {
@ -2965,7 +2974,7 @@ index e4bdf715e2fb899e97a5bfeb2e147127460d6047..3554f919480278b7353617481c7ce805
break;
}
if (aEvent.IsMeta()) {
@@ -582,7 +600,7 @@
@@ -582,7 +600,7 @@ void NativeKeyBindings::GetEditCommandsForTests(
!aEvent.IsShift()
? ToObjcSelectorPtr(@selector(moveToBeginningOfDocument:))
: ToObjcSelectorPtr(
@ -2974,7 +2983,7 @@ index e4bdf715e2fb899e97a5bfeb2e147127460d6047..3554f919480278b7353617481c7ce805
aCommands);
break;
}
@@ -609,6 +627,10 @@
@@ -609,6 +627,10 @@ void NativeKeyBindings::GetEditCommandsForTests(
break;
case KEY_NAME_INDEX_ArrowDown:
if (aEvent.IsControl()) {
@ -2985,31 +2994,6 @@ 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<nsIWidget> hiddenWindowWidget;
+ nsCOMPtr<nsIWidget> 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
@ -3202,7 +3186,7 @@ index 9856991ef32f25f51942f8cd664a09bec2192c70..948947a421179e91c51005aeb83ed0d1
~HeadlessWidget();
bool mEnabled;
diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h
index a3d7aaf1b6c651fedbab7875c1a38647103d08ed..ac3b09e50216524cce90d96b013007f7b07472da 100644
index 8ba46829357fc4acc47bf20842fd869902efa000..a1b5b2c5230d90981bd563d4df2d2bf1c2e05cef 100644
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -234,6 +234,7 @@ struct ParamTraits<mozilla::WidgetMouseEvent> {

View file

@ -15,6 +15,9 @@ pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
// Force pdfs into downloads.
pref("pdfjs.disabled", true);
// This preference breaks our authentication flow.
pref("network.auth.use_redirect_for_retries", false);
// Disable cross-process iframes, but not cross-process navigations.
pref("fission.webContentIsolationStrategy", 0);
@ -109,9 +112,20 @@ pref("prompts.contentPromptSubDialog", false);
// Do not use system colors - they are affected by themes.
pref("ui.use_standins_for_native_colors", true);
// Turn off the Push service.
pref("dom.push.serverURL", "");
// This setting breaks settings loading.
// Prevent Remote Settings (firefox.settings.services.mozilla.com) to issue non local connections.
pref("services.settings.server", "");
// Prevent location.services.mozilla.com to issue non local connections.
pref("browser.region.network.url", "");
pref("browser.pocket.enabled", false);
pref("browser.newtabpage.activity-stream.feeds.topsites", false);
// Disable sponsored tiles from "Mozilla Tiles Service"
pref("browser.newtabpage.activity-stream.showSponsoredTopSites", false);
// required to prevent non-local access to push.services.mozilla.com
pref("dom.push.connection.enabled", false);
// Prevent contile.services.mozilla.com to issue non local connections.
pref("browser.topsites.contile.enabled", false);
pref("browser.safebrowsing.provider.mozilla.updateURL", "");
pref("browser.library.activity-stream.enabled", false);
pref("browser.search.geoSpecificDefaults", false);
@ -308,3 +322,6 @@ pref("devtools.toolbox.host", "window");
// Disable auto translations
pref("browser.translations.enable", false);
// Disable spell check
pref("layout.spellcheckDefault", 0);

View file

@ -1,3 +1,3 @@
REMOTE_URL="https://github.com/WebKit/WebKit.git"
BASE_BRANCH="main"
BASE_REVISION="3db3a794a844d2c7e4cda8fc6a7588f8e62ee85a"
BASE_REVISION="e225c278f4c06f451ea92cc68b12986dd2a99979"

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@ A few examples of problems this can catch include:
The following examples rely on the [`@axe-core/playwright`](https://npmjs.org/@axe-core/playwright) package which adds support for running the [axe accessibility testing engine](https://www.deque.com/axe/) as part of your Playwright tests.
:::note Disclaimer
:::note[Disclaimer]
Automated accessibility tests can detect some common accessibility problems such as missing or invalid properties. But many accessibility problems can only be discovered through manual testing. We recommend using a combination of automated testing, manual accessibility assessments, and inclusive user testing.
For manual assessments, we recommend [Accessibility Insights for Web](https://accessibilityinsights.io/docs/web/overview/?referrer=playwright-accessibility-testing-js), a free and open source dev tool that walks you through assessing a website for [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/?currentsidebar=%23col_customize&levels=aaa) coverage.

View file

@ -159,7 +159,10 @@ context cookies from the response. The method will automatically follow redirect
### option: APIRequestContext.delete.form = %%-csharp-fetch-option-form-%%
* since: v1.17
### option: APIRequestContext.delete.multipart = %%-js-python-fetch-option-multipart-%%
### option: APIRequestContext.delete.multipart = %%-js-fetch-option-multipart-%%
* since: v1.17
### option: APIRequestContext.delete.multipart = %%-python-fetch-option-multipart-%%
* since: v1.17
### option: APIRequestContext.delete.multipart = %%-csharp-fetch-option-multipart-%%
@ -187,10 +190,12 @@ All responses returned by [`method: APIRequestContext.get`] and similar methods
- returns: <[APIResponse]>
Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
context cookies from the response. The method will automatically follow redirects. JSON objects can be passed directly to the request.
context cookies from the response. The method will automatically follow redirects.
**Usage**
JSON objects can be passed directly to the request:
```js
await request.fetch('https://example.com/api/createBook', {
method: 'post',
@ -224,28 +229,17 @@ var data = new Dictionary<string, object>() {
await Request.FetchAsync("https://example.com/api/createBook", new() { Method = "post", DataObject = data });
```
The common way to send file(s) in the body of a request is to encode it as form fields with `multipart/form-data` encoding. You can achieve that with Playwright API like this:
The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` encoding. Use [FormData] to construct request body and pass it to the request as [`option: multipart`] parameter:
```js
// Open file as a stream and pass it to the request:
const stream = fs.createReadStream('team.csv');
await request.fetch('https://example.com/api/uploadTeamList', {
method: 'post',
multipart: {
fileField: stream
}
});
// Or you can pass the file content directly as an object:
await request.fetch('https://example.com/api/uploadScript', {
method: 'post',
multipart: {
fileField: {
name: 'f.js',
mimeType: 'text/javascript',
buffer: Buffer.from('console.log(2022);')
}
}
const form = new FormData();
form.set('name', 'John');
form.append('name', 'Doe');
// Send two file fields with the same name.
form.append('file', new File(['console.log(2024);'], 'f1.js', { type: 'text/javascript' }));
form.append('file', new File(['hello'], 'f2.txt', { type: 'text/plain' }));
await request.fetch('https://example.com/api/uploadForm', {
multipart: form
});
```
@ -259,15 +253,14 @@ APIResponse response = request.fetch("https://example.com/api/uploadTeamList",
// Or you can pass the file content directly as FilePayload object:
FilePayload filePayload = new FilePayload("f.js", "text/javascript",
"console.log(2022);".getBytes(StandardCharsets.UTF_8));
APIResponse response = request.fetch("https://example.com/api/uploadTeamList",
APIResponse response = request.fetch("https://example.com/api/uploadScript",
RequestOptions.create().setMethod("post").setMultipart(
FormData.create().set("fileField", filePayload)));
```
```python
api_request_context.fetch(
"https://example.com/api/uploadScrip'",
method="post",
"https://example.com/api/uploadScript", method="post",
multipart={
"fileField": {
"name": "f.js",
@ -324,7 +317,10 @@ If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/
### option: APIRequestContext.fetch.form = %%-csharp-fetch-option-form-%%
* since: v1.16
### option: APIRequestContext.fetch.multipart = %%-js-python-fetch-option-multipart-%%
### option: APIRequestContext.fetch.multipart = %%-js-fetch-option-multipart-%%
* since: v1.16
### option: APIRequestContext.fetch.multipart = %%-python-fetch-option-multipart-%%
* since: v1.16
### option: APIRequestContext.fetch.multipart = %%-csharp-fetch-option-multipart-%%
@ -410,7 +406,10 @@ await request.GetAsync("https://example.com/api/getText", new() { Params = query
### option: APIRequestContext.get.form = %%-csharp-fetch-option-form-%%
* since: v1.26
### option: APIRequestContext.get.multipart = %%-js-python-fetch-option-multipart-%%
### option: APIRequestContext.get.multipart = %%-js-fetch-option-multipart-%%
* since: v1.26
### option: APIRequestContext.get.multipart = %%-python-fetch-option-multipart-%%
* since: v1.26
### option: APIRequestContext.get.multipart = %%-csharp-fetch-option-multipart-%%
@ -460,7 +459,10 @@ context cookies from the response. The method will automatically follow redirect
### option: APIRequestContext.head.form = %%-csharp-fetch-option-form-%%
* since: v1.26
### option: APIRequestContext.head.multipart = %%-js-python-fetch-option-multipart-%%
### option: APIRequestContext.head.multipart = %%-js-fetch-option-multipart-%%
* since: v1.26
### option: APIRequestContext.head.multipart = %%-python-fetch-option-multipart-%%
* since: v1.26
### option: APIRequestContext.head.multipart = %%-csharp-fetch-option-multipart-%%
@ -510,7 +512,10 @@ context cookies from the response. The method will automatically follow redirect
### option: APIRequestContext.patch.form = %%-csharp-fetch-option-form-%%
* since: v1.16
### option: APIRequestContext.patch.multipart = %%-js-python-fetch-option-multipart-%%
### option: APIRequestContext.patch.multipart = %%-js-fetch-option-multipart-%%
* since: v1.16
### option: APIRequestContext.patch.multipart = %%-python-fetch-option-multipart-%%
* since: v1.16
### option: APIRequestContext.patch.multipart = %%-csharp-fetch-option-multipart-%%
@ -566,7 +571,7 @@ api_request_context.post("https://example.com/api/createBook", data=data)
```csharp
var data = new Dictionary<string, object>() {
{ "firstNam", "John" },
{ "firstName", "John" },
{ "lastName", "Doe" }
};
await request.PostAsync("https://example.com/api/createBook", new() { DataObject = data });
@ -604,26 +609,17 @@ formData.Set("body", "John Doe");
await request.PostAsync("https://example.com/api/findBook", new() { Form = formData });
```
The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` encoding. You can achieve that with Playwright API like this:
The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data` encoding. Use [FormData] to construct request body and pass it to the request as `multipart` parameter:
```js
// Open file as a stream and pass it to the request:
const stream = fs.createReadStream('team.csv');
await request.post('https://example.com/api/uploadTeamList', {
multipart: {
fileField: stream
}
});
// Or you can pass the file content directly as an object:
await request.post('https://example.com/api/uploadScript', {
multipart: {
fileField: {
name: 'f.js',
mimeType: 'text/javascript',
buffer: Buffer.from('console.log(2022);')
}
}
const form = new FormData();
form.set('name', 'John');
form.append('name', 'Doe');
// Send two file fields with the same name.
form.append('file', new File(['console.log(2024);'], 'f1.js', { type: 'text/javascript' }));
form.append('file', new File(['hello'], 'f2.txt', { type: 'text/plain' }));
await request.post('https://example.com/api/uploadForm', {
multipart: form
});
```
@ -635,16 +631,16 @@ APIResponse response = request.post("https://example.com/api/uploadTeamList",
FormData.create().set("fileField", file)));
// Or you can pass the file content directly as FilePayload object:
FilePayload filePayload = new FilePayload("f.js", "text/javascript",
FilePayload filePayload1 = new FilePayload("f1.js", "text/javascript",
"console.log(2022);".getBytes(StandardCharsets.UTF_8));
APIResponse response = request.post("https://example.com/api/uploadTeamList",
APIResponse response = request.post("https://example.com/api/uploadScript",
RequestOptions.create().setMultipart(
FormData.create().set("fileField", filePayload)));
```
```python
api_request_context.post(
"https://example.com/api/uploadScrip'",
"https://example.com/api/uploadScript'",
multipart={
"fileField": {
"name": "f.js",
@ -690,7 +686,10 @@ await request.PostAsync("https://example.com/api/uploadScript", new() { Multipar
### option: APIRequestContext.post.form = %%-csharp-fetch-option-form-%%
* since: v1.16
### option: APIRequestContext.post.multipart = %%-js-python-fetch-option-multipart-%%
### option: APIRequestContext.post.multipart = %%-js-fetch-option-multipart-%%
* since: v1.16
### option: APIRequestContext.post.multipart = %%-python-fetch-option-multipart-%%
* since: v1.16
### option: APIRequestContext.post.multipart = %%-csharp-fetch-option-multipart-%%
@ -740,7 +739,10 @@ context cookies from the response. The method will automatically follow redirect
### option: APIRequestContext.put.form = %%-csharp-fetch-option-form-%%
* since: v1.16
### option: APIRequestContext.put.multipart = %%-js-python-fetch-option-multipart-%%
### option: APIRequestContext.put.multipart = %%-js-fetch-option-multipart-%%
* since: v1.16
### option: APIRequestContext.put.multipart = %%-python-fetch-option-multipart-%%
* since: v1.16
### option: APIRequestContext.put.multipart = %%-csharp-fetch-option-multipart-%%

View file

@ -64,7 +64,6 @@ await context.CloseAsync();
## event: BrowserContext.backgroundPage
* since: v1.11
* langs: js, python
- argument: <[Page]>
:::note
@ -73,6 +72,12 @@ Only works with Chromium browser's persistent context.
Emitted when new background page is created in the context.
```java
context.onBackgroundPage(backgroundPage -> {
System.out.println(backgroundPage.url());
});
```
```js
const backgroundPage = await context.waitForEvent('backgroundpage');
```
@ -85,6 +90,14 @@ background_page = await context.wait_for_event("backgroundpage")
background_page = context.wait_for_event("backgroundpage")
```
```csharp
context.BackgroundPage += (_, backgroundPage) =>
{
Console.WriteLine(backgroundPage.Url);
};
```
## event: BrowserContext.close
* since: v1.8
- argument: <[BrowserContext]>
@ -205,7 +218,7 @@ also fire for popup pages. See also [`event: Page.popup`] to receive events abou
The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a
popup with `window.open('http://example.com')`, this event will fire when the network request to "http://example.com" is
done and its response has started loading in the popup.
done and its response has started loading in the popup. If you would like to route/listen to this network request, use [`method: BrowserContext.route`] and [`event: BrowserContext.request`] respectively instead of similar methods on the [Page].
```js
const newPagePromise = context.waitForEvent('page');
@ -441,7 +454,6 @@ Script to be evaluated in all pages in the browser context. Optional.
## method: BrowserContext.backgroundPages
* since: v1.11
* langs: js, python
- returns: <[Array]<[Page]>>
:::note
@ -459,7 +471,71 @@ Returns the browser instance of the context. If it was launched as a persistent
## async method: BrowserContext.clearCookies
* since: v1.8
Clears context cookies.
Removes cookies from context. Accepts optional filter.
**Usage**
```js
await context.clearCookies();
await context.clearCookies({ name: 'session-id' });
await context.clearCookies({ domain: 'my-origin.com' });
await context.clearCookies({ domain: /.*my-origin\.com/ });
await context.clearCookies({ path: '/api/v1' });
await context.clearCookies({ name: 'session-id', domain: 'my-origin.com' });
```
```java
context.clearCookies();
context.clearCookies(new BrowserContext.ClearCookiesOptions().setName("session-id"));
context.clearCookies(new BrowserContext.ClearCookiesOptions().setDomain("my-origin.com"));
context.clearCookies(new BrowserContext.ClearCookiesOptions().setPath("/api/v1"));
context.clearCookies(new BrowserContext.ClearCookiesOptions()
.setName("session-id")
.setDomain("my-origin.com"));
```
```python async
await context.clear_cookies()
await context.clear_cookies(name="session-id")
await context.clear_cookies(domain="my-origin.com")
await context.clear_cookies(path="/api/v1")
await context.clear_cookies(name="session-id", domain="my-origin.com")
```
```python sync
context.clear_cookies()
context.clear_cookies(name="session-id")
context.clear_cookies(domain="my-origin.com")
context.clear_cookies(path="/api/v1")
context.clear_cookies(name="session-id", domain="my-origin.com")
```
```csharp
await context.ClearCookiesAsync();
await context.ClearCookiesAsync(new() { Name = "session-id" });
await context.ClearCookiesAsync(new() { Domain = "my-origin.com" });
await context.ClearCookiesAsync(new() { Path = "/api/v1" });
await context.ClearCookiesAsync(new() { Name = "session-id", Domain = "my-origin.com" });
```
### option: BrowserContext.clearCookies.name
* since: v1.43
- `name` <[string]|[RegExp]>
Only removes cookies with the given name.
### option: BrowserContext.clearCookies.domain
* since: v1.43
- `domain` <[string]|[RegExp]>
Only removes cookies with the given domain.
### option: BrowserContext.clearCookies.path
* since: v1.43
- `path` <[string]|[RegExp]>
Only removes cookies with the given path.
## async method: BrowserContext.clearPermissions
* since: v1.8
@ -1013,27 +1089,6 @@ Creates a new page in the browser context.
Returns all open pages in the context.
## async method: BrowserContext.removeCookies
* since: v1.43
Removes cookies from context. At least one of the removal criteria should be provided.
**Usage**
```js
await browserContext.removeCookies({ name: 'session-id' });
await browserContext.removeCookies({ domain: 'my-origin.com' });
await browserContext.removeCookies({ path: '/api/v1' });
await browserContext.removeCookies({ name: 'session-id', domain: 'my-origin.com' });
```
### param: BrowserContext.removeCookies.filter
* since: v1.43
- `filter` <[Object]>
- `name` ?<[string]>
- `domain` ?<[string]>
- `path` ?<[string]>
## property: BrowserContext.request
* since: v1.16
* langs:

View file

@ -30,7 +30,7 @@ client.on("Animation.animationCreated", lambda: print("animation created!"))
response = await client.send("Animation.getPlaybackRate")
print("playback rate is " + str(response["playbackRate"]))
await client.send("Animation.setPlaybackRate", {
playbackRate: response["playbackRate"] / 2
"playbackRate": response["playbackRate"] / 2
})
```
@ -41,7 +41,7 @@ client.on("Animation.animationCreated", lambda: print("animation created!"))
response = client.send("Animation.getPlaybackRate")
print("playback rate is " + str(response["playbackRate"]))
client.send("Animation.setPlaybackRate", {
playbackRate: response["playbackRate"] / 2
"playbackRate": response["playbackRate"] / 2
})
```
```csharp

View file

@ -4,7 +4,7 @@
ElementHandle represents an in-page DOM element. ElementHandles can be created with the [`method: Page.querySelector`] method.
:::caution Discouraged
:::warning[Discouraged]
The use of ElementHandle is discouraged, use [Locator] objects and web-first assertions instead.
:::
@ -692,7 +692,7 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

View file

@ -6,9 +6,6 @@
Error is raised whenever certain operations are terminated abnormally, e.g.
browser closes while [`method: Page.evaluate`] is running. All Playwright exceptions
inherit from this class.
- [error.message](./class-error.md#errormessage)
- [error.name](./class-error.md#errorname)
- [error.stack](./class-error.md#errorstack)
## property: Error.message
* since: v1.11

View file

@ -14,6 +14,77 @@ FormData form = FormData.create()
page.request().post("http://localhost/submit", RequestOptions.create().setForm(form));
```
## method: FormData.append
* since: v1.44
- returns: <[FormData]>
Appends a new value onto an existing key inside a FormData object, or adds the key if it
does not already exist. File values can be passed either as `Path` or as `FilePayload`.
Multiple fields with the same name can be added.
The difference between [`method: FormData.set`] and [`method: FormData.append`] is that if the specified key already exists,
[`method: FormData.set`] will overwrite all existing values with the new one, whereas [`method: FormData.append`] will append
the new value onto the end of the existing set of values.
```java
import com.microsoft.playwright.options.FormData;
...
FormData form = FormData.create()
// Only name and value are set.
.append("firstName", "John")
// Name and value are set, filename and Content-Type are inferred from the file path.
.append("attachment", Paths.get("pic.jpg"))
// Name, value, filename and Content-Type are set.
.append("attachment", new FilePayload("table.csv", "text/csv", Files.readAllBytes(Paths.get("my-tble.csv"))));
page.request().post("http://localhost/submit", RequestOptions.create().setForm(form));
```
```csharp
var multipart = Context.APIRequest.CreateFormData();
// Only name and value are set.
multipart.Append("firstName", "John");
// Name, value, filename and Content-Type are set.
multipart.Append("attachment", new FilePayload()
{
Name = "pic.jpg",
MimeType = "image/jpeg",
Buffer = File.ReadAllBytes("john.jpg")
});
// Name, value, filename and Content-Type are set.
multipart.Append("attachment", new FilePayload()
{
Name = "table.csv",
MimeType = "text/csv",
Buffer = File.ReadAllBytes("my-tble.csv")
});
await Page.APIRequest.PostAsync("https://localhost/submit", new() { Multipart = multipart });
```
### param: FormData.append.name
* since: v1.44
- `name` <[string]>
Field name.
### param: FormData.append.value
* since: v1.44
- `value` <[string]|[boolean]|[int]|[Path]|[Object]>
- `name` <[string]> File name
- `mimeType` <[string]> File type
- `buffer` <[Buffer]> File content
Field value.
### param: FormData.append.value
* since: v1.44
* langs: csharp
- `value` <[string]|[boolean]|[int]|[Object]>
- `name` <[string]> File name
- `mimeType` <[string]> File type
- `buffer` <[Buffer]> File content
Field value.
## method: FormData.create
* since: v1.18
* langs: java
@ -36,7 +107,7 @@ FormData form = FormData.create()
// Name and value are set, filename and Content-Type are inferred from the file path.
.set("profilePicture1", Paths.get("john.jpg"))
// Name, value, filename and Content-Type are set.
.set("profilePicture2", new FilePayload("john.jpg", "image/jpeg", Files.readAllBytes(Paths.get("john.jpg"))));
.set("profilePicture2", new FilePayload("john.jpg", "image/jpeg", Files.readAllBytes(Paths.get("john.jpg"))))
.set("age", 30);
page.request().post("http://localhost/submit", RequestOptions.create().setForm(form));
```
@ -52,6 +123,7 @@ multipart.Set("profilePicture", new FilePayload()
MimeType = "image/jpeg",
Buffer = File.ReadAllBytes("john.jpg")
});
multipart.Set("age", 30);
await Page.APIRequest.PostAsync("https://localhost/submit", new() { Multipart = multipart });
```

View file

@ -1030,7 +1030,8 @@ Attribute name to get the value for.
%%-template-locator-get-by-role-%%
### param: Frame.getByRole.role = %%-locator-get-by-role-role-%%
### param: Frame.getByRole.role = %%-get-by-role-to-have-role-role-%%
* since: v1.27
### option: Frame.getByRole.-inline- = %%-locator-get-by-role-option-list-v1.27-%%
* since: v1.27
@ -1393,7 +1394,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.
Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.
@ -1970,6 +1972,10 @@ Waits for the required load state to be reached.
This returns when the frame reaches a required load state, `load` by default. The navigation must have been committed
when this method is called. If current document has already reached the required state, resolves immediately.
:::note
Most of the time, this method is not needed because Playwright [auto-waits before every action](../actionability.md).
:::
**Usage**
```js

View file

@ -74,27 +74,12 @@ await page.FrameLocator(".result-frame").First.getByRole(AriaRole.Button).ClickA
**Converting Locator to FrameLocator**
If you have a [Locator] object pointing to an `iframe` it can be converted to [FrameLocator] using [`:scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/:scope) CSS selector:
If you have a [Locator] object pointing to an `iframe` it can be converted to [FrameLocator] using [`method: Locator.contentFrame`].
```js
const frameLocator = locator.frameLocator(':scope');
```
**Converting FrameLocator to Locator**
```java
Locator frameLocator = locator.frameLocator(':scope');
```
If you have a [FrameLocator] object it can be converted to [Locator] pointing to the same `iframe` using [`method: FrameLocator.owner`].
```python async
frameLocator = locator.frame_locator(":scope")
```
```python sync
frameLocator = locator.frame_locator(":scope")
```
```csharp
var frameLocator = locator.FrameLocator(":scope");
```
## method: FrameLocator.first
* since: v1.17
@ -148,7 +133,8 @@ in that iframe.
%%-template-locator-get-by-role-%%
### param: FrameLocator.getByRole.role = %%-locator-get-by-role-role-%%
### param: FrameLocator.getByRole.role = %%-get-by-role-to-have-role-role-%%
* since: v1.27
### option: FrameLocator.getByRole.-inline- = %%-locator-get-by-role-option-list-v1.27-%%
* since: v1.27
@ -217,3 +203,51 @@ Returns locator to the n-th matching frame. It's zero based, `nth(0)` selects th
### param: FrameLocator.nth.index
* since: v1.17
- `index` <[int]>
## method: FrameLocator.owner
* since: v1.43
- returns: <[Locator]>
Returns a [Locator] object pointing to the same `iframe` as this frame locator.
Useful when you have a [FrameLocator] object obtained somewhere, and later on would like to interact with the `iframe` element.
For a reverse operation, use [`method: Locator.contentFrame`].
**Usage**
```js
const frameLocator = page.frameLocator('iframe[name="embedded"]');
// ...
const locator = frameLocator.owner();
await expect(locator).toBeVisible();
```
```java
FrameLocator frameLocator = page.frameLocator("iframe[name=\"embedded\"]");
// ...
Locator locator = frameLocator.owner();
assertThat(locator).isVisible();
```
```python async
frame_locator = page.frame_locator("iframe[name=\"embedded\"]")
# ...
locator = frame_locator.owner
await expect(locator).to_be_visible()
```
```python sync
frame_locator = page.frame_locator("iframe[name=\"embedded\"]")
# ...
locator = frame_locator.owner
expect(locator).to_be_visible()
```
```csharp
var frameLocator = Page.FrameLocator("iframe[name=\"embedded\"]");
// ...
var locator = frameLocator.Owner;
await Expect(locator).ToBeVisibleAsync();
```

View file

@ -151,7 +151,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.
Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.
@ -227,7 +228,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.
Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

View file

@ -53,7 +53,7 @@ foreach (var li in await page.GetByRole("listitem").AllAsync())
Returns an array of `node.innerText` values for all matching nodes.
:::caution Asserting text
:::warning[Asserting text]
If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] with [`option: useInnerText`] option to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -85,7 +85,7 @@ var texts = await page.GetByRole(AriaRole.Link).AllInnerTextsAsync();
Returns an array of `node.textContent` values for all matching nodes.
:::caution Asserting text
:::warning[Asserting text]
If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -443,7 +443,7 @@ await page.Locator("canvas").ClickAsync(new() {
Returns the number of elements matching the locator.
:::caution Asserting count
:::warning[Asserting count]
If you need to assert the number of elements on the page, prefer [`method: LocatorAssertions.toHaveCount`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -747,6 +747,53 @@ Resolves given locator to the first matching DOM element. If there are no matchi
Resolves given locator to all matching DOM elements. If there are no matching elements, returns an empty list.
## method: Locator.contentFrame
* since: v1.43
- returns: <[FrameLocator]>
Returns a [FrameLocator] object pointing to the same `iframe` as this locator.
Useful when you have a [Locator] object obtained somewhere, and later on would like to interact with the content inside the frame.
For a reverse operation, use [`method: FrameLocator.owner`].
**Usage**
```js
const locator = page.locator('iframe[name="embedded"]');
// ...
const frameLocator = locator.contentFrame();
await frameLocator.getByRole('button').click();
```
```java
Locator locator = page.locator("iframe[name=\"embedded\"]");
// ...
FrameLocator frameLocator = locator.contentFrame();
frameLocator.getByRole(AriaRole.BUTTON).click();
```
```python async
locator = page.locator("iframe[name=\"embedded\"]")
# ...
frame_locator = locator.content_frame
await frame_locator.get_by_role("button").click()
```
```python sync
locator = page.locator("iframe[name=\"embedded\"]")
# ...
frame_locator = locator.content_frame
frame_locator.get_by_role("button").click()
```
```csharp
var locator = Page.Locator("iframe[name=\"embedded\"]");
// ...
var frameLocator = locator.ContentFrame;
await frameLocator.GetByRole(AriaRole.Button).ClickAsync();
```
## async method: Locator.evaluate
* since: v1.14
- returns: <[Serializable]>
@ -1074,7 +1121,7 @@ await locator.ClickAsync();
Returns the matching element's attribute value.
:::caution Asserting attributes
:::warning[Asserting attributes]
If you need to assert an element's attribute, prefer [`method: LocatorAssertions.toHaveAttribute`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1126,7 +1173,8 @@ Attribute name to get the value for.
%%-template-locator-get-by-role-%%
### param: Locator.getByRole.role = %%-locator-get-by-role-role-%%
### param: Locator.getByRole.role = %%-get-by-role-to-have-role-role-%%
* since: v1.27
### option: Locator.getByRole.-inline- = %%-locator-get-by-role-option-list-v1.27-%%
* since: v1.27
@ -1246,7 +1294,7 @@ Returns the [`element.innerHTML`](https://developer.mozilla.org/en-US/docs/Web/A
Returns the [`element.innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText).
:::caution Asserting text
:::warning[Asserting text]
If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] with [`option: useInnerText`] option to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1262,7 +1310,7 @@ If you need to assert text on the page, prefer [`method: LocatorAssertions.toHav
Returns the value for the matching `<input>` or `<textarea>` or `<select>` element.
:::caution Asserting value
:::warning[Asserting value]
If you need to assert input value, prefer [`method: LocatorAssertions.toHaveValue`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1304,7 +1352,7 @@ Throws elements that are not an input, textarea or a select. However, if the ele
Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
:::caution Asserting checked state
:::warning[Asserting checked state]
If you need to assert that checkbox is checked, prefer [`method: LocatorAssertions.toBeChecked`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1342,7 +1390,7 @@ var isChecked = await page.GetByRole(AriaRole.Checkbox).IsCheckedAsync();
Returns whether the element is disabled, the opposite of [enabled](../actionability.md#enabled).
:::caution Asserting disabled state
:::warning[Asserting disabled state]
If you need to assert that an element is disabled, prefer [`method: LocatorAssertions.toBeDisabled`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1380,7 +1428,7 @@ Boolean disabled = await page.GetByRole(AriaRole.Button).IsDisabledAsync();
Returns whether the element is [editable](../actionability.md#editable).
:::caution Asserting editable state
:::warning[Asserting editable state]
If you need to assert that an element is editable, prefer [`method: LocatorAssertions.toBeEditable`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1418,7 +1466,7 @@ Boolean editable = await page.GetByRole(AriaRole.Textbox).IsEditableAsync();
Returns whether the element is [enabled](../actionability.md#enabled).
:::caution Asserting enabled state
:::warning[Asserting enabled state]
If you need to assert that an element is enabled, prefer [`method: LocatorAssertions.toBeEnabled`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1456,7 +1504,7 @@ Boolean enabled = await page.GetByRole(AriaRole.Button).IsEnabledAsync();
Returns whether the element is hidden, the opposite of [visible](../actionability.md#visible).
:::caution Asserting visibility
:::warning[Asserting visibility]
If you need to assert that element is hidden, prefer [`method: LocatorAssertions.toBeHidden`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1493,7 +1541,7 @@ Boolean hidden = await page.GetByRole(AriaRole.Button).IsHiddenAsync();
Returns whether the element is [visible](../actionability.md#visible).
:::caution Asserting visibility
:::warning[Asserting visibility]
If you need to assert that element is visible, prefer [`method: LocatorAssertions.toBeVisible`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::
@ -1713,7 +1761,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.
Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.
@ -2277,7 +2326,7 @@ When all steps combined have not finished during the specified [`option: timeout
Returns the [`node.textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent).
:::caution Asserting text
:::warning[Asserting text]
If you need to assert text on the page, prefer [`method: LocatorAssertions.toHaveText`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
:::

View file

@ -211,11 +211,8 @@ The opposite of [`method: LocatorAssertions.toContainText`].
Expected substring or RegExp or a list of those.
### option: LocatorAssertions.NotToContainText.ignoreCase
### option: LocatorAssertions.NotToContainText.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.23
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
### option: LocatorAssertions.NotToContainText.useInnerText
* since: v1.18
@ -226,6 +223,45 @@ Whether to use `element.innerText` instead of `element.textContent` when retriev
### option: LocatorAssertions.NotToContainText.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18
## async method: LocatorAssertions.NotToHaveAccessibleDescription
* since: v1.44
* langs: python
The opposite of [`method: LocatorAssertions.toHaveAccessibleDescription`].
### param: LocatorAssertions.NotToHaveAccessibleDescription.name
* since: v1.44
- `name` <[string]|[RegExp]>
Expected accessible name.
### option: LocatorAssertions.NotToHaveAccessibleDescription.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.44
### option: LocatorAssertions.NotToHaveAccessibleDescription.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.44
## async method: LocatorAssertions.NotToHaveAccessibleName
* since: v1.44
* langs: python
The opposite of [`method: LocatorAssertions.toHaveAccessibleName`].
### param: LocatorAssertions.NotToHaveAccessibleName.name
* since: v1.44
- `name` <[string]|[RegExp]>
Expected accessible name.
### option: LocatorAssertions.NotToHaveAccessibleName.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.44
### option: LocatorAssertions.NotToHaveAccessibleName.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.44
## async method: LocatorAssertions.NotToHaveAttribute
* since: v1.20
* langs: python
@ -244,11 +280,8 @@ Attribute name.
Expected attribute value.
### option: LocatorAssertions.NotToHaveAttribute.ignoreCase
### option: LocatorAssertions.NotToHaveAttribute.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.40
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
### option: LocatorAssertions.NotToHaveAttribute.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18
@ -340,6 +373,23 @@ Property value.
### option: LocatorAssertions.NotToHaveJSProperty.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18
## async method: LocatorAssertions.NotToHaveRole
* since: v1.44
* langs: python
The opposite of [`method: LocatorAssertions.toHaveRole`].
### param: LocatorAssertions.NotToHaveRole.name
* since: v1.44
- `name` <[string]|[RegExp]>
Expected accessible name.
### option: LocatorAssertions.NotToHaveRole.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.44
## async method: LocatorAssertions.NotToHaveText
* since: v1.20
* langs: python
@ -352,11 +402,8 @@ The opposite of [`method: LocatorAssertions.toHaveText`].
Expected string or RegExp or a list of those.
### option: LocatorAssertions.NotToHaveText.ignoreCase
### option: LocatorAssertions.NotToHaveText.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.23
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
### option: LocatorAssertions.NotToHaveText.useInnerText
* since: v1.18
@ -1089,11 +1136,8 @@ Expected substring or RegExp or a list of those.
Expected substring or RegExp or a list of those.
### option: LocatorAssertions.toContainText.ignoreCase
### option: LocatorAssertions.toContainText.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.23
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
### option: LocatorAssertions.toContainText.useInnerText
* since: v1.18
@ -1107,6 +1151,107 @@ Whether to use `element.innerText` instead of `element.textContent` when retriev
### option: LocatorAssertions.toContainText.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18
## async method: LocatorAssertions.toHaveAccessibleDescription
* since: v1.44
* langs:
- alias-java: hasAccessibleDescription
Ensures the [Locator] points to an element with a given [accessible description](https://w3c.github.io/accname/#dfn-accessible-description).
**Usage**
```js
const locator = page.getByTestId('save-button');
await expect(locator).toHaveAccessibleDescription('Save results to disk');
```
```java
Locator locator = page.getByTestId("save-button");
assertThat(locator).hasAccessibleDescription("Save results to disk");
```
```python async
locator = page.get_by_test_id("save-button")
await expect(locator).to_have_accessible_description("Save results to disk")
```
```python sync
locator = page.get_by_test_id("save-button")
expect(locator).to_have_accessible_description("Save results to disk")
```
```csharp
var locator = Page.GetByTestId("save-button");
await Expect(locator).toHaveAccessibleDescriptionAsync("Save results to disk");
```
### param: LocatorAssertions.toHaveAccessibleDescription.description
* since: v1.44
- `description` <[string]|[RegExp]>
Expected accessible description.
### option: LocatorAssertions.toHaveAccessibleDescription.timeout = %%-js-assertions-timeout-%%
* since: v1.44
### option: LocatorAssertions.toHaveAccessibleDescription.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.44
### option: LocatorAssertions.toHaveAccessibleDescription.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.44
## async method: LocatorAssertions.toHaveAccessibleName
* since: v1.44
* langs:
- alias-java: hasAccessibleName
Ensures the [Locator] points to an element with a given [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
**Usage**
```js
const locator = page.getByTestId('save-button');
await expect(locator).toHaveAccessibleName('Save to disk');
```
```java
Locator locator = page.getByTestId("save-button");
assertThat(locator).hasAccessibleName("Save to disk");
```
```python async
locator = page.get_by_test_id("save-button")
await expect(locator).to_have_accessible_name("Save to disk")
```
```python sync
locator = page.get_by_test_id("save-button")
expect(locator).to_have_accessible_name("Save to disk")
```
```csharp
var locator = Page.GetByTestId("save-button");
await Expect(locator).toHaveAccessibleNameAsync("Save to disk");
```
### param: LocatorAssertions.toHaveAccessibleName.name
* since: v1.44
- `name` <[string]|[RegExp]>
Expected accessible name.
### option: LocatorAssertions.toHaveAccessibleName.timeout = %%-js-assertions-timeout-%%
* since: v1.44
### option: LocatorAssertions.toHaveAccessibleName.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.44
### option: LocatorAssertions.toHaveAccessibleName.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.44
## async method: LocatorAssertions.toHaveAttribute
* since: v1.20
* langs:
@ -1162,11 +1307,8 @@ Expected attribute value.
### option: LocatorAssertions.toHaveAttribute.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18
### option: LocatorAssertions.toHaveAttribute.ignoreCase
### option: LocatorAssertions.toHaveAttribute.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.40
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
## async method: LocatorAssertions.toHaveAttribute#2
* since: v1.39
@ -1504,6 +1646,53 @@ Property value.
### option: LocatorAssertions.toHaveJSProperty.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18
## async method: LocatorAssertions.toHaveRole
* since: v1.44
* langs:
- alias-java: hasRole
Ensures the [Locator] points to an element with a given [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles).
Note that role is matched as a string, disregarding the ARIA role hierarchy. For example, asserting a superclass role `"checkbox"` on an element with a subclass role `"switch"` will fail.
**Usage**
```js
const locator = page.getByTestId('save-button');
await expect(locator).toHaveRole('button');
```
```java
Locator locator = page.getByTestId("save-button");
assertThat(locator).hasRole(AriaRole.BUTTON);
```
```python async
locator = page.get_by_test_id("save-button")
await expect(locator).to_have_role("button")
```
```python sync
locator = page.get_by_test_id("save-button")
expect(locator).to_have_role("button")
```
```csharp
var locator = Page.GetByTestId("save-button");
await Expect(locator).ToHaveRoleAsync(AriaRole.Button);
```
### param: LocatorAssertions.toHaveRole.role = %%-get-by-role-to-have-role-role-%%
* since: v1.44
### option: LocatorAssertions.toHaveRole.timeout = %%-js-assertions-timeout-%%
* since: v1.44
### option: LocatorAssertions.toHaveRole.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.44
## async method: LocatorAssertions.toHaveScreenshot#1
* since: v1.23
* langs: js
@ -1769,11 +1958,8 @@ Expected string or RegExp or a list of those.
Expected string or RegExp or a list of those.
### option: LocatorAssertions.toHaveText.ignoreCase
### option: LocatorAssertions.toHaveText.ignoreCase = %%-assertions-ignore-case-%%
* since: v1.23
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
### option: LocatorAssertions.toHaveText.useInnerText
* since: v1.18

View file

@ -10,7 +10,7 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
(async () => {
const browser = await chromium.launch({
logger: {
isEnabled: (name, severity) => name === 'browser',
isEnabled: (name, severity) => name === 'api',
log: (name, severity, message, args) => console.log(`${name} ${message}`)
}
});

View file

@ -290,9 +290,7 @@ Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` o
**Usage**
```js
page.on('dialog', dialog => {
dialog.accept();
});
page.on('dialog', dialog => dialog.accept());
```
```java
@ -448,7 +446,7 @@ Emitted when the page opens a new tab or window. This event is emitted in additi
The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a
popup with `window.open('http://example.com')`, this event will fire when the network request to "http://example.com" is
done and its response has started loading in the popup.
done and its response has started loading in the popup. If you would like to route/listen to this network request, use [`method: BrowserContext.route`] and [`event: BrowserContext.request`] respectively instead of similar methods on the [Page].
```js
// Start waiting for popup before clicking. Note no await.
@ -2342,7 +2340,8 @@ Attribute name to get the value for.
%%-template-locator-get-by-role-%%
### param: Page.getByRole.role = %%-locator-get-by-role-role-%%
### param: Page.getByRole.role = %%-get-by-role-to-have-role-role-%%
* since: v1.27
### option: Page.getByRole.-inline- = %%-locator-get-by-role-option-list-v1.27-%%
* since: v1.27
@ -3014,7 +3013,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.
Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.
@ -3146,17 +3146,14 @@ return value resolves to `[]`.
## async method: Page.addLocatorHandler
* since: v1.42
:::warning Experimental
This method is experimental and its behavior may change in the upcoming releases.
:::
When testing a web page, sometimes unexpected overlays like a "Sign up" dialog appear and block actions you want to automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making them tricky to handle in automated tests.
This method lets you set up a special function, called a handler, that activates when it detects that overlay is visible. The handler's job is to remove the overlay, allowing your test to continue as if the overlay wasn't there.
Things to keep in mind:
* When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as a part of your normal test flow, instead of using [`method: Page.addLocatorHandler`].
* Playwright checks for the overlay every time before executing or retrying an action that requires an [actionability check](../actionability.md), or before performing an auto-waiting assertion check. When overlay is visible, Playwright calls the handler first, and then proceeds with the action/assertion.
* Playwright checks for the overlay every time before executing or retrying an action that requires an [actionability check](../actionability.md), or before performing an auto-waiting assertion check. When overlay is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't perform any actions, the handler will not be triggered.
* After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with [`option: noWaitAfter`].
* The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. If your handler takes too long, it might cause timeouts.
* You can register multiple handlers. However, only a single handler will be running at a time. Make sure the actions within a handler don't depend on another handler.
@ -3286,13 +3283,13 @@ 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 `<body>` locator that is always visible, so the handler is called before every actionability check:
An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible, so the handler is called before every actionability check. It is important to specify [`option: noWaitAfter`], because the handler does not hide the `<body>` element.
```js
// Setup the handler.
await page.addLocatorHandler(page.locator('body'), async () => {
await page.evaluate(() => window.removeObstructionsForTestIfNeeded());
});
}, { noWaitAfter: true });
// Write the test as usual.
await page.goto('https://example.com');
@ -3303,7 +3300,7 @@ await page.getByRole('button', { name: 'Start here' }).click();
// Setup the handler.
page.addLocatorHandler(page.locator("body")), () => {
page.evaluate("window.removeObstructionsForTestIfNeeded()");
});
}, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true));
// Write the test as usual.
page.goto("https://example.com");
@ -3314,7 +3311,7 @@ page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
# Setup the handler.
def handler():
page.evaluate("window.removeObstructionsForTestIfNeeded()")
page.add_locator_handler(page.locator("body"), handler)
page.add_locator_handler(page.locator("body"), handler, no_wait_after=True)
# Write the test as usual.
page.goto("https://example.com")
@ -3325,7 +3322,7 @@ page.get_by_role("button", name="Start here").click()
# Setup the handler.
def handler():
await page.evaluate("window.removeObstructionsForTestIfNeeded()")
await page.add_locator_handler(page.locator("body"), handler)
await page.add_locator_handler(page.locator("body"), handler, no_wait_after=True)
# Write the test as usual.
await page.goto("https://example.com")
@ -3336,13 +3333,45 @@ await page.get_by_role("button", name="Start here").click()
// Setup the handler.
await page.AddLocatorHandlerAsync(page.Locator("body"), async () => {
await page.EvaluateAsync("window.removeObstructionsForTestIfNeeded()");
});
}, new() { NoWaitAfter = true });
// Write the test as usual.
await page.GotoAsync("https://example.com");
await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync();
```
Handler takes the original locator as an argument. You can also automatically remove the handler after a number of invocations by setting [`option: times`]:
```js
await page.addLocatorHandler(page.getByLabel('Close'), async locator => {
await locator.click();
}, { times: 1 });
```
```java
page.addLocatorHandler(page.getByLabel("Close"), locator => {
locator.click();
}, new Page.AddLocatorHandlerOptions().setTimes(1));
```
```python sync
def handler(locator):
locator.click()
page.add_locator_handler(page.get_by_label("Close"), handler, times=1)
```
```python async
def handler(locator):
await locator.click()
await page.add_locator_handler(page.get_by_label("Close"), handler, times=1)
```
```csharp
await page.AddLocatorHandlerAsync(page.GetByText("Sign up to the newsletter"), async locator => {
await locator.ClickAsync();
}, new() { Times = 1 });
```
### param: Page.addLocatorHandler.locator
* since: v1.42
- `locator` <[Locator]>
@ -3352,24 +3381,49 @@ Locator that triggers the handler.
### param: Page.addLocatorHandler.handler
* langs: js, python
* since: v1.42
- `handler` <[function]>
- `handler` <[function]\([Locator]\): [Promise<any>]>
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<any>]>
- `handler` <[function]\([Locator]\)>
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]>
- `handler` <[function]\([Locator]\)>
Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click.
### option: Page.addLocatorHandler.times
* since: v1.44
- `times` <[int]>
Specifies the maximum number of times this handler should be called. Unlimited by default.
### option: Page.addLocatorHandler.noWaitAfter
* since: v1.44
- `noWaitAfter` <[boolean]>
By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of this behavior, so that overlay can stay visible after the handler has run.
## async method: Page.removeLocatorHandler
* since: v1.44
Removes all locator handlers added by [`method: Page.addLocatorHandler`] for a specific locator.
### param: Page.removeLocatorHandler.locator
* since: v1.44
- `locator` <[Locator]>
Locator passed to [`method: Page.addLocatorHandler`].
## async method: Page.reload
* since: v1.8
- returns: <[null]|[Response]>
@ -3411,6 +3465,10 @@ The handler will only be called for the first url if the response is a redirect.
[`method: Page.route`] will not intercept requests intercepted by Service Worker. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
:::
:::note
[`method: Page.route`] will not intercept the first request of a popup page. Use [`method: BrowserContext.route`] instead.
:::
**Usage**
An example of a naive handler that aborts all image requests:
@ -4461,6 +4519,10 @@ Returns when the required load state has been reached.
This resolves when the page reaches a required load state, `load` by default. The navigation must have been committed
when this method is called. If current document has already reached the required state, resolves immediately.
:::note
Most of the time, this method is not needed because Playwright [auto-waits before every action](../actionability.md).
:::
**Usage**
```js

View file

@ -116,6 +116,13 @@ The opposite of [`method: PageAssertions.toHaveURL`].
Expected URL string or RegExp.
### option: PageAssertions.NotToHaveURL.ignoreCase
* since: v1.44
* langs: js
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
### option: PageAssertions.NotToHaveURL.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18
@ -325,6 +332,12 @@ await Expect(Page).ToHaveURL(new Regex(".*checkout"));
Expected URL string or RegExp.
### option: PageAssertions.toHaveURL.ignoreCase
* since: v1.44
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
### option: PageAssertions.toHaveURL.timeout = %%-js-assertions-timeout-%%
* since: v1.18

View file

@ -97,10 +97,11 @@ A point to use relative to the top-left corner of element padding box. If not sp
element.
## input-modifiers
- `modifiers` <[Array]<[KeyboardModifier]<"Alt"|"Control"|"Meta"|"Shift">>>
- `modifiers` <[Array]<[KeyboardModifier]<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">>>
Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
modifiers back. If not specified, currently pressed modifiers are used.
modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to "Control" on Windows
and Linux and to "Meta" on macOS.
## input-button
- `button` <[MouseButton]<"left"|"right"|"middle">>
@ -403,9 +404,9 @@ unless explicitly provided.
An instance of [FormData] can be created via [`method: APIRequestContext.createFormData`].
## js-python-fetch-option-multipart
* langs: js, python
- `multipart` <[Object]<[string], [string]|[float]|[boolean]|[ReadStream]|[Object]>>
## js-fetch-option-multipart
* langs: js
- `multipart` <[FormData]|[Object]<[string], [string]|[float]|[boolean]|[ReadStream]|[Object]>>
- `name` <[string]> File name
- `mimeType` <[string]> File type
- `buffer` <[Buffer]> File content
@ -415,14 +416,24 @@ this request body. If this parameter is specified `content-type` header will be
unless explicitly provided. File values can be passed either as [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream)
or as file-like object containing file name, mime-type and its content.
## python-fetch-option-multipart
* langs: python
- `multipart` <[Object]<[string], [string]|[float]|[boolean]|[ReadStream]|[Object]>>
- `name` <[string]> File name
- `mimeType` <[string]> File type
- `buffer` <[Buffer]> File content
Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as
this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data`
unless explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.
## csharp-fetch-option-multipart
* langs: csharp
- `multipart` <[FormData]>
Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as
this request body. If this parameter is specified `content-type` header will be set to `multipart/form-data`
unless explicitly provided. File values can be passed either as [`fs.ReadStream`](https://nodejs.org/api/fs.html#fs_class_fs_readstream)
or as file-like object containing file name, mime-type and its content.
unless explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.
An instance of [FormData] can be created via [`method: APIRequestContext.createFormData`].
@ -560,6 +571,7 @@ Whether to emulate network being offline. Defaults to `false`. Learn more about
- `username` <[string]>
- `password` <[string]>
- `origin` ?<[string]> Restrain sending http credentials on specific origin (scheme://host:port).
- `sendImmediately` ?<[boolean]> Whether to send `Authorization` header with the first API request. By deafult, the credentials are sent only when 401 (Unauthorized) response with `WWW-Authenticate` header is received. This option does not affect requests sent from the browser.
Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
If no origin is specified, the username and password are sent to any servers upon unauthorized responses.
@ -846,6 +858,11 @@ Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestC
Time to retry the assertion for in milliseconds. Defaults to `5000`.
## assertions-ignore-case
- `ignoreCase` <[boolean]>
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
## assertions-max-diff-pixels
* langs: js
- `maxDiffPixels` <[int]>
@ -999,6 +1016,7 @@ disable timeout.
If specified, traces are saved into this directory.
## browser-option-devtools
* deprecated: Use [debugging tools](../debug.md) instead.
- `devtools` <[boolean]>
**Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the
@ -1187,8 +1205,7 @@ Text to locate the element for.
Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a regular expression. Note that exact match still trims whitespace.
## locator-get-by-role-role
* since: v1.27
## get-by-role-to-have-role-role
- `role` <[AriaRole]<"alert"|"alertdialog"|"application"|"article"|"banner"|"blockquote"|"button"|"caption"|"cell"|"checkbox"|"code"|"columnheader"|"combobox"|"complementary"|"contentinfo"|"definition"|"deletion"|"dialog"|"directory"|"document"|"emphasis"|"feed"|"figure"|"form"|"generic"|"grid"|"gridcell"|"group"|"heading"|"img"|"insertion"|"link"|"list"|"listbox"|"listitem"|"log"|"main"|"marquee"|"math"|"meter"|"menu"|"menubar"|"menuitem"|"menuitemcheckbox"|"menuitemradio"|"navigation"|"none"|"note"|"option"|"paragraph"|"presentation"|"progressbar"|"radio"|"radiogroup"|"region"|"row"|"rowgroup"|"rowheader"|"scrollbar"|"search"|"searchbox"|"separator"|"slider"|"spinbutton"|"status"|"strong"|"subscript"|"superscript"|"switch"|"tab"|"table"|"tablist"|"tabpanel"|"term"|"textbox"|"time"|"timer"|"toolbar"|"tooltip"|"tree"|"treegrid"|"treeitem">>
Required aria role.
@ -1715,13 +1732,17 @@ test.describe('suite', () => {
The list of supported tokens:
* `{testDir}` - Project's [`property: TestConfig.testDir`].
* Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with config)
* `{snapshotDir}` - Project's [`property: TestConfig.snapshotDir`].
* Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`)
* `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated snapshot name.
* Value: `foo/bar/baz`
* `{ext}` - snapshot extension (with dots)
* Value: `.png`
* `{platform}` - The value of `process.platform`.
* `{projectName}` - Project's file-system-sanitized name, if any.
* Value: `''` (empty string).
* `{snapshotDir}` - Project's [`property: TestConfig.snapshotDir`].
* Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`)
* `{testDir}` - Project's [`property: TestConfig.testDir`].
* Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with config)
* `{testFileDir}` - Directories in relative path from `testDir` to **test file**.
* Value: `page`
* `{testFileName}` - Test file name with extension.
@ -1730,10 +1751,6 @@ The list of supported tokens:
* Value: `page/page-click.spec.ts`
* `{testName}` - File-system-sanitized test title, including parent describes but excluding file name.
* Value: `suite-test-should-work`
* `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated snapshot name.
* Value: `foo/bar/baz`
* `{ext}` - snapshot extension (with dots)
* Value: `.png`
Each token can be preceded with a single character that will be used **only if** this token has non-empty value.

View file

@ -296,15 +296,16 @@ context = browser.new_context(storage_state="state.json")
```csharp
// Save storage state into the file.
// Tests are executed in <TestProject>\bin\Debug\netX.0\ therefore relative path is used to reference playwright/.auth created in project root
await context.StorageStateAsync(new()
{
Path = "state.json"
Path = "../../../playwright/.auth/state.json"
});
// Create a new context with the saved storage state.
var context = await browser.NewContextAsync(new()
{
StorageStatePath = "state.json"
StorageStatePath = "../../../playwright/.auth/state.json"
});
```

View file

@ -30,7 +30,7 @@ To add a [GitHub Actions](https://docs.github.com/en/actions) file first create
#### You will learn
* langs: python, java, csharp
- [How to run tests on push/pull_request](/ci.md#on-pushpull_request)
- [How to run tests on push/pull_request](/ci-intro.md#on-pushpull_request)
- [How to view test logs](/ci-intro.md#viewing-test-logs)
- [How to view the trace](/ci-intro.md#viewing-the-trace)
@ -65,7 +65,7 @@ jobs:
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
@ -103,7 +103,7 @@ jobs:
- name: Run your tests
run: pytest --tracing=retain-on-failure
- uses: actions/upload-artifact@v4
if: always()
if: ${{ !cancelled() }}
with:
name: playwright-traces
path: test-results/
@ -148,7 +148,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- run: dotnet build
@ -266,7 +266,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- run: dotnet build
@ -370,7 +370,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- run: dotnet build
@ -388,23 +388,49 @@ jobs:
Once you have your [GitHub actions workflow](#setting-up-github-actions) setup then all you need to do is [Create a repo on GitHub](https://docs.github.com/en/get-started/quickstart/create-a-repo) or push your code to an existing repository. Follow the instructions on GitHub and don't forget to [initialize a git repository](https://github.com/git-guides/git-init) using the `git init` command so you can [add](https://github.com/git-guides/git-add), [commit](https://github.com/git-guides/git-commit) and [push](https://github.com/git-guides/git-push) your code.
######
* langs: js, java, python
<img width="861" alt="Create a Repo and Push to GitHub" src="https://user-images.githubusercontent.com/13063165/183423254-d2735278-a2ab-4d63-bb99-48d8e5e447bc.png"/>
######
* langs: csharp
![dotnet repo on github](https://github.com/microsoft/playwright/assets/13063165/4f1b4cc3-b850-4d60-a99e-24057eaf91ad)
## Opening the Workflows
Click on the **Actions** tab to see the workflows. Here you will see if your tests have passed or failed.
<img width="847" alt="Opening the Workflows" src="https://user-images.githubusercontent.com/13063165/183423584-2ea18038-cd49-4daa-a20c-2205352f0933.png"/>
######
* langs: js, python, java
![opening the workflow](https://user-images.githubusercontent.com/13063165/183423783-58bf2008-514e-4f96-9c12-c9a55703960c.png)
######
* langs: csharp
![opening the workflow](https://github.com/microsoft/playwright/assets/13063165/71793c09-0815-4faa-866b-85684a1f87e5)
On Pull Requests you can also click on the **Details** link in the [PR status check](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks).
<img width="645" alt="pr status checked" src="https://user-images.githubusercontent.com/13063165/183722462-17a985db-0e10-4205-b16c-8aaac36117b9.png" />
## Viewing Test Logs
Clicking on the workflow run will show you the all the actions that GitHub performed and clicking on **Run Playwright tests** will show the error messages, what was expected and what was received as well as the call log.
<img width="839" alt="Viewing Test Logs" src="https://user-images.githubusercontent.com/13063165/183423783-58bf2008-514e-4f96-9c12-c9a55703960c.png"/>
######
* langs: js, python, java
![Viewing Test Logs](https://user-images.githubusercontent.com/13063165/183423783-58bf2008-514e-4f96-9c12-c9a55703960c.png)
######
* langs: csharp
![viewing the test logs](https://github.com/microsoft/playwright/assets/13063165/ba2d8d7b-ffce-42de-95e0-bcb35c421975)
## HTML Report
@ -441,12 +467,22 @@ Once you have served the report using `npx playwright show-report`, click on the
![playwright trace viewer](https://github.com/microsoft/playwright/assets/13063165/10fe3585-8401-4051-b1c2-b2e92ac4c274)
## Viewing the Trace
* langs: python, java, csharp
* langs: python, java
[trace.playwright.dev](https://trace.playwright.dev) is a statically hosted variant of the Trace Viewer. You can upload trace files using drag and drop.
![playwright trace viewer](https://github.com/microsoft/playwright/assets/13063165/6d5885dc-d511-4c20-b728-040a7ef6cea4)
## Viewing the Trace
* langs: csharp
You can upload Traces which get created on your CI like GitHub Actions as artifacts. This requires [starting and stopping the trace](./trace-viewer-intro#recording-a-trace). We recommend only recording traces for failing tests. Once your traces have been uploaded to CI, they can then be downloaded and opened using [trace.playwright.dev](https://trace.playwright.dev), which is a statically hosted variant of the Trace Viewer. You can upload trace files using drag and drop.
######
* langs: csharp
![playwright trace viewer](https://github.com/microsoft/playwright/assets/13063165/84150084-5019-470a-8449-b61d206bfbb0)
## Publishing report on the web
* langs: js

View file

@ -29,7 +29,7 @@ playwright codegen demo.playwright.dev/todomvc
```
```bash csharp
pwsh bin/Debug/netX/playwright.ps1 codegen demo.playwright.dev/todomvc
pwsh bin/Debug/net8.0/playwright.ps1 codegen demo.playwright.dev/todomvc
```
### Recording a test

View file

@ -80,14 +80,14 @@ npx playwright test --debug
```
#### Debug one test on all browsers
To debug one test on a specific line run the test command followed by the name of the test file and the line number of the test you want to debug, followed by the `--debug` flag. This will run a single test in each browser configured in your [`playwright.config`](/test-configuration.md#multiple-browsers) and open the inspector.
To debug one test on a specific line run the test command followed by the name of the test file and the line number of the test you want to debug, followed by the `--debug` flag. This will run a single test in each browser configured in your [`playwright.config`](./test-projects.md#configure-projects-for-multiple-browsers) and open the inspector.
```bash
npx playwright test example.spec.ts:10 --debug
```
#### Debug on a specific browser
In Playwright you can configure projects in your [`playwright.config`](/test-configuration.md#multiple-browsers). Once configured you can then debug your tests on a specific browser or mobile viewport using the `--project` flag followed by the name of the project configured in your `playwright.config`.
In Playwright you can configure projects in your [`playwright.config`](./test-projects.md#configure-projects-for-multiple-browsers). Once configured you can then debug your tests on a specific browser or mobile viewport using the `--project` flag followed by the name of the project configured in your `playwright.config`.
```bash
npx playwright test --project=chromium --debug

View file

@ -139,7 +139,7 @@ To learn more about the trace viewer see our [Trace Viewer guide](./trace-viewer
CodeGen will auto generate your tests for you as you perform actions in the browser and is a great way to quickly get started. The viewport for the browser window is set to a specific width and height. See the [configuration guide](./test-configuration.md) to change the viewport or emulate different environments.
<LiteYouTube
id="LM4yqrOzmFE"
id="5XIZPqKkdBA"
title="Generating Playwright tests in VS Code"
/>

View file

@ -52,7 +52,7 @@ var jsHandle = await page.EvaluateHandleAsync("window");
## Element Handles
:::caution Discouraged
:::warning[Discouraged]
The use of [ElementHandle] is discouraged, use [Locator] objects and web-first assertions instead.
:::

View file

@ -217,6 +217,10 @@ await page.getByText('Item').click({ button: 'right' });
// Shift + click
await page.getByText('Item').click({ modifiers: ['Shift'] });
// Ctrl + click or Windows and Linux
// Meta + click on macOS
await page.getByText('Item').click({ modifiers: ['ControlOrMeta'] });
// Hover over element
await page.getByText('Item').hover();
@ -237,6 +241,10 @@ page.getByText("Item").click(new Locator.ClickOptions().setButton(MouseButton.RI
// Shift + click
page.getByText("Item").click(new Locator.ClickOptions().setModifiers(Arrays.asList(KeyboardModifier.SHIFT)));
// Ctrl + click or Windows and Linux
// Meta + click on macOS
page.getByText("Item").click(new Locator.ClickOptions().setModifiers(Arrays.asList(KeyboardModifier.CONTROL_OR_META)));
// Hover over element
page.getByText("Item").hover();
@ -257,6 +265,10 @@ await page.get_by_text("Item").click(button="right")
# Shift + click
await page.get_by_text("Item").click(modifiers=["Shift"])
# Ctrl + click or Windows and Linux
# Meta + click on macOS
await page.get_by_text("Item").click(modifiers=["ControlOrMeta"])
# Hover over element
await page.get_by_text("Item").hover()
@ -297,6 +309,10 @@ await page.GetByText("Item").ClickAsync(new() { Button = MouseButton.Right });
// Shift + click
await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.Shift } });
// Ctrl + click or Windows and Linux
// Meta + click on macOS
await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.ControlOrMeta } });
// Hover over element
await page.GetByText("Item").HoverAsync();

View file

@ -69,13 +69,13 @@ dotnet add package Microsoft.Playwright.MSTest
dotnet build
```
4. Install required browsers by replacing `netX` with the actual output folder name, e.g. `net8.0`:
1. Install required browsers. This example uses `net8.0`, if you are using a different version of .NET you will need to adjust the command and change `net8.0` to your version.
```bash
pwsh bin/Debug/netX/playwright.ps1 install
pwsh bin/Debug/net8.0/playwright.ps1 install
```
If `pwsh` is not available, you have to [install PowerShell](https://docs.microsoft.com/powershell/scripting/install/installing-powershell).
If `pwsh` is not available, you will have to [install PowerShell](https://docs.microsoft.com/powershell/scripting/install/installing-powershell).
## Add Example Tests
@ -102,28 +102,28 @@ namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class Tests : PageTest
public class ExampleTest : PageTest
{
[Test]
public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
public async Task HasTitle()
{
await Page.GotoAsync("https://playwright.dev");
// Expect a title "to contain" a substring.
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
}
// create a locator
var getStarted = Page.GetByRole(AriaRole.Link, new() { Name = "Get started" });
// Expect an attribute "to be strictly equal" to the value.
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro");
[Test]
public async Task GetStartedLink()
{
await Page.GotoAsync("https://playwright.dev");
// Click the get started link.
await getStarted.ClickAsync();
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
// Expects the URL to contain intro.
await Expect(Page).ToHaveURLAsync(new Regex(".*intro"));
}
// Expects page to have a heading with the name of Installation.
await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync();
}
}
```
@ -140,28 +140,28 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace PlaywrightTests;
[TestClass]
public class UnitTest1 : PageTest
public class ExampleTest : PageTest
{
[TestMethod]
public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
public async Task HasTitle()
{
await Page.GotoAsync("https://playwright.dev");
// Expect a title "to contain" a substring.
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
}
// create a locator
var getStarted = Page.GetByRole(AriaRole.Link, new() { Name = "Get started" });
// Expect an attribute "to be strictly equal" to the value.
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro");
[TestMethod]
public async Task GetStartedLink()
{
await Page.GotoAsync("https://playwright.dev");
// Click the get started link.
await getStarted.ClickAsync();
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
// Expects the URL to contain intro.
await Expect(Page).ToHaveURLAsync(new Regex(".*intro"));
}
// Expects page to have a heading with the name of Installation.
await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync();
}
}
```
@ -170,33 +170,13 @@ public class UnitTest1 : PageTest
## Running the Example Tests
By default tests will be run on Chromium. This can be configured via the `BROWSER` environment variable, or by adjusting the [launch configuration options](./test-runners.md). Tests are run in headless mode meaning no browser will open up when running the tests. Results of the tests and test logs will be shown in the terminal.
<Tabs
groupId="test-runners"
defaultValue="nunit"
values={[
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
By default tests will be run on Chromium. This can be configured via the `BROWSER` environment variable, or by adjusting the [launch configuration options](./running-tests.md). Tests are run in headless mode meaning no browser will open up when running the tests. Results of the tests and test logs will be shown in the terminal.
```bash
dotnet test -- NUnit.NumberOfTestWorkers=5
dotnet test
```
</TabItem>
<TabItem value="mstest">
```bash
dotnet test -- MSTest.Parallelize.Workers=5
```
</TabItem>
</Tabs>
See our doc on [Test Runners](./test-runners.md) to learn more about running tests in headed mode, running multiple tests, running specific configurations etc.
See our doc on [Running and Debugging Tests](./running-tests.md) to learn more about running tests in headed mode, running multiple tests, running specific configurations etc.
## System requirements
@ -209,7 +189,7 @@ See our doc on [Test Runners](./test-runners.md) to learn more about running tes
- [Write tests using web first assertions, page fixtures and locators](./writing-tests.md)
- [Run single test, multiple tests, headed mode](./running-tests.md)
- [Learn more about the NUnit and MSTest base classes](./test-runners.md)
- [Generate tests with Codegen](./codegen.md)
- [See a trace of your tests](./trace-viewer-intro.md)
- [Using Playwright as library](./library.md)
- [Run tests on CI](./ci-intro.md)
- [Learn more about the NUnit and MSTest base classes](./test-runners.md)

View file

@ -17,7 +17,7 @@ Playwright Test was created specifically to accommodate the needs of end-to-end
## Installing Playwright
Get started by installing Playwright using npm or yarn. Alternatively you can also get started and run your tests using the [VS Code Extension](./getting-started-vscode.md).
Get started by installing Playwright using npm, yarn or pnpm. Alternatively you can also get started and run your tests using the [VS Code Extension](./getting-started-vscode.md).
<Tabs
defaultValue="npm"

View file

@ -264,7 +264,7 @@ Role locators include [buttons, checkboxes, headings, links, lists, tables, and
Note that role locators **do not replace** accessibility audits and conformance tests, but rather give early feedback about the ARIA guidelines.
:::note When to use role locators
:::note[When to use role locators]
We recommend prioritizing role locators to locate elements, as it is the closest way to how users and assistive technology perceive the page.
:::
@ -301,7 +301,7 @@ page.get_by_label("Password").fill("secret")
await page.GetByLabel("Password").FillAsync("secret");
```
:::note When to use label locators
:::note[When to use label locators]
Use this locator when locating form fields.
:::
### Locate by placeholder
@ -340,7 +340,7 @@ await page
.FillAsync("playwright@microsoft.com");
```
:::note When to use placeholder locators
:::note[When to use placeholder locators]
Use this locator when locating form elements that do not have labels but do have placeholder texts.
:::
@ -433,7 +433,7 @@ await Expect(Page
Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into one, turns line breaks into spaces and ignores leading and trailing whitespace.
:::
:::note When to use text locators
:::note[When to use text locators]
We recommend using text locators to find non interactive elements like `div`, `span`, `p`, etc. For interactive elements like `button`, `a`, `input`, etc. use [role locators](#locate-by-role).
:::
@ -471,7 +471,7 @@ page.get_by_alt_text("playwright logo").click()
await page.GetByAltText("playwright logo").ClickAsync();
```
:::note When to use alt locators
:::note[When to use alt locators]
Use this locator when your element supports alt text such as `img` and `area` elements.
:::
@ -507,7 +507,7 @@ expect(page.get_by_title("Issues count")).to_have_text("25 issues")
await Expect(Page.GetByTitle("Issues count")).toHaveText("25 issues");
```
:::note When to use title locators
:::note[When to use title locators]
Use this locator when your element has the `title` attribute.
:::
@ -543,7 +543,7 @@ page.get_by_test_id("directions").click()
await page.GetByTestId("directions").ClickAsync();
```
:::note When to use testid locators
:::note[When to use testid locators]
You can also use test ids when you choose to use the test id methodology or when you can't locate by [role](#locate-by-role) or [text](#locate-by-text).
:::
@ -693,7 +693,7 @@ await page.Locator("#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > di
await page.Locator("//*[@id='tsf']/div[2]/div[1]/div[1]/div/div[2]/input").ClickAsync();
```
:::note When to use this
:::note[When to use this]
CSS and XPath are not recommended as the DOM can often change leading to non resilient tests. Instead, try to come up with a locator that is close to how the user perceives the page such as [role locators](#locate-by-role) or [define an explicit testing contract](#locate-by-test-id) using test ids.
:::

View file

@ -60,6 +60,23 @@ test('show battery status', async ({ page }) => {
```
## Mocking read-only APIs
Some APIs are read-only so you won't be able to assign to a navigator property. For example,
```js
// Following line will have no effect.
navigator.cookieEnabled = true;
```
However, if the property is [configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#configurable), you can still override it using the plain JavaScript:
```js
await page.addInitScript(() => {
Object.defineProperty(Object.getPrototypeOf(navigator), 'cookieEnabled', { value: false });
});
```
## Verifying API calls
Sometimes it is useful to check if the page made all expected APIs calls. You can

View file

@ -109,7 +109,7 @@ Sometimes, it is essential to make an API request, but the response needs to be
allow for reproducible testing. In that case, instead of mocking the request, one
can perform the request and fulfill it with the modified response.
In the example below we intercept the call to the fruit API and add a new fruit called 'playwright', to the data. We then go to the url and assert that this data is there:
In the example below we intercept the call to the fruit API and add a new fruit called 'Loquat', to the data. We then go to the url and assert that this data is there:
```js

View file

@ -143,7 +143,8 @@ handle new pages opened by `target="_blank"` links.
const pagePromise = context.waitForEvent('page');
await page.getByText('open new tab').click();
const newPage = await pagePromise;
await newPage.waitForLoadState();
// Interact with the new page normally.
await newPage.getByRole('button').click();
console.log(await newPage.title());
```
@ -152,7 +153,8 @@ console.log(await newPage.title());
Page newPage = context.waitForPage(() -> {
page.getByText("open new tab").click(); // Opens a new tab
});
newPage.waitForLoadState();
// Interact with the new page normally
newPage.getByRole(AriaRole.BUTTON).click();
System.out.println(newPage.title());
```
@ -162,7 +164,8 @@ async with context.expect_page() as new_page_info:
await page.get_by_text("open new tab").click() # Opens a new tab
new_page = await new_page_info.value
await new_page.wait_for_load_state()
# Interact with the new page normally
await new_page.get_by_role("button").click()
print(await new_page.title())
```
@ -172,7 +175,8 @@ with context.expect_page() as new_page_info:
page.get_by_text("open new tab").click() # Opens a new tab
new_page = new_page_info.value
new_page.wait_for_load_state()
# Interact with the new page normally
new_page.get_by_role("button").click()
print(new_page.title())
```
@ -182,7 +186,8 @@ var newPage = await context.RunAndWaitForPageAsync(async () =>
{
await page.GetByText("open new tab").ClickAsync();
});
await newPage.WaitForLoadStateAsync();
// Interact with the new page normally
await newPage.GetByRole(AriaRole.Button).ClickAsync();
Console.WriteLine(await newPage.TitleAsync());
```
@ -242,8 +247,8 @@ This event is emitted in addition to the `browserContext.on('page')` event, but
const popupPromise = page.waitForEvent('popup');
await page.getByText('open the popup').click();
const popup = await popupPromise;
// Wait for the popup to load.
await popup.waitForLoadState();
// Interact with the new popup normally.
await popup.getByRole('button').click();
console.log(await popup.title());
```
@ -252,7 +257,8 @@ console.log(await popup.title());
Page popup = page.waitForPopup(() -> {
page.getByText("open the popup").click();
});
popup.waitForLoadState();
// Interact with the popup normally
popup.getByRole(AriaRole.BUTTON).click();
System.out.println(popup.title());
```
@ -262,7 +268,8 @@ async with page.expect_popup() as popup_info:
await page.get_by_text("open the popup").click()
popup = await popup_info.value
await popup.wait_for_load_state()
# Interact with the popup normally
await popup.get_by_role("button").click()
print(await popup.title())
```
@ -272,7 +279,8 @@ with page.expect_popup() as popup_info:
page.get_by_text("open the popup").click()
popup = popup_info.value
popup.wait_for_load_state()
# Interact with the popup normally
popup.get_by_role("button").click()
print(popup.title())
```
@ -282,7 +290,8 @@ var popup = await page.RunAndWaitForPopupAsync(async () =>
{
await page.GetByText("open the popup").ClickAsync();
});
await popup.WaitForLoadStateAsync();
// Interact with the popup normally
await popup.GetByRole(AriaRole.Button).ClickAsync();
Console.WriteLine(await popup.TitleAsync());
```

View file

@ -4,6 +4,50 @@ title: "Release notes"
toc_max_heading_level: 2
---
## Version 1.43
### New APIs
- Method [`method: BrowserContext.clearCookies`] now supports filters to remove only some cookies.
```csharp
// Clear all cookies.
await Context.ClearCookiesAsync();
// New: clear cookies with a particular name.
await Context.ClearCookiesAsync(new() { Name = "session-id" });
// New: clear cookies for a particular domain.
await Context.ClearCookiesAsync(new() { Domain = "my-origin.com" });
```
- New property [`method: Locator.contentFrame`] converts a [Locator] object to a [FrameLocator]. This can be useful when you have a [Locator] object obtained somewhere, and later on would like to interact with the content inside the frame.
```csharp
var locator = Page.Locator("iframe[name='embedded']");
// ...
var frameLocator = locator.ContentFrame;
await frameLocator.GetByRole(AriaRole.Button).ClickAsync();
```
- New property [`method: FrameLocator.owner`] converts a [FrameLocator] object to a [Locator]. This can be useful when you have a [FrameLocator] object obtained somewhere, and later on would like to interact with the `iframe` element.
```csharp
var frameLocator = page.FrameLocator("iframe[name='embedded']");
// ...
var locator = frameLocator.Owner;
await Expect(locator).ToBeVisibleAsync();
```
### Browser Versions
* Chromium 124.0.6367.8
* Mozilla Firefox 124.0
* WebKit 17.4
This version was also tested against the following stable channels:
* Google Chrome 123
* Microsoft Edge 123
## Version 1.42
### New Locator Handler

View file

@ -4,6 +4,50 @@ title: "Release notes"
toc_max_heading_level: 2
---
## Version 1.43
### New APIs
- Method [`method: BrowserContext.clearCookies`] now supports filters to remove only some cookies.
```java
// Clear all cookies.
context.clearCookies();
// New: clear cookies with a particular name.
context.clearCookies(new BrowserContext.ClearCookiesOptions().setName("session-id"));
// New: clear cookies for a particular domain.
context.clearCookies(new BrowserContext.ClearCookiesOptions().setDomain("my-origin.com"));
```
- New method [`method: Locator.contentFrame`] converts a [Locator] object to a [FrameLocator]. This can be useful when you have a [Locator] object obtained somewhere, and later on would like to interact with the content inside the frame.
```java
Locator locator = page.locator("iframe[name='embedded']");
// ...
FrameLocator frameLocator = locator.contentFrame();
frameLocator.getByRole(AriaRole.BUTTON).click();
```
- New method [`method: FrameLocator.owner`] converts a [FrameLocator] object to a [Locator]. This can be useful when you have a [FrameLocator] object obtained somewhere, and later on would like to interact with the `iframe` element.
```java
FrameLocator frameLocator = page.frameLocator("iframe[name='embedded']");
// ...
Locator locator = frameLocator.owner();
assertThat(locator).isVisible();
```
### Browser Versions
* Chromium 124.0.6367.8
* Mozilla Firefox 124.0
* WebKit 17.4
This version was also tested against the following stable channels:
* Google Chrome 123
* Microsoft Edge 123
## Version 1.42
### Experimental JUnit integration
@ -644,7 +688,7 @@ This version was also tested against the following stable channels:
### New APIs & changes
- Default assertions timeout now can be changed with [`setDefaultAssertionTimeout`](./test-assertions#playwright-assertions-set-default-assertion-timeout).
- Default assertions timeout now can be changed with [`setDefaultAssertionTimeout`](./api/class-playwrightassertions#playwright-assertions-set-default-assertion-timeout).
### Announcements

View file

@ -6,8 +6,186 @@ toc_max_heading_level: 2
import LiteYouTube from '@site/src/components/LiteYouTube';
## Version 1.44
### New APIs
**Accessibility assertions**
- [`method: LocatorAssertions.toHaveAccessibleName`] checks if the element has the specified accessible name:
```js
const locator = page.getByRole('button');
await expect(locator).toHaveAccessibleName('Submit');
```
- [`method: LocatorAssertions.toHaveAccessibleDescription`] checks if the element has the specified accessible description:
```js
const locator = page.getByRole('button');
await expect(locator).toHaveAccessibleDescription('Upload a photo');
```
- [`method: LocatorAssertions.toHaveRole`] checks if the element has the specified ARIA role:
```js
const locator = page.getByTestId('save-button');
await expect(locator).toHaveRole('button');
```
**Locator handler**
- After executing the handler added with [`method: Page.addLocatorHandler`], Playwright will now wait until the overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with the new `noWaitAfter` option.
- You can use new `times` option in [`method: Page.addLocatorHandler`] to specify maximum number of times the handler should be run.
- The handler in [`method: Page.addLocatorHandler`] now accepts the locator as argument.
- New [`method: Page.removeLocatorHandler`] method for removing previously added locator handlers.
```js
const locator = page.getByText('This interstitial covers the button');
await page.addLocatorHandler(locator, async overlay => {
await overlay.locator('#close').click();
}, { times: 3, noWaitAfter: true });
// Run your tests that can be interrupted by the overlay.
// ...
await page.removeLocatorHandler(locator);
```
**Miscellaneous options**
- [`multipart`](./api/class-apirequestcontext#api-request-context-fetch-option-multipart) option in `apiRequestContext.fetch()` now accepts [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) and supports repeating fields with the same name.
```js
const formData = new FormData();
formData.append('file', new File(['let x = 2024;'], 'f1.js', { type: 'text/javascript' }));
formData.append('file', new File(['hello'], 'f2.txt', { type: 'text/plain' }));
context.request.post('https://example.com/uploadFiles', {
multipart: formData
});
```
- `expect(callback).toPass({ intervals })` can now be configured by `expect.toPass.inervals` option globally in [`property: TestConfig.expect`] or per project in [`property: TestProject.expect`].
- `expect(page).toHaveURL(url)` now supports `ignoreCase` [option](./api/class-pageassertions#page-assertions-to-have-url-option-ignore-case).
- [`property: TestProject.ignoreSnapshots`](./api/class-testproject#test-project-ignore-snapshots) allows to configure per project whether to skip screenshot expectations.
**Reporter API**
- New method [`method: Suite.entries`] returns child test suites and test cases in their declaration order. [`property: Suite.type`] and [`property: TestCase.type`] can be used to tell apart test cases and suites in the list.
- [Blob](./test-reporters#blob-reporter) reporter now allows overriding report file path with a single option `outputFile`. The same option can also be specified as `PLAYWRIGHT_BLOB_OUTPUT_FILE` environment variable that might be more convenient on CI/CD.
- [JUnit](./test-reporters#junit-reporter) reporter now supports `includeProjectInTestName` option.
**Command line**
- `--last-failed` CLI option to for running only tests that failed in the previous run.
First run all tests:
```sh
$ npx playwright test
Running 103 tests using 5 workers
...
2 failed
[chromium] my-test.spec.ts:8:5 two ─────────────────────────────────────────────────────────
[chromium] my-test.spec.ts:13:5 three ──────────────────────────────────────────────────────
101 passed (30.0s)
```
Now fix the failing tests and run Playwright again with `--last-failed` option:
```sh
$ npx playwright test --last-failed
Running 2 tests using 2 workers
2 passed (1.2s)
```
### Browser Versions
* Chromium 125.0.6422.14
* Mozilla Firefox 125.0.1
* WebKit 17.4
This version was also tested against the following stable channels:
* Google Chrome 124
* Microsoft Edge 124
## Version 1.43
### New APIs
- Method [`method: BrowserContext.clearCookies`] now supports filters to remove only some cookies.
```js
// Clear all cookies.
await context.clearCookies();
// New: clear cookies with a particular name.
await context.clearCookies({ name: 'session-id' });
// New: clear cookies for a particular domain.
await context.clearCookies({ domain: 'my-origin.com' });
```
- New mode `retain-on-first-failure` for [`property: TestOptions.trace`]. In this mode, trace is recorded for the first run of each test, but not for retires. When test run fails, the trace file is retained, otherwise it is removed.
```js title=playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
trace: 'retain-on-first-failure',
},
});
```
- New property [`property: TestInfo.tags`] exposes test tags during test execution.
```js
test('example', async ({ page }) => {
console.log(test.info().tags);
});
```
- New method [`method: Locator.contentFrame`] converts a [Locator] object to a [FrameLocator]. This can be useful when you have a [Locator] object obtained somewhere, and later on would like to interact with the content inside the frame.
```js
const locator = page.locator('iframe[name="embedded"]');
// ...
const frameLocator = locator.contentFrame();
await frameLocator.getByRole('button').click();
```
- New method [`method: FrameLocator.owner`] converts a [FrameLocator] object to a [Locator]. This can be useful when you have a [FrameLocator] object obtained somewhere, and later on would like to interact with the `iframe` element.
```js
const frameLocator = page.frameLocator('iframe[name="embedded"]');
// ...
const locator = frameLocator.owner();
await expect(locator).toBeVisible();
```
### UI Mode Updates
![Playwright UI Mode](https://github.com/microsoft/playwright/assets/9881434/61ca7cfc-eb7a-4305-8b62-b6c9f098f300)
* See tags in the test list.
* Filter by tags by typing `@fast` or clicking on the tag itself.
* New shortcuts:
- "F5" to run tests.
- "Shift F5" to stop running tests.
- "Ctrl `" to toggle test output.
### Browser Versions
* Chromium 124.0.6367.8
* Mozilla Firefox 124.0
* WebKit 17.4
This version was also tested against the following stable channels:
* Google Chrome 123
* Microsoft Edge 123
## Version 1.42
<LiteYouTube
id="KjSaIQLlgns"
title="Playwright 1.41 & 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:
@ -182,7 +360,7 @@ test('pass', async ({ page }) => {
});
```
See the documentation [for a full example](./test-assertions.md#add-custom-matchers-using-expectextend).
See the documentation [for a full example](./test-assertions#add-custom-matchers-using-expectextend).
### Merge test fixtures
@ -1673,7 +1851,7 @@ This version was also tested against the following stable channels:
### Locator Improvements
- [`method: Locator.dragTo`]
- [`expect(locator).toBeChecked({ checked })`](./test-assertions#locator-assertions-to-be-checked)
- [`expect(locator).toBeChecked({ checked })`](./api/class-locatorassertions#locator-assertions-to-be-checked)
- Each locator can now be optionally filtered by the text it contains:
```js
await page.locator('li', { hasText: 'my item' }).locator('button').click();
@ -1828,7 +2006,7 @@ Playwright Trace Viewer is now **available online** at https://trace.playwright.
- [`testInfo.parallelIndex`](./api/class-testinfo#test-info-parallel-index)
- [`testInfo.titlePath`](./api/class-testinfo#test-info-title-path)
- [`testOptions.trace`](./api/class-testoptions#test-options-trace) has new options
- [`expect.toMatchSnapshot`](./test-assertions#expectvaluetomatchsnapshotname-options) supports subdirectories
- [`expect.toMatchSnapshot`](./api/class-genericassertions.md) supports subdirectories
- [`reporter.printsToStdio()`](./api/class-reporter#reporter-prints-to-stdio)
@ -2109,25 +2287,25 @@ By default, the timeout for assertions is not set, so it'll wait forever, until
List of all new assertions:
- [`expect(locator).toBeChecked()`](./test-assertions#expectlocatortobechecked)
- [`expect(locator).toBeDisabled()`](./test-assertions#expectlocatortobedisabled)
- [`expect(locator).toBeEditable()`](./test-assertions#expectlocatortobeeditable)
- [`expect(locator).toBeEmpty()`](./test-assertions#expectlocatortobeempty)
- [`expect(locator).toBeEnabled()`](./test-assertions#expectlocatortobeenabled)
- [`expect(locator).toBeFocused()`](./test-assertions#expectlocatortobefocused)
- [`expect(locator).toBeHidden()`](./test-assertions#expectlocatortobehidden)
- [`expect(locator).toBeVisible()`](./test-assertions#expectlocatortobevisible)
- [`expect(locator).toContainText(text, options?)`](./test-assertions#expectlocatortocontaintexttext-options)
- [`expect(locator).toHaveAttribute(name, value)`](./test-assertions#expectlocatortohaveattributename-value)
- [`expect(locator).toHaveClass(expected)`](./test-assertions#expectlocatortohaveclassexpected)
- [`expect(locator).toHaveCount(count)`](./test-assertions#expectlocatortohavecountcount)
- [`expect(locator).toHaveCSS(name, value)`](./test-assertions#expectlocatortohavecssname-value)
- [`expect(locator).toHaveId(id)`](./test-assertions#expectlocatortohaveidid)
- [`expect(locator).toHaveJSProperty(name, value)`](./test-assertions#expectlocatortohavejspropertyname-value)
- [`expect(locator).toHaveText(expected, options)`](./test-assertions#expectlocatortohavetextexpected-options)
- [`expect(page).toHaveTitle(title)`](./test-assertions#expectpagetohavetitletitle)
- [`expect(page).toHaveURL(url)`](./test-assertions#expectpagetohaveurlurl)
- [`expect(locator).toHaveValue(value)`](./test-assertions#expectlocatortohavevaluevalue)
- [`expect(locator).toBeChecked()`](./api/class-locatorassertions#locator-assertions-to-be-checked)
- [`expect(locator).toBeDisabled()`](./api/class-locatorassertions#locator-assertions-to-be-disabled)
- [`expect(locator).toBeEditable()`](./api/class-locatorassertions#locator-assertions-to-be-editable)
- [`expect(locator).toBeEmpty()`](./api/class-locatorassertions#locator-assertions-to-be-empty)
- [`expect(locator).toBeEnabled()`](./api/class-locatorassertions#locator-assertions-to-be-enabled)
- [`expect(locator).toBeFocused()`](./api/class-locatorassertions#locator-assertions-to-be-focused)
- [`expect(locator).toBeHidden()`](./api/class-locatorassertions#locator-assertions-to-be-hidden)
- [`expect(locator).toBeVisible()`](./api/class-locatorassertions#locator-assertions-to-be-visible)
- [`expect(locator).toContainText(text, options?)`](./api/class-locatorassertions#locator-assertions-to-contain-text)
- [`expect(locator).toHaveAttribute(name, value)`](./api/class-locatorassertions#locator-assertions-to-have-attribute)
- [`expect(locator).toHaveClass(expected)`](./api/class-locatorassertions#locator-assertions-to-have-class)
- [`expect(locator).toHaveCount(count)`](./api/class-locatorassertions#locator-assertions-to-have-count)
- [`expect(locator).toHaveCSS(name, value)`](./api/class-locatorassertions#locator-assertions-to-have-css)
- [`expect(locator).toHaveId(id)`](./api/class-locatorassertions#locator-assertions-to-have-id)
- [`expect(locator).toHaveJSProperty(name, value)`](./api/class-locatorassertions#locator-assertions-to-have-js-property)
- [`expect(locator).toHaveText(expected, options)`](./api/class-locatorassertions#locator-assertions-to-have-text)
- [`expect(page).toHaveTitle(title)`](./api/class-pageassertions#page-assertions-to-have-title)
- [`expect(page).toHaveURL(url)`](./api/class-pageassertions#page-assertions-to-have-url)
- [`expect(locator).toHaveValue(value)`](./api/class-locatorassertions#locator-assertions-to-have-value)
#### ⛓ Serial mode with [`describe.serial`](./api/class-test#test-describe-serial)
@ -2163,7 +2341,7 @@ Step information is exposed in reporters API.
#### 🌎 Launch web server before running tests
To launch a server during the tests, use the [`webServer`](./test-webserver) option in the configuration file. The server will wait for a given url to be available before running the tests, and the url will be passed over to Playwright as a [`baseURL`](./api/class-fixtures#fixtures-base-url) when creating a context.
To launch a server during the tests, use the [`webServer`](./test-webserver) option in the configuration file. The server will wait for a given url to be available before running the tests, and the url will be passed over to Playwright as a [`baseURL`](./api/class-testoptions#test-options-base-url) when creating a context.
```ts title="playwright.config.ts"
import { defineConfig } from '@playwright/test';
@ -2192,7 +2370,7 @@ Learn more in the [documentation](./test-webserver).
#### Playwright Test
- **⚡️ Introducing [Reporter API](https://github.com/microsoft/playwright/blob/65a9037461ffc15d70cdc2055832a0c5512b227c/packages/playwright-test/types/testReporter.d.ts)** which is already used to create an [Allure Playwright reporter](https://github.com/allure-framework/allure-js/pull/297).
- **⛺️ New [`baseURL` fixture](./test-configuration#basic-options)** to support relative paths in tests.
- **⛺️ New [`baseURL` fixture](./test-configuration#basic-configuration)** to support relative paths in tests.
#### Playwright

View file

@ -4,6 +4,52 @@ title: "Release notes"
toc_max_heading_level: 2
---
## Version 1.43
### New APIs
- Method [`method: BrowserContext.clearCookies`] now supports filters to remove only some cookies.
```python
# Clear all cookies.
context.clear_cookies()
# New: clear cookies with a particular name.
context.clear_cookies(name="session-id")
# New: clear cookies for a particular domain.
context.clear_cookies(domain="my-origin.com")
```
- New method [`method: Locator.contentFrame`] converts a [Locator] object to a [FrameLocator]. This can be useful when you have a [Locator] object obtained somewhere, and later on would like to interact with the content inside the frame.
```python
locator = page.locator("iframe[name='embedded']")
# ...
frame_locator = locator.content_frame
frame_locator.getByRole("button").click()
```
- New method [`method: FrameLocator.owner`] converts a [FrameLocator] object to a [Locator]. This can be useful when you have a [FrameLocator] object obtained somewhere, and later on would like to interact with the `iframe` element.
```python
frame_locator = page.frame_locator("iframe[name='embedded']")
# ...
locator = frame_locator.owner
expect(locator).to_be_visible()
```
- Conda builds are now published for macOS-arm64 and Linux-arm64.
### Browser Versions
* Chromium 124.0.6367.8
* Mozilla Firefox 124.0
* WebKit 17.4
This version was also tested against the following stable channels:
* Google Chrome 123
* Microsoft Edge 123
## Version 1.42
### New Locator Handler

View file

@ -7,57 +7,130 @@ title: "Running and debugging tests"
You can run a single test, a set of tests or all tests. Tests can be run on different browsers. By default, tests are run in a headless manner, meaning no browser window will be opened while running the tests and results will be seen in the terminal. If you prefer, you can run your tests in headed mode by using the `headless` test run parameter.
- Running all tests
**You will learn**
```bash
dotnet test
```
- [How to run tests](#running-tests)
- [How to debug tests](#debugging-tests)
- Running a single test file
## Running tests
```bash
dotnet test --filter "MyClassName"
```
### Run all tests
- Run a set of test files
Use the following command to run all tests.
```bash
dotnet test --filter "MyClassName1|MyClassName2"
```
```bash
dotnet test
```
- Run the test with the title
### Run your tests in headed mode
```bash
dotnet test --filter "Name~TestMethod1"
```
Use the following command to run your tests in headed mode opening a browser window for each test.
- Running Tests on specific browsers
```bash tab=bash-bash lang=csharp
HEADED=1 dotnet test
```
```bash
dotnet test -- Playwright.BrowserName=webkit
```
```batch tab=bash-batch lang=csharp
set HEADED=1
dotnet test
```
- Running Tests on multiple browsers
```powershell tab=bash-powershell lang=csharp
$env:HEADED="1"
dotnet test
```
To run your test on multiple browsers or configurations, you need to invoke the `dotnet test` command multiple times. There you can then either specify the `BROWSER` environment variable or set the `Playwright.BrowserName` via the runsettings file:
### Run tests on different browsers: Browser env
```bash
dotnet test --settings:chromium.runsettings
dotnet test --settings:firefox.runsettings
dotnet test --settings:webkit.runsettings
```
Specify which browser you would like to run your tests on via the `BROWSER` environment variable.
```xml
<?xml version="1.0" encoding="utf-8"?>
```bash tab=bash-bash lang=csharp
BROWSER=webkit dotnet test
```
```batch tab=bash-batch lang=csharp
set BROWSER=webkit
dotnet test
```
```powershell tab=bash-powershell lang=csharp
$env:BROWSER="webkit"
dotnet test
```
### Run tests on different browsers: launch configuration
Specify which browser you would like to run your tests on by adjusting the launch configuration options:
```bash
dotnet test -- Playwright.BrowserName=webkit
```
To run your test on multiple browsers or configurations, you need to invoke the `dotnet test` command multiple times. There you can then either specify the `BROWSER` environment variable or set the `Playwright.BrowserName` via the runsettings file:
```bash
dotnet test --settings:chromium.runsettings
dotnet test --settings:firefox.runsettings
dotnet test --settings:webkit.runsettings
```
```xml
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<Playwright>
<BrowserName>chromium</BrowserName>
</Playwright>
</RunSettings>
```
```
For more information see [selective unit tests](https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests?pivots=mstest) in the Microsoft docs.
### Run specific tests
To run a single test file, use the filter flag followed by the class name of the test you want to run.
```bash
dotnet test --filter "ExampleTest"
```
To run a set of test files, use the filter flag followed by the class names of the tests you want to run.
```bash
dotnet test --filter "ExampleTest1|ExampleTest2"
```
To run a test with a specific title use the filter flag followed by *Name~* and the title of the test.
```bash
dotnet test --filter "Name~GetStartedLink"
```
### Run tests with multiple workers:
<Tabs
groupId="test-runners"
defaultValue="nunit"
values={[
{label: 'NUnit', value: 'nunit'},
{label: 'MSTest', value: 'mstest'}
]
}>
<TabItem value="nunit">
```bash
dotnet test -- NUnit.NumberOfTestWorkers=5
```
</TabItem>
<TabItem value="mstest">
```bash
dotnet test -- MSTest.Parallelize.Workers=5
```
</TabItem>
</Tabs>
## Debugging Tests
Since Playwright runs in .NET, you can debug it with your debugger of choice in e.g. Visual Studio Code or Visual Studio. Playwright comes with the Playwright Inspector which allows you to step through Playwright API calls, see their debug logs and explore [locators](./locators.md).
@ -76,7 +149,7 @@ $env:PWDEBUG=1
dotnet test
```
<img width="712" alt="Playwright Inspector" src="https://user-images.githubusercontent.com/883973/108614092-8c478a80-73ac-11eb-9597-67dfce110e00.png"></img>
![debugging tests with playwright inspector](https://github.com/microsoft/playwright/assets/13063165/a1e758d3-d379-414f-be0b-7339f12bb635)
Check out our [debugging guide](./debug.md) to learn more about the [Playwright Inspector](./debug.md#playwright-inspector) as well as debugging with [Browser Developer tools](./debug.md#browser-developer-tools).

View file

@ -0,0 +1,137 @@
# class: FullConfig
* since: v1.10
* langs: js
Resolved configuration which is accessible via [`property: TestInfo.config`] and is passed to the test reporters. To see the format of Playwright configuration file, please see [TestConfig] instead.
## property: FullConfig.configFile
* since: v1.20
- type: ?<[string]>
Path to the configuration file used to run the tests. The value is an empty string if no config file was used.
## property: FullConfig.forbidOnly
* since: v1.10
- type: <[boolean]>
See [`property: TestConfig.forbidOnly`].
## property: FullConfig.fullyParallel
* since: v1.20
- type: <[boolean]>
See [`property: TestConfig.fullyParallel`].
## property: FullConfig.globalSetup
* since: v1.10
- type: <[null]|[string]>
See [`property: TestConfig.globalSetup`].
## property: FullConfig.globalTeardown
* since: v1.10
- type: <[null]|[string]>
See [`property: TestConfig.globalTeardown`].
## property: FullConfig.globalTimeout
* since: v1.10
- type: <[int]>
See [`property: TestConfig.globalTimeout`].
## property: FullConfig.grep
* since: v1.10
- type: <[RegExp]|[Array]<[RegExp]>>
See [`property: TestConfig.grep`].
## property: FullConfig.grepInvert
* since: v1.10
- type: <[null]|[RegExp]|[Array]<[RegExp]>>
See [`property: TestConfig.grepInvert`].
## property: FullConfig.maxFailures
* since: v1.10
- type: <[int]>
See [`property: TestConfig.maxFailures`].
## property: FullConfig.metadata
* since: v1.10
- type: <[Metadata]>
See [`property: TestConfig.metadata`].
## property: FullConfig.preserveOutput
* since: v1.10
- type: <[PreserveOutput]<"always"|"never"|"failures-only">>
See [`property: TestConfig.preserveOutput`].
## property: FullConfig.projects
* since: v1.10
- type: <[Array]<[FullProject]>>
List of resolved projects.
## property: FullConfig.quiet
* since: v1.10
- type: <[boolean]>
See [`property: TestConfig.quiet`].
## property: FullConfig.reporter
* since: v1.10
- type: <[string]|[Array]<[Object]>|[BuiltInReporter]<"list"|"dot"|"line"|"github"|"json"|"junit"|"null"|"html">>
- `0` <[string]> Reporter name or module or file path
- `1` <[Object]> An object with reporter options if any
See [`property: TestConfig.reporter`].
## property: FullConfig.reportSlowTests
* since: v1.10
- type: <[null]|[Object]>
- `max` <[int]> The maximum number of slow test files to report. Defaults to `5`.
- `threshold` <[float]> Test duration in milliseconds that is considered slow. Defaults to 15 seconds.
See [`property: TestConfig.reportSlowTests`].
## property: FullConfig.rootDir
* since: v1.20
- type: <[string]>
Base directory for all relative paths used in the reporters.
## property: FullConfig.shard
* since: v1.10
- type: <[null]|[Object]>
- `total` <[int]> The total number of shards.
- `current` <[int]> The index of the shard to execute, one-based.
See [`property: TestConfig.shard`].
## property: FullConfig.updateSnapshots
* since: v1.10
- type: <[UpdateSnapshots]<"all"|"none"|"missing">>
See [`property: TestConfig.updateSnapshots`].
## property: FullConfig.version
* since: v1.20
- type: <[string]>
Playwright version.
## property: FullConfig.webServer
* since: v1.10
- type: <[null]|[Object]>
See [`property: TestConfig.webServer`].
## property: FullConfig.workers
* since: v1.10
- type: <[int]>
See [`property: TestConfig.workers`].

View file

@ -0,0 +1,95 @@
# class: FullProject
* since: v1.10
* langs: js
Runtime representation of the test project configuration. It is accessible in the tests via [`property: TestInfo.project`] and [`property: WorkerInfo.project`] and is passed to the test reporters. To see the format of the project in the Playwright configuration file please see [TestProject] instead.
## property: FullProject.dependencies
* since: v1.31
- type: <[Array]<[string]>>
See [`property: TestProject.dependencies`].
## property: FullProject.grep
* since: v1.10
- type: <[RegExp]|[Array]<[RegExp]>>
See [`property: TestProject.grep`].
## property: FullProject.grepInvert
* since: v1.10
- type: <[null]|[RegExp]|[Array]<[RegExp]>>
See [`property: TestProject.grepInvert`].
## property: FullProject.metadata
* since: v1.10
- type: <[Metadata]>
See [`property: TestProject.metadata`].
## property: FullProject.name
* since: v1.10
- type: <[string]>
See [`property: TestProject.name`].
## property: FullProject.snapshotDir
* since: v1.10
- type: <[string]>
See [`property: TestProject.snapshotDir`].
## property: FullProject.outputDir
* since: v1.10
- type: <[string]>
See [`property: TestProject.outputDir`].
## property: FullProject.repeatEach
* since: v1.10
- type: <[int]>
See [`property: TestProject.repeatEach`].
## property: FullProject.retries
* since: v1.10
- type: <[int]>
See [`property: TestProject.retries`].
## property: FullProject.teardown
* since: v1.34
- type: ?<[string]>
See [`property: TestProject.teardown`].
## property: FullProject.testDir
* since: v1.10
- type: <[string]>
See [`property: TestProject.testDir`].
## property: FullProject.testIgnore
* since: v1.10
- type: <[string]|[RegExp]|[Array]<[string]|[RegExp]>>
See [`property: TestProject.testIgnore`].
## property: FullProject.testMatch
* since: v1.10
- type: <[string]|[RegExp]|[Array]<[string]|[RegExp]>>
See [`property: TestProject.testMatch`].
## property: FullProject.timeout
* since: v1.10
- type: <[int]>
See [`property: TestProject.timeout`].
## property: FullProject.use
* since: v1.10
- type: <[Fixtures]>
See [`property: TestProject.use`].

View file

@ -2,7 +2,7 @@
* since: v1.10
* langs: js
Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or `testDir`. These options are described in the [TestConfig] object in the [configuration file](../test-configuration.md).
Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or `testDir`. These options are described in the [TestConfig] object in the [configuration file](../test-configuration.md). This type describes format of the configuration file, to access resolved configuration parameters at run time use [FullConfig].
Playwright Test supports running multiple test projects at the same time. Project-specific options should be put to [`property: TestConfig.projects`], but top-level [TestConfig] can also define base options shared between all projects.
@ -41,19 +41,20 @@ export default defineConfig({
- type: ?<[Object]>
- `timeout` ?<[int]> Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
- `toHaveScreenshot` ?<[Object]> Configuration for the [`method: PageAssertions.toHaveScreenshot#1`] method.
- `threshold` ?<[float]> an acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`.
- `maxDiffPixels` ?<[int]> an acceptable amount of pixels that could be different, unset by default.
- `maxDiffPixelRatio` ?<[float]> an acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
- `animations` ?<[ScreenshotAnimations]<"allow"|"disabled">> See [`option: animations`] in [`method: Page.screenshot`]. Defaults to `"disabled"`.
- `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`.
- `maxDiffPixels` ?<[int]> An acceptable amount of pixels that could be different, unset by default.
- `maxDiffPixelRatio` ?<[float]> An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1` , unset by default.
- `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: scale`] in [`method: Page.screenshot`]. Defaults to `"css"`.
- `stylePath` ?<[string]|[Array]<[string]>> See [`option: style`] in [`method: Page.screenshot`].
- `threshold` ?<[float]> An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`.
- `toMatchSnapshot` ?<[Object]> Configuration for the [`method: SnapshotAssertions.toMatchSnapshot#1`] method.
- `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.
- `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.
- `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`.
- `toPass` ?<[Object]> Configuration for the [expect(value).toPass()](../test-assertions.md#expecttopass) method.
- `timeout` ?<[int]> timeout for toPass method in milliseconds.
- `intervals` ?<[Array]<[int]>> Probe intervals for toPass method in milliseconds.
- `timeout` ?<[int]> Timeout for toPass method in milliseconds.
Configuration for the `expect` assertion library. Learn more about [various timeouts](../test-timeouts.md).
@ -111,7 +112,7 @@ export default defineConfig({
* since: v1.10
- type: ?<[string]>
Path to the global setup file. This file will be required and run before all the tests. It must export a single function that takes a [`TestConfig`] argument.
Path to the global setup file. This file will be required and run before all the tests. It must export a single function that takes a [FullConfig] argument.
Learn more about [global setup and teardown](../test-global-setup-teardown.md).
@ -457,8 +458,8 @@ export default defineConfig({
## property: TestConfig.shard
* since: v1.10
- type: ?<[null]|[Object]>
- `total` <[int]> The total number of shards.
- `current` <[int]> The index of the shard to execute, one-based.
- `total` <[int]> The total number of shards.
Shard tests and execute only the selected shard. Specify in the one-based form like `{ total: 5, current: 2 }`.
@ -588,15 +589,15 @@ export default defineConfig({
* since: v1.10
- type: ?<[Object]|[Array]<[Object]>>
- `command` <[string]> Shell command to start. For example `npm run start`..
- `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` or `url` should be specified.
- `url` ?<[string]> The url on 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. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified.
- `cwd` ?<[string]> Current working directory of the spawned process, defaults to the directory of the configuration file.
- `env` ?<[Object]<[string], [string]>> Environment variables to set for the command, `process.env` by default.
- `ignoreHTTPSErrors` ?<[boolean]> Whether to ignore HTTPS errors when fetching the `url`. Defaults to `false`.
- `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000.
- `port` ?<[int]> The port that your http server is expected to appear on. It does wait until it accepts connections. Either `port` or `url` should be specified.
- `reuseExistingServer` ?<[boolean]> If true, it will re-use an existing server on the `port` or `url` when available. If no server is running on that `port` or `url`, it will run the command to start a new server. If `false`, it will throw if an existing process is listening on the `port` or `url`. This should be commonly set to `!process.env.CI` to allow the local dev server when running tests locally.
- `stdout` ?<["pipe"|"ignore"]> 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` ?<["pipe"|"ignore"]> Whether to pipe the stderr of the command to the process stderr or ignore it. Defaults to `"pipe"`.
- `cwd` ?<[string]> Current working directory of the spawned process, defaults to the directory of the configuration file.
- `env` ?<[Object]<[string], [string]>> Environment variables to set for the command, `process.env` by default.
- `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000.
- `url` ?<[string]> The url on 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. Redirects (3xx status codes) are being followed and the new location is checked. Either `port` or `url` should be specified.
Launch a development web server (or multiple) during the tests.

View file

@ -106,7 +106,7 @@ Column number where the currently running test is declared.
## property: TestInfo.config
* since: v1.10
- type: <[TestConfig]>
- type: <[FullConfig]>
Processed configuration from the [configuration file](../test-configuration.md).
@ -279,7 +279,7 @@ Also available as `process.env.TEST_PARALLEL_INDEX`. Learn more about [paralleli
## property: TestInfo.project
* since: v1.10
- type: <[TestProject]>
- type: <[FullProject]>
Processed project configuration from the [configuration file](../test-configuration.md).

View file

@ -556,10 +556,10 @@ export default defineConfig({
Whether to record trace for each test. Defaults to `'off'`.
* `'off'`: Do not record trace.
* `'on'`: Record trace for each test.
* `'retain-on-failure'`: Record trace for each test, but remove all traces from successful test runs.
* `'on-first-retry'`: Record trace only when retrying a test for the first time.
* `'on-all-retries'`: Record traces only when retrying for all retries.
* `'retain-on-first-failure'`: Record traces only when the test fails for the first time.
* `'on-all-retries'`: Record trace only when retrying a test.
* `'retain-on-failure'`: Record trace for each test. When test run passes, remove the recorded trace.
* `'retain-on-first-failure'`: Record trace for the first run of each test, but not for retires. When test run passes, remove the recorded trace.
For more control, pass an object that specifies `mode` and trace features to enable.

View file

@ -2,7 +2,7 @@
* since: v1.10
* langs: js
Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple configurations. For example, consider running tests against multiple browsers.
Playwright Test supports running multiple test projects at the same time. This is useful for running tests in multiple configurations. For example, consider running tests against multiple browsers. This type describes format of a project in the configuration file, to access resolved configuration parameters at run time use [FullProject].
`TestProject` encapsulates configuration specific to a single project. Projects are configured in [`property: TestConfig.projects`] specified in the [configuration file](../test-configuration.md). Note that all properties of [TestProject] are available in the top-level [TestConfig], in which case they are shared between all projects.
@ -104,6 +104,7 @@ export default defineConfig({
- `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.
- `intervals` ?<[Array]<[int]>> probe intervals for toPass method in milliseconds.
Configuration for the `expect` assertion library.
@ -134,6 +135,39 @@ Filter to only run tests with a title **not** matching one of the patterns. This
`grepInvert` option is also useful for [tagging tests](../test-annotations.md#tag-tests).
## property: TestProject.ignoreSnapshots
* since: v1.44
- type: ?<[boolean]>
Whether to skip snapshot expectations, such as `expect(value).toMatchSnapshot()` and `await expect(page).toHaveScreenshot()`.
**Usage**
The following example will only perform screenshot assertions on Chromium.
```js title="playwright.config.ts"
import { defineConfig } from '@playwright/test';
export default defineConfig({
projects: [
{
name: 'chromium',
use: devices['Desktop Chrome'],
},
{
name: 'firefox',
use: devices['Desktop Firefox'],
ignoreSnapshots: true,
},
{
name: 'webkit',
use: devices['Desktop Safari'],
ignoreSnapshots: true,
},
],
});
```
## property: TestProject.metadata
* since: v1.10
- type: ?<[Metadata]>

View file

@ -6,7 +6,7 @@
## property: WorkerInfo.config
* since: v1.10
- type: <[TestConfig]>
- type: <[FullConfig]>
Processed configuration from the [configuration file](../test-configuration.md).
@ -22,7 +22,7 @@ Also available as `process.env.TEST_PARALLEL_INDEX`. Learn more about [paralleli
## property: WorkerInfo.project
* since: v1.10
- type: <[TestProject]>
- type: <[FullProject]>
Processed project configuration from the [configuration file](../test-configuration.md).

View file

@ -18,12 +18,15 @@ title: "Assertions"
| [`method: LocatorAssertions.toBeInViewport`] | Element intersects viewport |
| [`method: LocatorAssertions.toBeVisible`] | Element is visible |
| [`method: LocatorAssertions.toContainText`] | Element contains text |
| [`method: LocatorAssertions.toHaveAccessibleDescription`] | Element has a matching [accessible description](https://w3c.github.io/accname/#dfn-accessible-description) |
| [`method: LocatorAssertions.toHaveAccessibleName`] | Element has a matching [accessible name](https://w3c.github.io/accname/#dfn-accessible-name) |
| [`method: LocatorAssertions.toHaveAttribute`] | Element has a DOM attribute |
| [`method: LocatorAssertions.toHaveClass`] | Element has a class property |
| [`method: LocatorAssertions.toHaveCount`] | List has exact number of children |
| [`method: LocatorAssertions.toHaveCSS`] | Element has CSS property |
| [`method: LocatorAssertions.toHaveId`] | Element has an ID |
| [`method: LocatorAssertions.toHaveJSProperty`] | Element has a JavaScript property |
| [`method: LocatorAssertions.toHaveRole`] | Element has a specific [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles) |
| [`method: LocatorAssertions.toHaveText`] | Element matches text |
| [`method: LocatorAssertions.toHaveValue`] | Input has a value |
| [`method: LocatorAssertions.toHaveValues`] | Select has options selected |

View file

@ -40,12 +40,15 @@ Note that retrying assertions are async, so you must `await` them.
| [await expect(locator).toBeInViewport()](./api/class-locatorassertions.md#locator-assertions-to-be-in-viewport) | Element intersects viewport |
| [await expect(locator).toBeVisible()](./api/class-locatorassertions.md#locator-assertions-to-be-visible) | Element is visible |
| [await expect(locator).toContainText()](./api/class-locatorassertions.md#locator-assertions-to-contain-text) | Element contains text |
| [await expect(locator).toHaveAccessibleDescription()](./api/class-locatorassertions.md#locator-assertions-to-have-accessible-description) | Element has a matching [accessible description](https://w3c.github.io/accname/#dfn-accessible-description) |
| [await expect(locator).toHaveAccessibleName()](./api/class-locatorassertions.md#locator-assertions-to-have-accessible-name) | Element has a matching [accessible name](https://w3c.github.io/accname/#dfn-accessible-name) |
| [await expect(locator).toHaveAttribute()](./api/class-locatorassertions.md#locator-assertions-to-have-attribute) | Element has a DOM attribute |
| [await expect(locator).toHaveClass()](./api/class-locatorassertions.md#locator-assertions-to-have-class) | Element has a class property |
| [await expect(locator).toHaveCount()](./api/class-locatorassertions.md#locator-assertions-to-have-count) | List has exact number of children |
| [await expect(locator).toHaveCSS()](./api/class-locatorassertions.md#locator-assertions-to-have-css) | Element has CSS property |
| [await expect(locator).toHaveId()](./api/class-locatorassertions.md#locator-assertions-to-have-id) | Element has an ID |
| [await expect(locator).toHaveJSProperty()](./api/class-locatorassertions.md#locator-assertions-to-have-js-property) | Element has a JavaScript property |
| [await expect(locator).toHaveRole()](./api/class-locatorassertions.md#locator-assertions-to-have-role) | Element has a specific [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles) |
| [await expect(locator).toHaveScreenshot()](./api/class-locatorassertions.md#locator-assertions-to-have-screenshot-1) | Element has a screenshot |
| [await expect(locator).toHaveText()](./api/class-locatorassertions.md#locator-assertions-to-have-text) | Element matches text |
| [await expect(locator).toHaveValue()](./api/class-locatorassertions.md#locator-assertions-to-have-value) | Input has a value |

View file

@ -79,14 +79,15 @@ Complete set of Playwright Test options is available in the [configuration file]
| Option | Description |
| :- | :- |
| Non-option arguments | Each argument is treated as a regular expression matched against the full test file path. Only tests from the files matching the pattern will be executed. Special symbols like `$` or `*` should be escaped with `\`. In many shells/terminals you may need to quote the arguments. |
| `--headed` | Run tests in headed browsers. Useful for debugging. |
|`--browser`| Run test in a specific browser. Available options are `"chromium"`, `"firefox"`, `"webkit"` or `"all"` to run tests in all three browsers at the same time. |
| `--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. |
| `--debug`| Run tests with Playwright Inspector. Shortcut for `PWDEBUG=1` environment variable and `--timeout=0 --max-failures=1 --headed --workers=1` options.|
| `--forbid-only` | Whether to disallow `test.only`. Useful on CI.|
| `--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).|
| `-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).|
| `--headed` | Run tests in headed browsers. Useful for debugging. |
| `--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. |
| `--last-failed` | Only re-run the failures.|
| `--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. |
@ -101,6 +102,5 @@ Complete set of Playwright Test options is available in the [configuration file]
| `--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. |
| `--update-snapshots` or `-u` | Whether to update [snapshots](./test-snapshots.md) with actual results instead of comparing them. Use this when snapshot expectations have changed.|
| `--workers <number>` or `-j <number>`| The maximum number of concurrent worker processes that run in [parallel](./test-parallel.md). |

View file

@ -312,7 +312,7 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let
```js title="src/pages/ProductsPage.spec.tsx"
import { test, expect } from '@playwright/experimental-ct-react';
import type { HooksConfig } from '@playwright/test';
import type { HooksConfig } from '../playwright';
import { ProductsPage } from './pages/ProductsPage';
test('configure routing through hooks config', async ({ page, mount }) => {
@ -343,7 +343,7 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let
```js title="src/pages/ProductsPage.spec.tsx"
import { test, expect } from '@playwright/experimental-ct-solid';
import type { HooksConfig } from '@playwright/test';
import type { HooksConfig } from '../playwright';
import { ProductsPage } from './pages/ProductsPage';
test('configure routing through hooks config', async ({ page, mount }) => {
@ -374,7 +374,7 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let
```js title="src/pages/ProductsPage.spec.ts"
import { test, expect } from '@playwright/experimental-ct-vue';
import type { HooksConfig } from '@playwright/test';
import type { HooksConfig } from '../playwright';
import ProductsPage from './pages/ProductsPage.vue';
test('configure routing through hooks config', async ({ page, mount }) => {
@ -408,7 +408,7 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let
```js title="src/pages/ProductsPage.spec.ts"
import { test, expect } from '@playwright/experimental-ct-vue2';
import type { HooksConfig } from '@playwright/test';
import type { HooksConfig } from '../playwright';
import ProductsPage from './pages/ProductsPage.vue';
test('configure routing through hooks config', async ({ page, mount }) => {
@ -669,7 +669,7 @@ beforeMount<HooksConfig>(async ({ hooksConfig }) => {
```js title="src/pinia.spec.ts"
import { test, expect } from '@playwright/experimental-ct-vue';
import type { HooksConfig } from '@playwright/test';
import type { HooksConfig } from '../playwright';
import Store from './Store.vue';
test('override initialState ', async ({ mount }) => {

View file

@ -129,7 +129,7 @@ You can use the `globalSetup` option in the [configuration file](./test-configur
Similarly, use `globalTeardown` to run something once after all the tests. Alternatively, let `globalSetup` return a function that will be used as a global teardown. You can pass data such as port number, authentication tokens, etc. from your global setup to your tests using environment variables.
:::note
Using `globalSetup` and `globalTeardown` will not produce traces or artifacts. If you want to produce traces and artifacts, use [project dependencies](#option-1-project-dependencies).
Using `globalSetup` and `globalTeardown` will not produce traces or artifacts, and options like `headless` or `testIdAttribute` specified in the config file are not applied. If you want to produce traces and artifacts and respect config options, use [project dependencies](#option-1-project-dependencies).
:::
```js title="playwright.config.ts"

View file

@ -216,7 +216,7 @@ You can also teardown your setup by adding a [`property: TestProject.teardown`]
### Test filtering
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.
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 dependencies ignoring the filters.
## Custom project parameters

View file

@ -70,7 +70,7 @@ Now use this reporter with [`property: TestConfig.reporter`]. Learn more about [
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: ['./my-awesome-reporter.ts', { customOption: 'some value' }],
reporter: [['./my-awesome-reporter.ts', { customOption: 'some value' }]],
});
```
@ -87,6 +87,15 @@ and [`method: Reporter.onError`] is called when something went wrong outside of
If your custom reporter does not print anything to the terminal, implement [`method: Reporter.printsToStdio`] and return `false`. This way, Playwright will use one of the standard terminal reporters in addition to your custom reporter to enhance user experience.
**Merged report API notes**
When merging multiple [`blob`](../test-reporters#blob-reporter) reports via [`merge-reports`](../test-sharding#merge-reports-cli) CLI
command, the same [Reporter] API is called to produce final reports and all existing reporters
should work without any changes. There some subtle differences though which might affect some custom
reporters.
* Projects from different shards are always kept as separate [TestProject] objects. E.g. if project 'Desktop Chrome' was sharded across 5 machines then there will be 5 instances of projects with the same name in the config passed to [`method: Reporter.onBegin`].
## optional method: Reporter.onBegin
* since: v1.10
@ -94,7 +103,7 @@ Called once before running tests. All tests have been already discovered and put
### param: Reporter.onBegin.config
* since: v1.10
- `config` <[TestConfig]>
- `config` <[FullConfig]>
Resolved configuration.

View file

@ -4,7 +4,7 @@
`Suite` is a group of tests. All tests in Playwright Test form the following hierarchy:
* Root suite has a child suite for each [TestProject].
* Root suite has a child suite for each [FullProject].
* Project suite #1. Has a child suite for each test file in the project.
* File suite #1
* [TestCase] #1
@ -26,6 +26,12 @@ Reporter is given a root suite in the [`method: Reporter.onBegin`] method.
Returns the list of all test cases in this suite and its descendants, as opposite to [`property: Suite.tests`].
## method: Suite.entries
* since: v1.44
- type: <[Array]<[TestCase]|[Suite]>>
Test cases and suites defined directly in this suite. The elements are returned in their declaration order. You can differentiate between various entry types by using [`property: TestCase.type`] and [`property: Suite.type`].
## property: Suite.location
* since: v1.10
- type: ?<[Location]>
@ -40,7 +46,7 @@ Parent suite, missing for the root suite.
## method: Suite.project
* since: v1.10
- returns: ?<[TestProject]>
- returns: <[FullProject]|[undefined]>
Configuration of the project this suite belongs to, or [void] for the root suite.
@ -72,3 +78,10 @@ Suite title.
- returns: <[Array]<[string]>>
Returns a list of titles from the root down to this suite.
## property: Suite.type
* since: v1.44
- returns: <[SuiteType]<"root"|"project"|"file"|"describe">>
Returns the type of the suite. The Suites form the following hierarchy:
`root` -> `project` -> `file` -> `describe` -> ...`describe` -> `test`.

View file

@ -34,7 +34,7 @@ See also [`property: TestResult.status`] for the actual status.
* since: v1.25
- type: <[string]>
Unique test ID that is computed based on the test file name, test title and project name. Test ID can be used as a history ID.
A test ID that is computed based on the test file name, test title and project name. The ID is unique within Playwright session.
## property: TestCase.location
* since: v1.10
@ -108,3 +108,8 @@ Test title as passed to the [`method: Test.(call)`] call.
Returns a list of titles from the root down to this test.
## property: TestCase.type
* since: v1.44
- returns: <[TestCaseType]<"test">>
Returns "test". Useful for detecting test cases in [`method: Suite.entries`].

View file

@ -215,16 +215,24 @@ Blob reports contain all the details about the test run and can be used later to
npx playwright test --reporter=blob
```
By default, the report is written into the `blob-report` directory in the package.json directory or current working directory (if no package.json is found). The report file name is `report.zip` or `report-<shard_number>.zip` when [sharding](./test-sharding.md) is used. Both output directory and report file name can be overridden in the configuration file:
By default, the report is written into the `blob-report` directory in the package.json directory or current working directory (if no package.json is found). The report file name looks like `report-<hash>.zip` or `report-<hash>-<shard_number>.zip` when [sharding](./test-sharding.md) is used. The hash is an optional value computed from `--grep`, `--grepInverted`, `--project` and file filters passed as command line arguments. The hash guarantees that running Playwright with different command line options will produce different but stable between runs report names. The output file name can be overridden in the configuration file or pass as `'PLAYWRIGHT_BLOB_OUTPUT_FILE'` environment variable.
```js title="playwright.config.ts"
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: [['blob', { outputDir: 'my-report', fileName: `report-${os.platform()}.zip` }]],
reporter: [['blob', { outputFile: `./blob-report/report-${os.platform()}.zip` }]],
});
```
Blob report supports following configuration options and environment variables:
| Environment Variable Name | Reporter Config Option| Description | Default
|---|---|---|---|
| `PLAYWRIGHT_BLOB_OUTPUT_DIR` | `outputDir` | Directory to save the output. Existing content is deleted before writing the new report. | `blob-report`
| `PLAYWRIGHT_BLOB_OUTPUT_NAME` | `fileName` | Report file name. | `report-<project>-<hash>-<shard_number>.zip`
| `PLAYWRIGHT_BLOB_OUTPUT_FILE` | `outputFile` | Full path to the output file. If defined, `outputDir` and `fileName` will be ignored. | `undefined`
### JSON reporter
JSON reporter produces an object with all information about the test run.
@ -255,6 +263,14 @@ export default defineConfig({
});
```
JSON report supports following configuration options and environment variables:
| Environment Variable Name | Reporter Config Option| Description | Default
|---|---|---|---|
| `PLAYWRIGHT_JSON_OUTPUT_DIR` | | Directory to save the output file. Ignored if output file is specified. | `cwd` or config directory.
| `PLAYWRIGHT_JSON_OUTPUT_NAME` | `outputFile` | Base file name for the output, relative to the output dir. | JSON report is printed to the stdout.
| `PLAYWRIGHT_JSON_OUTPUT_FILE` | `outputFile` | Full path to the output file. If defined, `PLAYWRIGHT_JSON_OUTPUT_DIR` and `PLAYWRIGHT_JSON_OUTPUT_NAME` will be ignored. | JSON report is printed to the stdout.
### JUnit reporter
JUnit reporter produces a JUnit-style xml report.
@ -285,6 +301,18 @@ export default defineConfig({
});
```
JUnit report supports following configuration options and environment variables:
| Environment Variable Name | Reporter Config Option| Description | Default
|---|---|---|---|
| `PLAYWRIGHT_JUNIT_OUTPUT_DIR` | | Directory to save the output file. Ignored if output file is not specified. | `cwd` or config directory.
| `PLAYWRIGHT_JUNIT_OUTPUT_NAME` | `outputFile` | Base file name for the output, relative to the output dir. | JUnit report is printed to the stdout.
| `PLAYWRIGHT_JUNIT_OUTPUT_FILE` | `outputFile` | Full path to the output file. If defined, `PLAYWRIGHT_JUNIT_OUTPUT_DIR` and `PLAYWRIGHT_JUNIT_OUTPUT_NAME` will be ignored. | JUnit report is printed to the stdout.
| | `stripANSIControlSequences` | Whether to remove ANSI control sequences from the text before writing it in the report. | By default output text is added as is.
| | `includeProjectInTestName` | Whether to include Playwright project name in every test case as a name prefix. | By default not included.
| `PLAYWRIGHT_JUNIT_SUITE_ID` | | Value of the `id` attribute on the root `<testsuites/>` report entry. | Empty string.
| `PLAYWRIGHT_JUNIT_SUITE_NAME` | | Value of the `name` attribute on the root `<testsuites/>` report entry. | Empty string.
### GitHub Actions annotations
You can use the built in `github` reporter to get automatic failure annotations when running in GitHub actions.
@ -355,6 +383,7 @@ npx playwright test --reporter="./myreporter/my-awesome-reporter.ts"
* [Argos Visual Testing](https://argos-ci.com/docs/playwright)
* [Currents](https://www.npmjs.com/package/@currents/playwright)
* [GitHub Actions Reporter](https://www.npmjs.com/package/@estruyf/github-actions-reporter)
* [GitHub Pull Request Comment](https://github.com/marketplace/actions/playwright-report-comment)
* [Monocart](https://github.com/cenfun/monocart-reporter)
* [ReportPortal](https://github.com/reportportal/agent-js-playwright)
* [Serenity/JS](https://serenity-js.org/handbook/test-runners/playwright-test)

View file

@ -5,107 +5,18 @@ title: "Test Runners"
## Introduction
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience
it works best with the built-in .NET test runner, and using NUnit as the test framework. NUnit is
also what we use internally for [our tests](https://github.com/microsoft/playwright-dotnet/tree/main/src/Playwright.Tests).
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience it works best with the built-in .NET test runner, and using NUnit as the test framework. NUnit is also what we use internally for [our tests](https://github.com/microsoft/playwright-dotnet/tree/main/src/Playwright.Tests).
Playwright and Browser instances can be reused between tests for better performance. We
recommend running each test case in a new BrowserContext, this way browser state will be
isolated between the tests.
<!-- TOC -->
## NUnit
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
### Creating an NUnit project
```bash
# Create a new project
dotnet new nunit -n PlaywrightTests
cd PlaywrightTests
# Add the required reference
dotnet add package Microsoft.Playwright.NUnit
dotnet build
# Install the required browsers and operating system dependencies
pwsh bin/Debug/netX/playwright.ps1 install --with-deps
```
Modify the UnitTest1.cs:
```csharp
using Microsoft.Playwright.NUnit;
namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class MyTest : PageTest
{
[Test]
public async Task ShouldHaveTheCorrectSlogan()
{
await Page.GotoAsync("https://playwright.dev");
await Expect(Page.Locator("text=enables reliable end-to-end testing for modern web apps")).ToBeVisibleAsync();
}
[Test]
public async Task ShouldHaveTheCorrectTitle()
{
await Page.GotoAsync("https://playwright.dev");
var title = Page.Locator(".navbar__inner .navbar__title");
await Expect(title).ToHaveTextAsync("Playwright");
}
}
```
Run your tests against Chromium
```bash
dotnet test
```
Run your tests against WebKit
```bash tab=bash-bash lang=csharp
BROWSER=webkit dotnet test
```
```batch tab=bash-batch lang=csharp
set BROWSER=webkit
dotnet test
```
```powershell tab=bash-powershell lang=csharp
$env:BROWSER="webkit"
dotnet test
```
Run your tests with GUI
```bash tab=bash-bash lang=csharp
HEADED=1 dotnet test
```
```batch tab=bash-batch lang=csharp
set HEADED=1
dotnet test
```
```powershell tab=bash-powershell lang=csharp
$env:HEADED="1"
dotnet test
```
You can also choose specifically which tests to run, using the [filtering capabilities](https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests?pivots=nunit):
```bash
dotnet test --filter "Name~Slogan"
```
Check out the [installation guide](./intro.md) to get started.
### Running NUnit tests in Parallel
@ -114,6 +25,10 @@ Only `ParallelScope.Self` is supported.
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
```bash
dotnet test -- NUnit.NumberOfTestWorkers=5
```
### Customizing [BrowserContext] options
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
@ -223,91 +138,7 @@ There are a few base classes available to you in `Microsoft.Playwright.NUnit` na
Playwright provides base classes to write tests with MSTest via the [`Microsoft.Playwright.MSTest`](https://www.nuget.org/packages/Microsoft.Playwright.MSTest) package.
### Creating an MSTest project
```bash
# Create a new project
dotnet new mstest -n PlaywrightTests
cd PlaywrightTests
# Add the required reference
dotnet add package Microsoft.Playwright.MSTest
dotnet build
# Install the required browsers and operating system dependencies
pwsh bin/Debug/netX/playwright.ps1 install --with-deps
```
Modify the UnitTest1.cs:
```csharp
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestClass]
public class UnitTest1: PageTest
{
[TestMethod]
public async Task ShouldHaveTheCorrectSlogan()
{
await Page.GotoAsync("https://playwright.dev");
await Expect(Page.Locator("text=enables reliable end-to-end testing for modern web apps")).ToBeVisibleAsync();
}
[TestMethod]
public async Task ShouldHaveTheCorrectTitle()
{
await Page.GotoAsync("https://playwright.dev");
var title = Page.Locator(".navbar__inner .navbar__title");
await Expect(title).ToHaveTextAsync("Playwright");
}
}
```
Run your tests against Chromium
```bash
dotnet test
```
Run your tests against WebKit
```bash tab=bash-bash lang=csharp
BROWSER=webkit dotnet test
```
```batch tab=bash-batch lang=csharp
set BROWSER=webkit
dotnet test
```
```powershell tab=bash-powershell lang=csharp
$env:BROWSER="webkit"
dotnet test
```
Run your tests with GUI
```bash tab=bash-bash lang=csharp
HEADED=1 dotnet test
```
```batch tab=bash-batch lang=csharp
set HEADED=1
dotnet test
```
```powershell tab=bash-powershell lang=csharp
$env:HEADED="1"
dotnet test
```
You can also choose specifically which tests to run, using the [filtering capabilities](https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests?pivots=mstest):
```bash
dotnet test --filter "Name~Slogan"
```
Check out the [installation guide](./intro.md) to get started.
### Running MSTest tests in Parallel
@ -331,7 +162,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace PlaywrightTests;
[TestClass]
public class UnitTest1 : PageTest
public class ExampleTest : PageTest
{
[TestMethod]
public async Task TestWithCustomContextOptions()

View file

@ -55,6 +55,7 @@ def test_my_app_is_working(fixture_name):
- `context`: New [browser context](./browser-contexts) for a test.
- `page`: New [browser page](./pages) for a test.
- `new_context`: Allows creating different [browser contexts](./browser-contexts) for a test. Useful for multi-user scenarios. Accepts the same parameters as [`method: Browser.newContext`].
**Session scope**: These fixtures are created when requested in a test function and destroyed when all tests end.
@ -211,30 +212,6 @@ def browser_context_args(browser_context_args, playwright):
Or via the CLI `--device="iPhone 11 Pro"`
### Persistent context
```py title="conftest.py"
import pytest
from playwright.sync_api import BrowserType
from typing import Dict
@pytest.fixture(scope="session")
def context(
browser_type: BrowserType,
browser_type_launch_args: Dict,
browser_context_args: Dict
):
context = browser_type.launch_persistent_context("./foobar", **{
**browser_type_launch_args,
**browser_context_args,
"locale": "de-DE",
})
yield context
context.close()
```
When using that all pages inside your test are created from the persistent context.
### Using with `unittest.TestCase`
See the following example for using it with `unittest.TestCase`. This has a limitation,

View file

@ -5,7 +5,7 @@ title: "Sharding"
## Introduction
By default, Playwright runs tests in [parallel](/test-parallel.md) and strives for optimal utilization of CPU cores on your machine. In order to achieve even greater parallelisation, you can further scale Playwright test execution by running tests on multiple machines simultaneously. We call this mode of operation "sharding".
By default, Playwright runs test files in [parallel](./test-parallel.md) and strives for optimal utilization of CPU cores on your machine. In order to achieve even greater parallelisation, you can further scale Playwright test execution by running tests on multiple machines simultaneously. We call this mode of operation "sharding".
## Sharding tests between multiple machines
@ -20,6 +20,8 @@ npx playwright test --shard=4/4
Now, if you run these shards in parallel on different computers, your test suite completes four times faster.
Note that Playwright can only shard tests that can be run in parallel. By default, this means Playwright will shard test files. Learn about other options in the [parallelism guide](./test-parallel.md).
## Merging reports from multiple shards
In the previous example, each test shard has its own test report. If you want to have a combined report showing all the test results from all the shards, you can merge them.
@ -47,7 +49,7 @@ This will produce a standard HTML report into `playwright-report` directory.
## GitHub Actions example
GitHub Actions supports [sharding tests between multiple jobs](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs) using the [`jobs.<job_id>.strategy.matrix`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) option. The `matrix` option will run a separate job for every possible combination of the provided options.
GitHub Actions supports [sharding tests between multiple jobs](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs) using the [`jobs.<job_id>.strategy.matrix`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) option. The `matrix` option will run a separate job for every possible combination of the provided options.
The following example shows you how to configure a job to run your tests on four machines in parallel and then merge the reports into a single report. Don't forget to add `reporter: process.env.CI ? 'blob' : 'html',` to your `playwright.config.ts` file as in the example above.
@ -55,7 +57,7 @@ The following example shows you how to configure a job to run your tests on four
1. Then we run our Playwright tests with the `--shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}` option. This will run our test command for each shard.
1. Finally we upload our blob report to the GitHub Actions Artifacts. This will make the blob report available to other jobs in the workflow.
1. Finally we upload our blob report to the GitHub Actions Artifacts. This will make the blob report available to other jobs in the workflow.
@ -89,7 +91,7 @@ jobs:
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
- name: Upload blob report to GitHub Actions Artifacts
if: always()
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ matrix.shardIndex }}
@ -104,7 +106,7 @@ jobs:
...
merge-reports:
# Merge reports after playwright-tests, even if some shards have failed
if: always()
if: ${{ !cancelled() }}
needs: [playwright-tests]
runs-on: ubuntu-latest
@ -124,7 +126,7 @@ jobs:
merge-multiple: true
- name: Merge into HTML Report
run: npx playwright merge-reports --reporter html ./all-blob-reports
run: npx playwright merge-reports --reporter html ./all-blob-reports
- name: Upload HTML report
uses: actions/upload-artifact@v4
@ -150,7 +152,7 @@ Supported options:
Which report to produce. Can be multiple reporters separated by comma.
Example:
Example:
```bash
npx playwright merge-reports --reporter=html,github ./blob-reports
@ -162,8 +164,8 @@ Supported options:
additional configuration to the output reporter. This configuration file can differ from
the one used during the creation of blob reports.
Example:
Example:
```bash
npx playwright merge-reports --config=merge.config.ts ./blob-reports
```

View file

@ -7,7 +7,7 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
## Introduction
UI Mode let's you explore, run and debug tests with a time travel experience complete with watch mode. All test files are loaded into the testing sidebar where you can expand each file and describe block to individually run, view, watch and debug each test. Filter tests by **text** or **@tag** or by **passed**, **failed** and **skipped** tests as well as by [**projects**](./test-projects) as set in your `playwright.config` file. See a full trace of your tests and hover back and forward over each action to see what was happening during each step and pop out the DOM snapshot to a separate window for a better debugging experience.
UI Mode lets you explore, run and debug tests with a time travel experience complete with watch mode. All test files are loaded into the testing sidebar where you can expand each file and describe block to individually run, view, watch and debug each test. Filter tests by **text** or **@tag** or by **passed**, **failed** and **skipped** tests as well as by [**projects**](./test-projects) as set in your `playwright.config` file. See a full trace of your tests and hover back and forward over each action to see what was happening during each step and pop out the DOM snapshot to a separate window for a better debugging experience.
<LiteYouTube
id="d0u6XhXknzU"

View file

@ -99,7 +99,7 @@ await expect(page.getByText('the lion king')).toBeVisible();
await expect(page.getByText('the mummy')).toBeHidden();
```
When you cannot find a suitable assertion, use [`expect.poll`](./test-assertions#polling) instead.
When you cannot find a suitable assertion, use [`expect.poll`](./test-assertions#expectpoll) instead.
```js
await expect.poll(async () => {

View file

@ -10,7 +10,6 @@ Playwright Trace Viewer is a GUI tool that lets you explore recorded Playwright
**You will learn**
- How to record a trace
- How to open the HTML report
- How to open the trace viewer
## Recording a trace
@ -124,8 +123,6 @@ public class Tests : PageTest
[TearDown]
public async Task TearDown()
{
// This will produce e.g.:
// bin/Debug/net8.0/playwright-traces/PlaywrightTests.Tests.Test1.zip
await Context.Tracing.StopAsync(new()
{
Path = Path.Combine(
@ -152,17 +149,17 @@ using System.Text.RegularExpressions;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTestsMSTest;
namespace PlaywrightTests;
[TestClass]
public class UnitTest1 : PageTest
public class ExampleTest : PageTest
{
[TestInitialize]
public async Task TestInitialize()
{
await Context.Tracing.StartAsync(new()
{
Title = TestContext.TestName,
Title = TestContext.CurrentContext.Test.ClassName + "." + TestContext.
Screenshots = true,
Snapshots = true,
Sources = true
@ -172,20 +169,18 @@ public class UnitTest1 : PageTest
[TestCleanup]
public async Task TestCleanup()
{
// This will produce e.g.:
// bin/Debug/net8.0/playwright-traces/PlaywrightTests.UnitTest1.zip
await Context.Tracing.StopAsync(new()
{
Path = Path.Combine(
Environment.CurrentDirectory,
"playwright-traces",
$"{TestContext.FullyQualifiedTestClassName}.zip"
$"{TestContext.CurrentContext.Test.ClassName}.{TestContext.CurrentContext.Test.Name}.zip"
)
});
}
[TestMethod]
public async Task TestYourOnlineShop()
public async Task GetStartedLink()
{
// ...
}
@ -195,11 +190,11 @@ public class UnitTest1 : PageTest
</TabItem>
</Tabs>
This will record the trace and place it into the `bin/Debug/net8.0/playwright-traces/` directory.
This will record a zip file for each test, e.g. `PlaywrightTests.ExampleTest.GetStartedLink.zip` and place it into the `bin/Debug/net8.0/playwright-traces/` directory.
## Opening the trace
You can open the saved trace using the Playwright CLI or in your browser on [`trace.playwright.dev`](https://trace.playwright.dev). Make sure to add the full path to where your `trace.zip` file is located. This should include the `test-results` directory followed by the test name and then `trace.zip`.
You can open the saved trace using the Playwright CLI or in your browser on [`trace.playwright.dev`](https://trace.playwright.dev). Make sure to add the full path to where your trace's zip file is located. Once opened you can click on each action or use the timeline to see the state of the page before and after each action. You can also inspect the log, source and network during each step of the test. The trace viewer creates a DOM snapshot so you can fully interact with it, open devtools etc.
```bash java
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace trace.zip"
@ -210,14 +205,19 @@ playwright show-trace trace.zip
```
```bash csharp
pwsh bin/Debug/netX/playwright.ps1 show-trace trace.zip
pwsh bin/Debug/net8.0/playwright.ps1 show-trace bin/Debug/net8.0/playwright-traces/PlaywrightTests.ExampleTest.GetStartedLink.zip
```
## Viewing the trace
View traces of your test by clicking through each action or hovering using the timeline and see the state of the page before and after the action. Inspect the log, source and network during each step of the test. The trace viewer creates a DOM snapshot so you can fully interact with it, open devtools etc.
######
* langs: python, java
![playwright trace viewer](https://github.com/microsoft/playwright/assets/13063165/10fe3585-8401-4051-b1c2-b2e92ac4c274)
######
* langs: csharp
![playwright trace viewer dotnet](https://github.com/microsoft/playwright/assets/13063165/4372d661-5bfa-4e1f-be65-0d2fe165a75c)
To learn more check out our detailed guide on [Trace Viewer](/trace-viewer.md).
## What's next

View file

@ -22,7 +22,7 @@ Playwright Trace Viewer is a GUI tool that lets you explore recorded Playwright
## Recording a Trace
By default the [playwright.config](/test-configuration.md#record-test-trace) file will contain the configuration needed to create a `trace.zip` file for each test. Traces are setup to run `on-first-retry` meaning they will be run on the first retry of a failed test. Also `retries` are set to 2 when running on CI and 0 locally. This means the traces will be recorded on the first retry of a failed test but not on the first run and not on the second retry.
By default the [playwright.config](./trace-viewer.md#recording-a-trace-on-ci) file will contain the configuration needed to create a `trace.zip` file for each test. Traces are setup to run `on-first-retry` meaning they will be run on the first retry of a failed test. Also `retries` are set to 2 when running on CI and 0 locally. This means the traces will be recorded on the first retry of a failed test but not on the first run and not on the second retry.
```js title="playwright.config.ts"
import { defineConfig } from '@playwright/test';

View file

@ -5,9 +5,33 @@ title: "Writing tests"
## Introduction
Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met. Playwright comes with [auto-wait](./actionability.md) built in meaning it waits for elements to be actionable prior to performing actions. Playwright provides the [Expect](./test-assertions) function to write assertions.
Playwright tests are simple, they
Take a look at the example test below to see how to write a test using using [locators](/locators.md) and web first assertions.
- **perform actions**, and
- **assert the state** against expectations.
There is no need to wait for anything prior to performing an action: Playwright
automatically waits for the wide range of [actionability](./actionability.md)
checks to pass prior to performing each action.
There is also no need to deal with the race conditions when performing the checks -
Playwright assertions are designed in a way that they describe the expectations
that need to be eventually met.
That's it! These design choices allow Playwright users to forget about flaky
timeouts and racy checks in their tests altogether.
**You will learn**
- [How to write the first test](/writing-tests.md#first-test)
- [How to perform actions](/writing-tests.md#actions)
- [How to use assertions](/writing-tests.md#assertions)
- [How tests run in isolation](/writing-tests.md#test-isolation)
- [How to use test hooks](/writing-tests.md#using-test-hooks)
## First test
Take a look at the following example to see how to write a test.
<Tabs
groupId="test-runners"
@ -30,30 +54,28 @@ namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class Tests : PageTest
public class ExampleTest : PageTest
{
[Test]
public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
public async Task HasTitle()
{
await Page.GotoAsync("https://playwright.dev");
// Expect a title "to contain" a substring.
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
}
// create a locator
var getStarted = Page.GetByRole(AriaRole.Link, new() { Name = "Get started" });
// Expect an attribute "to be strictly equal" to the value.
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro");
[Test]
public async Task GetStartedLink()
{
await Page.GotoAsync("https://playwright.dev");
// Click the get started link.
await getStarted.ClickAsync();
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
// Expects page to have a heading with the name of Installation.
await Expect(Page
.GetByRole(AriaRole.Heading, new() { Name = "Installation" }))
.ToBeVisibleAsync();
}
await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync();
}
}
```
@ -70,55 +92,109 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace PlaywrightTests;
[TestClass]
public class UnitTest1 : PageTest
public class ExampleTest : PageTest
{
[TestMethod]
public async Task HomepageHasPlaywrightInTitleAndGetStartedLinkLinkingtoTheIntroPage()
public async Task HasTitle()
{
await Page.GotoAsync("https://playwright.dev");
// Expect a title "to contain" a substring.
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
}
// create a locator
var getStarted = Page.GetByRole(AriaRole.Link, new() { Name = "Get started" });
// Expect an attribute "to be strictly equal" to the value.
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/intro");
[TestMethod]
public async Task GetStartedLink()
{
await Page.GotoAsync("https://playwright.dev");
// Click the get started link.
await getStarted.ClickAsync();
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
// Expects the URL to contain intro.
await Expect(Page).ToHaveURLAsync(new Regex(".*intro"));
}
// Expects page to have a heading with the name of Installation.
await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync();
}
}
```
</TabItem>
</Tabs>
### Assertions
## Actions
### Navigation
Most of the tests will start by navigating the page to a URL. After that, the test
will be able to interact with the page elements.
```csharp
await Page.GotoAsync("https://playwright.dev");
```
Playwright will wait for the page to reach the load state prior to moving forward.
Learn more about the [`method: Page.goto`] options.
### Interactions
Performing actions starts with locating the elements. Playwright uses [Locators API](./locators.md) for that. Locators represent a way to find element(s) on the page at any moment, learn more about the [different types](./locators.md) of locators available. Playwright will wait for the element to be [actionable](./actionability.md) prior to performing the action, so there is no need to wait for it to become available.
```csharp
// Create a locator.
var getStarted = Page.GetByRole(AriaRole.Link, new() { Name = "Get started" });
// Click it.
await getStarted.ClickAsync();
```
In most cases, it'll be written in one line:
```csharp
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
```
### Basic actions
This is the list of the most popular Playwright actions. Note that there are many more, so make sure to check the [Locator API](./api/class-locator.md) section to
learn more about them.
| Action | Description |
| :- | :- |
| [`method: Locator.check`] | Check the input checkbox |
| [`method: Locator.click`] | Click the element |
| [`method: Locator.uncheck`] | Uncheck the input checkbox |
| [`method: Locator.hover`] | Hover mouse over the element |
| [`method: Locator.fill`] | Fill the form field, input text |
| [`method: Locator.focus`] | Focus the element |
| [`method: Locator.press`] | Press single key |
| [`method: Locator.setInputFiles`] | Pick files to upload |
| [`method: Locator.selectOption`] | Select option in the drop down |
## Assertions
Playwright provides an async function called [Expect](./test-assertions) to assert and wait until the expected condition is met.
```csharp
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
```
```
Here is the list of the most popular async assertions. Note that there are [many more](./test-assertions.md) to get familiar with:
| Assertion | Description |
| :- | :- |
| [`method: LocatorAssertions.toBeChecked`] | Checkbox is checked |
| [`method: LocatorAssertions.toBeEnabled`] | Control is enabled |
| [`method: LocatorAssertions.toBeVisible`] | Element is visible |
| [`method: LocatorAssertions.toContainText`] | Element contains text |
| [`method: LocatorAssertions.toHaveAttribute`] | Element has attribute |
| [`method: LocatorAssertions.toHaveCount`] | List of elements has given length |
| [`method: LocatorAssertions.toHaveText`] | Element matches text |
| [`method: LocatorAssertions.toHaveValue`] | Input element has value |
| [`method: PageAssertions.toHaveTitle`] | Page has title |
| [`method: PageAssertions.toHaveURL`] | Page has URL |
### Locators
[Locators](./locators.md) are the central piece of Playwright's auto-waiting and retry-ability. Locators represent a way to find element(s) on the page at any moment and are used to perform actions on elements such as `.ClickAsync` `.FillAsync` etc.
```csharp
var getStarted = Page.GetByRole(AriaRole.Link, new() { Name = "Get started" });
await Expect(getStarted).ToHaveAttributeAsync("href", "/docs/installation");
await getStarted.ClickAsync();
```
### Test Isolation
## Test Isolation
The Playwright NUnit and MSTest test framework base classes will isolate each test from each other by providing a separate `Page` instance. Pages are isolated between tests due to the Browser Context, which is equivalent to a brand new browser profile, where every test gets a fresh environment, even when multiple tests run in a single Browser.
@ -141,7 +217,7 @@ namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class Tests : PageTest
public class ExampleTest : PageTest
{
[Test]
public async Task BasicTest()
@ -162,7 +238,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace PlaywrightTests;
[TestClass]
public class UnitTest1 : PageTest
public class ExampleTest : PageTest
{
[TestMethod]
public async Task BasicTest()
@ -175,7 +251,7 @@ public class UnitTest1 : PageTest
</TabItem>
</Tabs>
### Using Test Hooks
## Using Test Hooks
You can use `SetUp`/`TearDown` in NUnit or `TestInitialize`/`TestCleanup` in MSTest to prepare and clean up your test environment:
@ -198,7 +274,7 @@ namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class Tests : PageTest
public class ExampleTest : PageTest
{
[Test]
public async Task MainNavigation()
@ -226,7 +302,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace PlaywrightTests;
[TestClass]
public class UnitTest1 : PageTest
public class ExampleTest : PageTest
{
[TestMethod]
public async Task MainNavigation()
@ -251,3 +327,5 @@ public class UnitTest1 : PageTest
- [Run single test, multiple tests, headed mode](./running-tests.md)
- [Generate tests with Codegen](./codegen.md)
- [See a trace of your tests](./trace-viewer-intro.md)
- [Run tests on CI](./ci-intro.md)
- [Learn more about the NUnit and MSTest base classes](./test-runners.md)

433
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "playwright-internal",
"version": "1.43.0-next",
"version": "1.45.0-next",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "playwright-internal",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"workspaces": [
"packages/*"
@ -61,7 +61,7 @@
"socksv5": "0.0.6",
"ssim.js": "^3.5.0",
"typescript": "^5.3.2",
"vite": "^5.0.12",
"vite": "^5.0.13",
"ws": "^8.5.0",
"xml2js": "^0.5.0",
"yaml": "^2.2.2"
@ -853,9 +853,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
"cpu": [
"ppc64"
],
@ -1518,9 +1518,9 @@
"link": true
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz",
"integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==",
"cpu": [
"arm"
],
@ -1530,9 +1530,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.5.tgz",
"integrity": "sha512-f14d7uhAMtsCGjAYwZGv6TwuS3IFaM4ZnGMUn3aCBgkcHAYErhV1Ad97WzBvS2o0aaDv4mVz+syiN0ElMyfBPg==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz",
"integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==",
"cpu": [
"arm64"
],
@ -1542,9 +1542,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.5.tgz",
"integrity": "sha512-ndoXeLx455FffL68OIUrVr89Xu1WLzAG4n65R8roDlCoYiQcGGg6MALvs2Ap9zs7AHg8mpHtMpwC8jBBjZrT/w==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz",
"integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==",
"cpu": [
"arm64"
],
@ -1554,9 +1554,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.5.tgz",
"integrity": "sha512-UmElV1OY2m/1KEEqTlIjieKfVwRg0Zwg4PLgNf0s3glAHXBN99KLpw5A5lrSYCa1Kp63czTpVll2MAqbZYIHoA==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz",
"integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==",
"cpu": [
"x64"
],
@ -1566,9 +1566,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz",
"integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==",
"cpu": [
"arm"
],
@ -1578,9 +1578,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz",
"integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==",
"cpu": [
"arm64"
],
@ -1590,9 +1590,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz",
"integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==",
"cpu": [
"arm64"
],
@ -1601,10 +1601,22 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz",
"integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==",
"cpu": [
"ppc64le"
],
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz",
"integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==",
"cpu": [
"riscv64"
],
@ -1613,10 +1625,22 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz",
"integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==",
"cpu": [
"s390x"
],
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz",
"integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==",
"cpu": [
"x64"
],
@ -1626,9 +1650,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz",
"integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==",
"cpu": [
"x64"
],
@ -1638,9 +1662,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz",
"integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==",
"cpu": [
"arm64"
],
@ -1650,9 +1674,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz",
"integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==",
"cpu": [
"ia32"
],
@ -1662,9 +1686,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"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==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz",
"integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==",
"cpu": [
"x64"
],
@ -6009,9 +6033,9 @@
"link": true
},
"node_modules/postcss": {
"version": "8.4.33",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"funding": [
{
"type": "opencollective",
@ -6029,7 +6053,7 @@
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
@ -6407,9 +6431,9 @@
}
},
"node_modules/rollup": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.5.tgz",
"integrity": "sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz",
"integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==",
"dependencies": {
"@types/estree": "1.0.5"
},
@ -6421,19 +6445,21 @@
"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",
"@rollup/rollup-android-arm-eabi": "4.14.0",
"@rollup/rollup-android-arm64": "4.14.0",
"@rollup/rollup-darwin-arm64": "4.14.0",
"@rollup/rollup-darwin-x64": "4.14.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.14.0",
"@rollup/rollup-linux-arm64-gnu": "4.14.0",
"@rollup/rollup-linux-arm64-musl": "4.14.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.14.0",
"@rollup/rollup-linux-riscv64-gnu": "4.14.0",
"@rollup/rollup-linux-s390x-gnu": "4.14.0",
"@rollup/rollup-linux-x64-gnu": "4.14.0",
"@rollup/rollup-linux-x64-musl": "4.14.0",
"@rollup/rollup-win32-arm64-msvc": "4.14.0",
"@rollup/rollup-win32-ia32-msvc": "4.14.0",
"@rollup/rollup-win32-x64-msvc": "4.14.0",
"fsevents": "~2.3.2"
}
},
@ -6740,9 +6766,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"engines": {
"node": ">=0.10.0"
}
@ -7232,9 +7258,9 @@
}
},
"node_modules/undici": {
"version": "5.28.3",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz",
"integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==",
"version": "5.28.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
"dev": true,
"dependencies": {
"@fastify/busboy": "^2.0.0"
@ -7345,13 +7371,13 @@
}
},
"node_modules/vite": {
"version": "5.0.12",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
"integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==",
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
"integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
"dependencies": {
"esbuild": "^0.19.3",
"postcss": "^8.4.32",
"rollup": "^4.2.0"
"esbuild": "^0.20.1",
"postcss": "^8.4.38",
"rollup": "^4.13.0"
},
"bin": {
"vite": "bin/vite.js"
@ -7417,9 +7443,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
"cpu": [
"arm"
],
@ -7432,9 +7458,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
"cpu": [
"arm64"
],
@ -7447,9 +7473,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
"cpu": [
"x64"
],
@ -7462,9 +7488,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
"cpu": [
"arm64"
],
@ -7477,9 +7503,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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
"cpu": [
"x64"
],
@ -7492,9 +7518,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
"cpu": [
"arm64"
],
@ -7507,9 +7533,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
"cpu": [
"x64"
],
@ -7522,9 +7548,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
"cpu": [
"arm"
],
@ -7537,9 +7563,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
"cpu": [
"arm64"
],
@ -7552,9 +7578,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
"cpu": [
"ia32"
],
@ -7567,9 +7593,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
"cpu": [
"loong64"
],
@ -7582,9 +7608,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
"cpu": [
"mips64el"
],
@ -7597,9 +7623,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
"cpu": [
"ppc64"
],
@ -7612,9 +7638,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
"cpu": [
"riscv64"
],
@ -7627,9 +7653,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
"cpu": [
"s390x"
],
@ -7642,9 +7668,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
"cpu": [
"x64"
],
@ -7657,9 +7683,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
"cpu": [
"x64"
],
@ -7672,9 +7698,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
"cpu": [
"x64"
],
@ -7687,9 +7713,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
"cpu": [
"x64"
],
@ -7702,9 +7728,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
"cpu": [
"arm64"
],
@ -7717,9 +7743,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
"cpu": [
"ia32"
],
@ -7732,9 +7758,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
"cpu": [
"x64"
],
@ -7747,9 +7773,9 @@
}
},
"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==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@ -7758,29 +7784,29 @@
"node": ">=12"
},
"optionalDependencies": {
"@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"
"@esbuild/aix-ppc64": "0.20.2",
"@esbuild/android-arm": "0.20.2",
"@esbuild/android-arm64": "0.20.2",
"@esbuild/android-x64": "0.20.2",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@esbuild/freebsd-arm64": "0.20.2",
"@esbuild/freebsd-x64": "0.20.2",
"@esbuild/linux-arm": "0.20.2",
"@esbuild/linux-arm64": "0.20.2",
"@esbuild/linux-ia32": "0.20.2",
"@esbuild/linux-loong64": "0.20.2",
"@esbuild/linux-mips64el": "0.20.2",
"@esbuild/linux-ppc64": "0.20.2",
"@esbuild/linux-riscv64": "0.20.2",
"@esbuild/linux-s390x": "0.20.2",
"@esbuild/linux-x64": "0.20.2",
"@esbuild/netbsd-x64": "0.20.2",
"@esbuild/openbsd-x64": "0.20.2",
"@esbuild/sunos-x64": "0.20.2",
"@esbuild/win32-arm64": "0.20.2",
"@esbuild/win32-ia32": "0.20.2",
"@esbuild/win32-x64": "0.20.2"
}
},
"node_modules/vitefu": {
@ -8136,10 +8162,10 @@
}
},
"packages/playwright": {
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.43.0-next"
"playwright-core": "1.45.0-next"
},
"bin": {
"playwright": "cli.js"
@ -8153,11 +8179,11 @@
},
"packages/playwright-browser-chromium": {
"name": "@playwright/browser-chromium",
"version": "1.43.0-next",
"version": "1.45.0-next",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.43.0-next"
"playwright-core": "1.45.0-next"
},
"engines": {
"node": ">=16"
@ -8165,11 +8191,11 @@
},
"packages/playwright-browser-firefox": {
"name": "@playwright/browser-firefox",
"version": "1.43.0-next",
"version": "1.45.0-next",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.43.0-next"
"playwright-core": "1.45.0-next"
},
"engines": {
"node": ">=16"
@ -8177,22 +8203,22 @@
},
"packages/playwright-browser-webkit": {
"name": "@playwright/browser-webkit",
"version": "1.43.0-next",
"version": "1.45.0-next",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.43.0-next"
"playwright-core": "1.45.0-next"
},
"engines": {
"node": ">=16"
}
},
"packages/playwright-chromium": {
"version": "1.43.0-next",
"version": "1.45.0-next",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.43.0-next"
"playwright-core": "1.45.0-next"
},
"bin": {
"playwright": "cli.js"
@ -8202,7 +8228,7 @@
}
},
"packages/playwright-core": {
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
@ -8213,15 +8239,12 @@
},
"packages/playwright-ct-core": {
"name": "@playwright/experimental-ct-core",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.43.0-next",
"playwright-core": "1.43.0-next",
"vite": "^5.0.12"
},
"bin": {
"playwright": "cli.js"
"playwright": "1.45.0-next",
"playwright-core": "1.45.0-next",
"vite": "^5.2.8"
},
"engines": {
"node": ">=16"
@ -8229,10 +8252,10 @@
},
"packages/playwright-ct-react": {
"name": "@playwright/experimental-ct-react",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next",
"@playwright/experimental-ct-core": "1.45.0-next",
"@vitejs/plugin-react": "^4.2.1"
},
"bin": {
@ -8244,10 +8267,10 @@
},
"packages/playwright-ct-react17": {
"name": "@playwright/experimental-ct-react17",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next",
"@playwright/experimental-ct-core": "1.45.0-next",
"@vitejs/plugin-react": "^4.2.1"
},
"bin": {
@ -8259,10 +8282,10 @@
},
"packages/playwright-ct-solid": {
"name": "@playwright/experimental-ct-solid",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next",
"@playwright/experimental-ct-core": "1.45.0-next",
"vite-plugin-solid": "^2.7.0"
},
"bin": {
@ -8277,10 +8300,10 @@
},
"packages/playwright-ct-svelte": {
"name": "@playwright/experimental-ct-svelte",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next",
"@playwright/experimental-ct-core": "1.45.0-next",
"@sveltejs/vite-plugin-svelte": "^3.0.1"
},
"bin": {
@ -8295,10 +8318,10 @@
},
"packages/playwright-ct-vue": {
"name": "@playwright/experimental-ct-vue",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next",
"@playwright/experimental-ct-core": "1.45.0-next",
"@vitejs/plugin-vue": "^4.2.1"
},
"bin": {
@ -8310,10 +8333,10 @@
},
"packages/playwright-ct-vue2": {
"name": "@playwright/experimental-ct-vue2",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next",
"@playwright/experimental-ct-core": "1.45.0-next",
"@vitejs/plugin-vue2": "^2.2.0"
},
"bin": {
@ -8362,11 +8385,11 @@
}
},
"packages/playwright-firefox": {
"version": "1.43.0-next",
"version": "1.45.0-next",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.43.0-next"
"playwright-core": "1.45.0-next"
},
"bin": {
"playwright": "cli.js"
@ -8377,10 +8400,10 @@
},
"packages/playwright-test": {
"name": "@playwright/test",
"version": "1.43.0-next",
"version": "1.45.0-next",
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.43.0-next"
"playwright": "1.45.0-next"
},
"bin": {
"playwright": "cli.js"
@ -8390,11 +8413,11 @@
}
},
"packages/playwright-webkit": {
"version": "1.43.0-next",
"version": "1.45.0-next",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.43.0-next"
"playwright-core": "1.45.0-next"
},
"bin": {
"playwright": "cli.js"

View file

@ -1,7 +1,7 @@
{
"name": "playwright-internal",
"private": true,
"version": "1.43.0-next",
"version": "1.45.0-next",
"description": "A high-level API to automate web browsers",
"repository": {
"type": "git",
@ -99,7 +99,7 @@
"socksv5": "0.0.6",
"ssim.js": "^3.5.0",
"typescript": "^5.3.2",
"vite": "^5.0.12",
"vite": "^5.0.13",
"ws": "^8.5.0",
"xml2js": "^0.5.0",
"yaml": "^2.2.2"

View file

@ -98,27 +98,7 @@ export class Filter {
}
matches(test: TestCaseSummary): boolean {
if (!(test as any).searchValues) {
let status = 'passed';
if (test.outcome === 'unexpected')
status = 'failed';
if (test.outcome === 'flaky')
status = 'flaky';
if (test.outcome === 'skipped')
status = 'skipped';
const searchValues: SearchValues = {
text: (status + ' ' + test.projectName + ' ' + test.tags.join(' ') + ' ' + test.location.file + ' ' + test.path.join(' ') + ' ' + test.title).toLowerCase(),
project: test.projectName.toLowerCase(),
status: status as any,
file: test.location.file,
line: String(test.location.line),
column: String(test.location.column),
labels: test.tags.map(tag => tag.toLowerCase()),
};
(test as any).searchValues = searchValues;
}
const searchValues = (test as any).searchValues as SearchValues;
const searchValues = cacheSearchValues(test);
if (this.project.length) {
const matches = !!this.project.find(p => searchValues.project.includes(p));
if (!matches)
@ -128,6 +108,9 @@ export class Filter {
const matches = !!this.status.find(s => searchValues.status.includes(s));
if (!matches)
return false;
} else {
if (searchValues.status === 'skipped')
return false;
}
if (this.text.length) {
for (const text of this.text) {
@ -159,3 +142,50 @@ type SearchValues = {
labels: string[];
};
const searchValuesSymbol = Symbol('searchValues');
function cacheSearchValues(test: TestCaseSummary): SearchValues {
const cached = (test as any)[searchValuesSymbol] as SearchValues | undefined;
if (cached)
return cached;
let status: SearchValues['status'] = 'passed';
if (test.outcome === 'unexpected')
status = 'failed';
if (test.outcome === 'flaky')
status = 'flaky';
if (test.outcome === 'skipped')
status = 'skipped';
const searchValues: SearchValues = {
text: (status + ' ' + test.projectName + ' ' + test.tags.join(' ') + ' ' + test.location.file + ' ' + test.path.join(' ') + ' ' + test.title).toLowerCase(),
project: test.projectName.toLowerCase(),
status,
file: test.location.file,
line: String(test.location.line),
column: String(test.location.column),
labels: test.tags.map(tag => tag.toLowerCase()),
};
(test as any)[searchValuesSymbol] = searchValues;
return searchValues;
}
export function filterWithToken(tokens: string[], token: string, append: boolean): string {
if (append) {
if (!tokens.includes(token))
return '#?q=' + [...tokens, token].join(' ').trim();
return '#?q=' + tokens.filter(t => t !== token).join(' ').trim();
}
// if metaKey or ctrlKey is not pressed, replace existing token with new token
let prefix: 's:' | 'p:' | '@';
if (token.startsWith('s:'))
prefix = 's:';
if (token.startsWith('p:'))
prefix = 'p:';
if (token.startsWith('@'))
prefix = '@';
const newTokens = tokens.filter(t => !t.startsWith(prefix));
newTokens.push(token);
return '#?q=' + newTokens.join(' ').trim();
}

View file

@ -28,7 +28,7 @@ test('should render counters', async ({ mount }) => {
skipped: 10,
ok: false,
}} filterText='' setFilterText={() => {}}></HeaderView>);
await expect(component.locator('a', { hasText: 'All' }).locator('.counter')).toHaveText('100');
await expect(component.locator('a', { hasText: 'All' }).locator('.counter')).toHaveText('90');
await expect(component.locator('a', { hasText: 'Passed' }).locator('.counter')).toHaveText('42');
await expect(component.locator('a', { hasText: 'Failed' }).locator('.counter')).toHaveText('31');
await expect(component.locator('a', { hasText: 'Flaky' }).locator('.counter')).toHaveText('17');

View file

@ -22,6 +22,7 @@ import './headerView.css';
import * as icons from './icons';
import { Link, navigate } from './links';
import { statusIcon } from './statusIcon';
import { filterWithToken } from './filter';
export const HeaderView: React.FC<React.PropsWithChildren<{
stats: Stats,
@ -64,20 +65,23 @@ export const HeaderView: React.FC<React.PropsWithChildren<{
const StatsNavView: React.FC<{
stats: Stats
}> = ({ stats }) => {
const searchParams = new URLSearchParams(window.location.hash.slice(1));
const q = searchParams.get('q')?.toString() || '';
const tokens = q.split(' ');
return <nav>
<Link className='subnav-item' href='#?'>
All <span className='d-inline counter'>{stats.total}</span>
All <span className='d-inline counter'>{stats.total - stats.skipped}</span>
</Link>
<Link className='subnav-item' href='#?q=s:passed'>
<Link className='subnav-item' click={filterWithToken(tokens, 's:passed', false)} ctrlClick={filterWithToken(tokens, 's:passed', true)}>
Passed <span className='d-inline counter'>{stats.expected}</span>
</Link>
<Link className='subnav-item' href='#?q=s:failed'>
<Link className='subnav-item' click={filterWithToken(tokens, 's:failed', false)} ctrlClick={filterWithToken(tokens, 's:failed', true)}>
{!!stats.unexpected && statusIcon('unexpected')} Failed <span className='d-inline counter'>{stats.unexpected}</span>
</Link>
<Link className='subnav-item' href='#?q=s:flaky'>
<Link className='subnav-item' click={filterWithToken(tokens, 's:flaky', false)} ctrlClick={filterWithToken(tokens, 's:flaky', true)}>
{!!stats.flaky && statusIcon('flaky')} Flaky <span className='d-inline counter'>{stats.flaky}</span>
</Link>
<Link className='subnav-item' href='#?q=s:skipped'>
<Link className='subnav-item' click={filterWithToken(tokens, 's:skipped', false)} ctrlClick={filterWithToken(tokens, 's:skipped', true)}>
Skipped <span className='d-inline counter'>{stats.skipped}</span>
</Link>
</nav>;

View file

@ -41,12 +41,19 @@ export const Route: React.FunctionComponent<{
};
export const Link: React.FunctionComponent<{
href: string,
href?: string,
click?: string,
ctrlClick?: string,
className?: string,
title?: string,
children: any,
}> = ({ href, className, children, title }) => {
return <a style={{ textDecoration: 'none', color: 'var(--color-fg-default)' }} className={`${className || ''}`} href={href} title={title}>{children}</a>;
}> = ({ href, click, ctrlClick, className, children, title }) => {
return <a style={{ textDecoration: 'none', color: 'var(--color-fg-default)', cursor: 'pointer' }} href={href} className={`${className || ''}`} title={title} onClick={e => {
if (click) {
e.preventDefault();
navigate(e.metaKey || e.ctrlKey ? ctrlClick || click : click);
}
}}>{children}</a>;
};
export const ProjectLink: React.FunctionComponent<{

Some files were not shown because too many files have changed in this diff Show more