Compare commits
30 commits
main
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c836cefb12 | ||
|
|
88bc8afc78 | ||
|
|
9e855d5b9a | ||
|
|
9365eb5dac | ||
|
|
f92b2339fe | ||
|
|
008722b2d9 | ||
|
|
1dc8b3cbdc | ||
|
|
fbc770c804 | ||
|
|
1046fe0455 | ||
|
|
1781bf35b3 | ||
|
|
b52a21030f | ||
|
|
2128fac196 | ||
|
|
2ba644852b | ||
|
|
4b0eca4d22 | ||
|
|
e3c5986c5b | ||
|
|
b3aaee0248 | ||
|
|
120cdf664b | ||
|
|
a70a96ab25 | ||
|
|
53f51a8cf1 | ||
|
|
2a00ca8453 | ||
|
|
0e6434013b | ||
|
|
cb0f456e46 | ||
|
|
698823a78e | ||
|
|
c0fa804367 | ||
|
|
7a32228aed | ||
|
|
0e31acea8f | ||
|
|
b2a39ffc61 | ||
|
|
1eea46bd66 | ||
|
|
4c53e56cb4 | ||
|
|
3f36d7ff51 |
11
.devcontainer/devcontainer.json
Normal file
11
.devcontainer/devcontainer.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "Playwright",
|
||||
"image": "mcr.microsoft.com/playwright:next",
|
||||
"postCreateCommand": "npm install && npm run build && apt-get update && apt-get install -y software-properties-common && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - && add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" && apt-get install -y docker-ce-cli",
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash"
|
||||
},
|
||||
"runArgs": [
|
||||
"-v", "/var/run/docker.sock:/var/run/docker.sock"
|
||||
]
|
||||
}
|
||||
18
.eslintignore
Normal file
18
.eslintignore
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
test/assets/modernizr.js
|
||||
/tests/third_party/
|
||||
/packages/*/lib/
|
||||
*.js
|
||||
/packages/playwright-core/src/generated/*
|
||||
/packages/playwright-core/src/third_party/
|
||||
/packages/playwright-core/types/*
|
||||
/packages/playwright-ct-core/src/generated/*
|
||||
/index.d.ts
|
||||
node_modules/
|
||||
**/*.d.ts
|
||||
output/
|
||||
test-results/
|
||||
/tests/components/
|
||||
/tests/installation/fixture-scripts/
|
||||
DEPS
|
||||
.cache/
|
||||
/utils/
|
||||
15
.eslintrc-with-ts-config.js
Normal file
15
.eslintrc-with-ts-config.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
module.exports = {
|
||||
extends: "./.eslintrc.js",
|
||||
parserOptions: {
|
||||
ecmaVersion: 9,
|
||||
sourceType: "module",
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-base-to-string": "error",
|
||||
"@typescript-eslint/no-unnecessary-boolean-literal-compare": 2,
|
||||
},
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json"
|
||||
},
|
||||
};
|
||||
136
.eslintrc.js
Normal file
136
.eslintrc.js
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
module.exports = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint", "notice"],
|
||||
parserOptions: {
|
||||
ecmaVersion: 9,
|
||||
sourceType: "module",
|
||||
},
|
||||
extends: [
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
|
||||
settings: {
|
||||
react: { version: "18" }
|
||||
},
|
||||
|
||||
/**
|
||||
* ESLint rules
|
||||
*
|
||||
* All available rules: http://eslint.org/docs/rules/
|
||||
*
|
||||
* Rules take the following form:
|
||||
* "rule-name", [severity, { opts }]
|
||||
* Severity: 2 == error, 1 == warning, 0 == off.
|
||||
*/
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": [2, {args: "none"}],
|
||||
"@typescript-eslint/consistent-type-imports": [2, {disallowTypeAnnotations: false}],
|
||||
/**
|
||||
* Enforced rules
|
||||
*/
|
||||
// syntax preferences
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"quotes": [2, "single", {
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}],
|
||||
"jsx-quotes": [2, "prefer-single"],
|
||||
"no-extra-semi": 2,
|
||||
"@typescript-eslint/semi": [2],
|
||||
"comma-style": [2, "last"],
|
||||
"wrap-iife": [2, "inside"],
|
||||
"spaced-comment": [2, "always", {
|
||||
"markers": ["*"]
|
||||
}],
|
||||
"eqeqeq": [2],
|
||||
"accessor-pairs": [2, {
|
||||
"getWithoutSet": false,
|
||||
"setWithoutGet": false
|
||||
}],
|
||||
"brace-style": [2, "1tbs", {"allowSingleLine": true}],
|
||||
"curly": [2, "multi-or-nest", "consistent"],
|
||||
"new-parens": 2,
|
||||
"arrow-parens": [2, "as-needed"],
|
||||
"prefer-const": 2,
|
||||
"quote-props": [2, "consistent"],
|
||||
"nonblock-statement-body-position": [2, "below"],
|
||||
|
||||
// anti-patterns
|
||||
"no-var": 2,
|
||||
"no-with": 2,
|
||||
"no-multi-str": 2,
|
||||
"no-caller": 2,
|
||||
"no-implied-eval": 2,
|
||||
"no-labels": 2,
|
||||
"no-new-object": 2,
|
||||
"no-octal-escape": 2,
|
||||
"no-self-compare": 2,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-cond-assign": 2,
|
||||
"no-debugger": 2,
|
||||
"no-dupe-keys": 2,
|
||||
"no-duplicate-case": 2,
|
||||
"no-empty-character-class": 2,
|
||||
"no-unreachable": 2,
|
||||
"no-unsafe-negation": 2,
|
||||
"radix": 2,
|
||||
"valid-typeof": 2,
|
||||
"no-implicit-globals": [2],
|
||||
"no-unused-expressions": [2, { "allowShortCircuit": true, "allowTernary": true, "allowTaggedTemplates": true}],
|
||||
"no-proto": 2,
|
||||
|
||||
// es2015 features
|
||||
"require-yield": 2,
|
||||
"template-curly-spacing": [2, "never"],
|
||||
|
||||
// spacing details
|
||||
"space-infix-ops": 2,
|
||||
"space-in-parens": [2, "never"],
|
||||
"array-bracket-spacing": [2, "never"],
|
||||
"comma-spacing": [2, { "before": false, "after": true }],
|
||||
"keyword-spacing": [2, "always"],
|
||||
"space-before-function-paren": [2, {
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}],
|
||||
"no-whitespace-before-property": 2,
|
||||
"keyword-spacing": [2, {
|
||||
"overrides": {
|
||||
"if": {"after": true},
|
||||
"else": {"after": true},
|
||||
"for": {"after": true},
|
||||
"while": {"after": true},
|
||||
"do": {"after": true},
|
||||
"switch": {"after": true},
|
||||
"return": {"after": true}
|
||||
}
|
||||
}],
|
||||
"arrow-spacing": [2, {
|
||||
"after": true,
|
||||
"before": true
|
||||
}],
|
||||
"@typescript-eslint/func-call-spacing": 2,
|
||||
"@typescript-eslint/type-annotation-spacing": 2,
|
||||
|
||||
// file whitespace
|
||||
"no-multiple-empty-lines": [2, {"max": 2}],
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-trailing-spaces": 2,
|
||||
"linebreak-style": [ process.platform === "win32" ? 0 : 2, "unix" ],
|
||||
"indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }],
|
||||
"key-spacing": [2, {
|
||||
"beforeColon": false
|
||||
}],
|
||||
|
||||
// copyright
|
||||
"notice/notice": [2, {
|
||||
"mustMatch": "Copyright",
|
||||
"templateFile": require("path").join(__dirname, "utils", "copyright.js"),
|
||||
}],
|
||||
|
||||
// react
|
||||
"react/react-in-jsx-scope": 0
|
||||
}
|
||||
};
|
||||
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
|
|
@ -1,14 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
2
.github/workflows/infra.yml
vendored
2
.github/workflows/infra.yml
vendored
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
- name: Audit prod NPM dependencies
|
||||
run: node utils/check_audit.js
|
||||
run: npm audit --omit dev
|
||||
lint-snippets:
|
||||
name: "Lint snippets"
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -27,11 +27,7 @@ jobs:
|
|||
repo: context.repo.repo,
|
||||
commit_sha: context.sha,
|
||||
});
|
||||
const commitHeader = data.message.split('\n')[0];
|
||||
const prMatch = commitHeader.match(/#(\d+)/);
|
||||
const formattedCommit = prMatch
|
||||
? `https://github.com/microsoft/playwright/pull/${prMatch[1]}`
|
||||
: `https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${context.sha} (${commitHeader})`;
|
||||
const commitHeader = data.message.split('\n')[0].replace(/#(\d+)/g, 'https://github.com/microsoft/playwright/pull/$1');
|
||||
|
||||
const title = '[Ports]: Backport client side changes for ' + currentPlaywrightVersion;
|
||||
for (const repo of ['playwright-python', 'playwright-java', 'playwright-dotnet']) {
|
||||
|
|
@ -54,7 +50,7 @@ jobs:
|
|||
issueBody = issueCreateData.body;
|
||||
}
|
||||
const newBody = issueBody.trimEnd() + `
|
||||
- [ ] ${formattedCommit}`;
|
||||
- [ ] https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${context.sha} (${commitHeader})`;
|
||||
const data = await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: repo,
|
||||
|
|
|
|||
|
|
@ -3,21 +3,9 @@ name: Roll Browser into Playwright
|
|||
on:
|
||||
repository_dispatch:
|
||||
types: [roll_into_pw]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
browser:
|
||||
description: 'Browser name, e.g. chromium'
|
||||
required: true
|
||||
type: string
|
||||
revision:
|
||||
description: 'Browser revision without v prefix, e.g. 1234'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
BROWSER: ${{ github.event.client_payload.browser || github.event.inputs.browser }}
|
||||
REVISION: ${{ github.event.client_payload.revision || github.event.inputs.revision }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
|
@ -36,19 +24,19 @@ jobs:
|
|||
run: npx playwright install-deps
|
||||
- name: Roll to new revision
|
||||
run: |
|
||||
./utils/roll_browser.js $BROWSER $REVISION
|
||||
./utils/roll_browser.js ${{ github.event.client_payload.browser }} ${{ github.event.client_payload.revision }}
|
||||
npm run build
|
||||
- name: Prepare branch
|
||||
id: prepare-branch
|
||||
run: |
|
||||
BRANCH_NAME="roll-into-pw-${BROWSER}/${REVISION}"
|
||||
BRANCH_NAME="roll-into-pw-${{ github.event.client_payload.browser }}/${{ github.event.client_payload.revision }}"
|
||||
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
git config --global user.name github-actions
|
||||
git config --global user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
git add .
|
||||
git commit -m "feat(${BROWSER}): roll to r${REVISION}"
|
||||
git push origin $BRANCH_NAME --force
|
||||
git commit -m "feat(${{ github.event.client_payload.browser }}): roll to r${{ github.event.client_payload.revision }}"
|
||||
git push origin $BRANCH_NAME
|
||||
- name: Create Pull Request
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
|
|
@ -59,7 +47,7 @@ jobs:
|
|||
repo: 'playwright',
|
||||
head: 'microsoft:${{ steps.prepare-branch.outputs.BRANCH_NAME }}',
|
||||
base: 'main',
|
||||
title: 'feat(${{ env.BROWSER }}): roll to r${{ env.REVISION }}',
|
||||
title: 'feat(${{ github.event.client_payload.browser }}): roll to r${{ github.event.client_payload.revision }}',
|
||||
});
|
||||
await github.rest.issues.addLabels({
|
||||
owner: 'microsoft',
|
||||
|
|
|
|||
26
.github/workflows/tests_bidi.yml
vendored
26
.github/workflows/tests_bidi.yml
vendored
|
|
@ -7,8 +7,6 @@ on:
|
|||
- main
|
||||
paths:
|
||||
- .github/workflows/tests_bidi.yml
|
||||
- packages/playwright-core/src/server/bidi/**
|
||||
- tests/bidi/**
|
||||
schedule:
|
||||
# Run every day at midnight
|
||||
- cron: '0 0 * * *'
|
||||
|
|
@ -45,27 +43,3 @@ jobs:
|
|||
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run biditest -- --project=${{ matrix.channel }}*
|
||||
env:
|
||||
PWTEST_USE_BIDI_EXPECTATIONS: '1'
|
||||
- name: Upload csv report to GitHub
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: csv-report-${{ matrix.channel }}
|
||||
path: test-results/report.csv
|
||||
retention-days: 7
|
||||
|
||||
- name: Azure Login
|
||||
if: ${{ !cancelled() && github.ref == 'refs/heads/main' }}
|
||||
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 report.csv to Azure
|
||||
if: ${{ !cancelled() && github.ref == 'refs/heads/main' }}
|
||||
run: |
|
||||
REPORT_DIR='bidi-reports'
|
||||
azcopy cp "./test-results/report.csv" "https://mspwblobreport.blob.core.windows.net/\$web/$REPORT_DIR/${{ matrix.channel }}.csv"
|
||||
echo "Report url: https://mspwblobreport.z1.web.core.windows.net/$REPORT_DIR/${{ matrix.channel }}.csv"
|
||||
env:
|
||||
AZCOPY_AUTO_LOGIN_TYPE: AZCLI
|
||||
|
|
|
|||
7
.github/workflows/tests_others.yml
vendored
7
.github/workflows/tests_others.yml
vendored
|
|
@ -147,13 +147,6 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Ubuntu Binary Installation # TODO: Remove when https://github.com/electron/electron/issues/42510 is fixed
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
run: |
|
||||
if grep -q "Ubuntu 24" /etc/os-release; then
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
fi
|
||||
shell: bash
|
||||
- uses: ./.github/actions/run-test
|
||||
with:
|
||||
browsers-to-install: chromium
|
||||
|
|
|
|||
7
.github/workflows/tests_primary.yml
vendored
7
.github/workflows/tests_primary.yml
vendored
|
|
@ -215,13 +215,6 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- run: npm install -g yarn@1
|
||||
- run: npm install -g pnpm@8
|
||||
- name: Setup Ubuntu Binary Installation # TODO: Remove when https://github.com/electron/electron/issues/42510 is fixed
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
run: |
|
||||
if grep -q "Ubuntu 24" /etc/os-release; then
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
fi
|
||||
shell: bash
|
||||
- uses: ./.github/actions/run-test
|
||||
with:
|
||||
command: npm run itest
|
||||
|
|
|
|||
28
.github/workflows/tests_secondary.yml
vendored
28
.github/workflows/tests_secondary.yml
vendored
|
|
@ -107,13 +107,6 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- run: npm install -g yarn@1
|
||||
- run: npm install -g pnpm@8
|
||||
- name: Setup Ubuntu Binary Installation # TODO: Remove when https://github.com/electron/electron/issues/42510 is fixed
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
run: |
|
||||
if grep -q "Ubuntu 24" /etc/os-release; then
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
fi
|
||||
shell: bash
|
||||
- uses: ./.github/actions/run-test
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
|
|
@ -241,27 +234,6 @@ jobs:
|
|||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
|
||||
chromium_tot_headless_shell:
|
||||
name: Chromium tip-of-tree headless-shell-${{ matrix.os }}
|
||||
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/run-test
|
||||
with:
|
||||
browsers-to-install: chromium-tip-of-tree-headless-shell
|
||||
command: npm run ctest
|
||||
bot-name: "chromium-tip-of-tree-headless-shell-${{ matrix.os }}"
|
||||
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
|
||||
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
|
||||
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree-headless-shell
|
||||
|
||||
firefox_beta:
|
||||
name: Firefox Beta ${{ matrix.os }}
|
||||
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -35,4 +35,4 @@ test-results
|
|||
.cache/
|
||||
.eslintcache
|
||||
playwright.env
|
||||
/firefox/
|
||||
firefox
|
||||
|
|
|
|||
|
|
@ -26,16 +26,6 @@ npm run watch
|
|||
npx playwright install
|
||||
```
|
||||
|
||||
**Experimental dev mode with Hot Module Replacement for recorder/trace-viewer/UI Mode**
|
||||
|
||||
```
|
||||
PW_HMR=1 npm run watch
|
||||
PW_HMR=1 npx playwright show-trace
|
||||
PW_HMR=1 npm run ctest -- --ui
|
||||
PW_HMR=1 npx playwright codegen
|
||||
PW_HMR=1 npx playwright show-report
|
||||
```
|
||||
|
||||
Playwright is a multi-package repository that uses npm workspaces. For browser APIs, look at [`packages/playwright-core`](https://github.com/microsoft/playwright/blob/main/packages/playwright-core). For test runner, see [`packages/playwright`](https://github.com/microsoft/playwright/blob/main/packages/playwright).
|
||||
|
||||
Note that some files are generated by the build, so the watch process might override your changes if done in the wrong file. For example, TypeScript types for the API are generated from the [`docs/src`](https://github.com/microsoft/playwright/blob/main/docs/src).
|
||||
|
|
@ -45,7 +35,7 @@ Coding style is fully defined in [.eslintrc](https://github.com/microsoft/playwr
|
|||
npm run lint
|
||||
```
|
||||
|
||||
Comments should have an explicit purpose and should improve readability rather than hinder it. If the code would not be understood without comments, consider re-writing the code to make it self-explanatory.
|
||||
Comments should be generally avoided. If the code would not be understood without comments, consider re-writing the code to make it self-explanatory.
|
||||
|
||||
### Write documentation
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
# How to File a Bug Report That Actually Gets Resolved
|
||||
|
||||
Make sure you’re on the latest Playwright release before filing. Check existing GitHub issues to avoid duplicates.
|
||||
|
||||
## Use the Template
|
||||
|
||||
Follow the **Bug Report** template. It guides you step-by-step:
|
||||
|
||||
- Fill it out thoroughly.
|
||||
- Clearly list the steps needed to reproduce the bug.
|
||||
- Provide what you expected to see versus what happened in reality.
|
||||
- Include system info from `npx envinfo --preset playwright`.
|
||||
|
||||
## Keep Your Repro Minimal
|
||||
|
||||
We can't parse your entire code base. Reduce it down to the absolute essentials:
|
||||
|
||||
- Start a fresh project (`npm init playwright@latest new-project`).
|
||||
- Add only the code/DOM needed to show the problem.
|
||||
- Only use major frameworks if necessary (React, Angular, static HTTP server, etc.).
|
||||
- Avoid adding extra libraries unless absolutely necessary. Note that we won't install any suspect dependencies.
|
||||
|
||||
## Why This Matters
|
||||
- Most issues that lack a repro turn out to be misconfigurations or usage errors.
|
||||
- We can't fix problems if we can’t reproduce them ourselves.
|
||||
- We can’t debug entire private projects or handle sensitive credentials.
|
||||
- Each confirmed bug will have a test in our repo, so your repro must be as clean as possible.
|
||||
|
||||
## More Help
|
||||
|
||||
- [Stack Overflow’s Minimal Reproducible Example Guide](https://stackoverflow.com/help/minimal-reproducible-example)
|
||||
- [Playwright Debugging Tools](https://playwright.dev/docs/debug)
|
||||
|
||||
## Bottom Line
|
||||
A well-isolated bug speeds up verification and resolution. Minimal, public repro or it’s unlikely we can assist.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# 🎭 Playwright
|
||||
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
|
|
@ -8,9 +8,9 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
|||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->134.0.6998.35<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->131.0.6778.33<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->135.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->132.0<!-- 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
REMOTE_URL="https://github.com/mozilla/gecko-dev"
|
||||
BASE_BRANCH="release"
|
||||
BASE_REVISION="5cfa81898f6eef8fb1abe463e5253cea5bc17f3f"
|
||||
BASE_REVISION="bc78b98043438d8ee2727a483b6e10dedfda883f"
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ class PageTarget {
|
|||
this._videoRecordingInfo = undefined;
|
||||
this._screencastRecordingInfo = undefined;
|
||||
this._dialogs = new Map();
|
||||
this.forcedColors = 'none';
|
||||
this.forcedColors = 'no-override';
|
||||
this.disableCache = false;
|
||||
this.mediumOverride = '';
|
||||
this.crossProcessCookie = {
|
||||
|
|
@ -635,8 +635,7 @@ class PageTarget {
|
|||
}
|
||||
|
||||
updateForcedColorsOverride(browsingContext = undefined) {
|
||||
const isActive = this.forcedColors === 'active' || this._browserContext.forcedColors === 'active';
|
||||
(browsingContext || this._linkedBrowser.browsingContext).forcedColorsOverride = isActive ? 'active' : 'none';
|
||||
(browsingContext || this._linkedBrowser.browsingContext).forcedColorsOverride = (this.forcedColors !== 'no-override' ? this.forcedColors : this._browserContext.forcedColors) || 'no-override';
|
||||
}
|
||||
|
||||
async setInterceptFileChooserDialog(enabled) {
|
||||
|
|
@ -859,8 +858,8 @@ function fromProtocolReducedMotion(reducedMotion) {
|
|||
function fromProtocolForcedColors(forcedColors) {
|
||||
if (forcedColors === 'active' || forcedColors === 'none')
|
||||
return forcedColors;
|
||||
if (!forcedColors)
|
||||
return 'none';
|
||||
if (forcedColors === null)
|
||||
return undefined;
|
||||
throw new Error('Unknown forced colors: ' + forcedColors);
|
||||
}
|
||||
|
||||
|
|
@ -894,7 +893,7 @@ class BrowserContext {
|
|||
this.forceOffline = false;
|
||||
this.disableCache = false;
|
||||
this.colorScheme = 'none';
|
||||
this.forcedColors = 'none';
|
||||
this.forcedColors = 'no-override';
|
||||
this.reducedMotion = 'none';
|
||||
this.videoRecordingOptions = undefined;
|
||||
this.crossProcessCookie = {
|
||||
|
|
|
|||
|
|
@ -105,10 +105,7 @@ class Juggler {
|
|||
};
|
||||
|
||||
// Force create hidden window here, otherwise its creation later closes the web socket!
|
||||
// Since https://phabricator.services.mozilla.com/D219834, hiddenDOMWindow is only available on MacOS.
|
||||
if (Services.appShell.hasHiddenWindow) {
|
||||
Services.appShell.hiddenDOMWindow;
|
||||
}
|
||||
Services.appShell.hiddenDOMWindow;
|
||||
|
||||
let pipeStopped = false;
|
||||
let browserHandler;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +1,3 @@
|
|||
REMOTE_URL="https://github.com/WebKit/WebKit.git"
|
||||
BASE_BRANCH="main"
|
||||
BASE_REVISION="76c95d6131edd36775a5eac01e297926fc974be8"
|
||||
BASE_REVISION="8ceb1da47e75a488ae4c12017a861636904acd4f"
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
#import <WebKit/WKUserContentControllerPrivate.h>
|
||||
#import <WebKit/WKWebViewConfigurationPrivate.h>
|
||||
#import <WebKit/WKWebViewPrivate.h>
|
||||
#import <WebKit/WKWebpagePreferencesPrivate.h>
|
||||
#import <WebKit/WKWebsiteDataStorePrivate.h>
|
||||
#import <WebKit/WebNSURLExtras.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
|
@ -241,8 +240,6 @@ const NSActivityOptions ActivityOptions =
|
|||
configuration.preferences._hiddenPageDOMTimerThrottlingAutoIncreases = NO;
|
||||
configuration.preferences._pageVisibilityBasedProcessSuppressionEnabled = NO;
|
||||
configuration.preferences._domTimersThrottlingEnabled = NO;
|
||||
// Do not auto play audio and video with sound.
|
||||
configuration.defaultWebpagePreferences._autoplayPolicy = _WKWebsiteAutoplayPolicyAllowWithoutSound;
|
||||
_WKProcessPoolConfiguration *processConfiguration = [[[_WKProcessPoolConfiguration alloc] init] autorelease];
|
||||
processConfiguration.forceOverlayScrollbars = YES;
|
||||
configuration.processPool = [[[WKProcessPool alloc] _initWithConfiguration:processConfiguration] autorelease];
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -93,20 +93,11 @@ Element is considered stable when it has maintained the same bounding box for at
|
|||
|
||||
## Enabled
|
||||
|
||||
Element is considered enabled when it is **not disabled**.
|
||||
|
||||
Element is **disabled** when:
|
||||
- it is a `<button>`, `<select>`, `<input>`, `<textarea>`, `<option>` or `<optgroup>` with a `[disabled]` attribute;
|
||||
- it is a `<button>`, `<select>`, `<input>`, `<textarea>`, `<option>` or `<optgroup>` that is a part of a `<fieldset>` with a `[disabled]` attribute;
|
||||
- it is a descendant of an element with `[aria-disabled=true]` attribute.
|
||||
Element is considered enabled unless it is a `<button>`, `<select>`, `<input>` or `<textarea>` with a `disabled` property.
|
||||
|
||||
## Editable
|
||||
|
||||
Element is considered editable when it is [enabled] and is **not readonly**.
|
||||
|
||||
Element is **readonly** when:
|
||||
- it is a `<select>`, `<input>` or `<textarea>` with a `[readonly]` attribute;
|
||||
- it has an `[aria-readonly=true]` attribute and an aria role that [supports it](https://w3c.github.io/aria/#aria-readonly).
|
||||
Element is considered editable when it is [enabled] and does not have `readonly` property set.
|
||||
|
||||
## Receives Events
|
||||
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ Launches Chrome browser on the device, and returns its persistent context.
|
|||
|
||||
### option: AndroidDevice.launchBrowser.pkg
|
||||
* since: v1.9
|
||||
- `pkg` <[string]>
|
||||
- `command` <[string]>
|
||||
|
||||
Optional package name to launch instead of default Chrome for Android.
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,6 @@ Creates new instances of [APIRequestContext].
|
|||
### option: APIRequest.newContext.extraHTTPHeaders = %%-context-option-extrahttpheaders-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequest.newContext.failOnStatusCode
|
||||
* since: v1.51
|
||||
- `failOnStatusCode` <[boolean]>
|
||||
|
||||
Whether to throw on response codes other than 2xx and 3xx. By default response object is returned
|
||||
for all status codes.
|
||||
|
||||
### option: APIRequest.newContext.httpCredentials = %%-context-option-httpcredentials-%%
|
||||
* since: v1.16
|
||||
|
||||
|
|
@ -71,7 +64,6 @@ Methods like [`method: APIRequestContext.get`] take the base URL into considerat
|
|||
- `localStorage` <[Array]<[Object]>>
|
||||
- `name` <[string]>
|
||||
- `value` <[string]>
|
||||
- `indexedDB` ?<[Array]<[unknown]>> indexedDB to set for context
|
||||
|
||||
Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
obtained via [`method: BrowserContext.storageState`] or [`method: APIRequestContext.storageState`]. Either a path to the
|
||||
|
|
|
|||
|
|
@ -880,7 +880,6 @@ context cookies from the response. The method will automatically follow redirect
|
|||
- `localStorage` <[Array]<[Object]>>
|
||||
- `name` <[string]>
|
||||
- `value` <[string]>
|
||||
- `indexedDB` <[Array]<[unknown]>>
|
||||
|
||||
Returns storage state for this request context, contains current cookies and local storage snapshot if it was passed to the constructor.
|
||||
|
||||
|
|
@ -891,9 +890,3 @@ Returns storage state for this request context, contains current cookies and loc
|
|||
|
||||
### option: APIRequestContext.storageState.path = %%-storagestate-option-path-%%
|
||||
* since: v1.16
|
||||
|
||||
### option: APIRequestContext.storageState.indexedDB
|
||||
* since: v1.51
|
||||
- `indexedDB` ?<boolean>
|
||||
|
||||
Set to `true` to include IndexedDB in the storage state snapshot.
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ In case this browser is connected to, clears all created contexts belonging to t
|
|||
browser server.
|
||||
|
||||
:::note
|
||||
This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close events, call [`method: BrowserContext.close`] on any [BrowserContext] instances you explicitly created earlier using [`method: Browser.newContext`] **before** calling [`method: Browser.close`].
|
||||
This is similar to force quitting the browser. Therefore, you should call [`method: BrowserContext.close`] on any [BrowserContext]'s you explicitly created earlier with [`method: Browser.newContext`] **before** calling [`method: Browser.close`].
|
||||
:::
|
||||
|
||||
The [Browser] object itself is considered to be disposed and cannot be used anymore.
|
||||
|
|
|
|||
|
|
@ -963,14 +963,9 @@ specified.
|
|||
* since: v1.8
|
||||
- `permissions` <[Array]<[string]>>
|
||||
|
||||
A list of permissions to grant.
|
||||
|
||||
:::danger
|
||||
Supported permissions differ between browsers, and even between different versions of the same browser. Any permission may stop working after an update.
|
||||
:::
|
||||
|
||||
Here are some permissions that may be supported by some browsers:
|
||||
A permission or an array of permissions to grant. Permissions can be one of the following values:
|
||||
* `'accelerometer'`
|
||||
* `'accessibility-events'`
|
||||
* `'ambient-light-sensor'`
|
||||
* `'background-sync'`
|
||||
* `'camera'`
|
||||
|
|
@ -1412,7 +1407,7 @@ This setting will change the default maximum time for all the methods accepting
|
|||
* since: v1.8
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds. Pass `0` to disable timeout.
|
||||
Maximum time in milliseconds
|
||||
|
||||
## async method: BrowserContext.setExtraHTTPHeaders
|
||||
* since: v1.8
|
||||
|
|
@ -1511,9 +1506,8 @@ Whether to emulate network being offline for the browser context.
|
|||
- `localStorage` <[Array]<[Object]>>
|
||||
- `name` <[string]>
|
||||
- `value` <[string]>
|
||||
- `indexedDB` <[Array]<[unknown]>>
|
||||
|
||||
Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB snapshot.
|
||||
Returns storage state for this browser context, contains current cookies and local storage snapshot.
|
||||
|
||||
## async method: BrowserContext.storageState
|
||||
* since: v1.8
|
||||
|
|
@ -1523,17 +1517,6 @@ Returns storage state for this browser context, contains current cookies, local
|
|||
### option: BrowserContext.storageState.path = %%-storagestate-option-path-%%
|
||||
* since: v1.8
|
||||
|
||||
### option: BrowserContext.storageState.indexedDB
|
||||
* since: v1.51
|
||||
- `indexedDB` ?<boolean>
|
||||
|
||||
Set to `true` to include IndexedDB in the storage state snapshot.
|
||||
If your application uses IndexedDB to store authentication tokens, like Firebase Authentication, enable this.
|
||||
|
||||
:::note
|
||||
IndexedDBs with typed arrays are currently not supported.
|
||||
:::
|
||||
|
||||
## property: BrowserContext.tracing
|
||||
* since: v1.12
|
||||
- type: <[Tracing]>
|
||||
|
|
|
|||
|
|
@ -89,17 +89,13 @@ class BrowserTypeExamples
|
|||
* since: v1.8
|
||||
- returns: <[Browser]>
|
||||
|
||||
This method attaches Playwright to an existing browser instance created via `BrowserType.launchServer` in Node.js.
|
||||
|
||||
:::note
|
||||
The major and minor version of the Playwright instance that connects needs to match the version of Playwright that launches the browser (1.2.3 → is compatible with 1.2.x).
|
||||
:::
|
||||
This method attaches Playwright to an existing browser instance. When connecting to another browser launched via `BrowserType.launchServer` in Node.js, the major and minor version needs to match the client version (1.2.3 → is compatible with 1.2.x).
|
||||
|
||||
### param: BrowserType.connect.wsEndpoint
|
||||
* since: v1.10
|
||||
- `wsEndpoint` <[string]>
|
||||
|
||||
A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`.
|
||||
A browser websocket endpoint to connect to.
|
||||
|
||||
### option: BrowserType.connect.headers
|
||||
* since: v1.11
|
||||
|
|
@ -156,10 +152,6 @@ The default browser context is accessible via [`method: Browser.contexts`].
|
|||
Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers.
|
||||
:::
|
||||
|
||||
:::note
|
||||
This connection is significantly lower fidelity than the Playwright protocol connection via [`method: BrowserType.connect`]. If you are experiencing issues or attempting to use advanced functionality, you probably want to use [`method: BrowserType.connect`].
|
||||
:::
|
||||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
|
|
|
|||
|
|
@ -161,41 +161,6 @@ await page.Clock.PauseAtAsync(DateTime.Parse("2020-02-02"));
|
|||
await page.Clock.PauseAtAsync("2020-02-02");
|
||||
```
|
||||
|
||||
For best results, install the clock before navigating the page and set it to a time slightly before the intended test time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the page has fully loaded, you can safely use [`method: Clock.pauseAt`] to pause the clock.
|
||||
|
||||
```js
|
||||
// Initialize clock with some time before the test time and let the page load
|
||||
// naturally. `Date.now` will progress as the timers fire.
|
||||
await page.clock.install({ time: new Date('2024-12-10T08:00:00') });
|
||||
await page.goto('http://localhost:3333');
|
||||
await page.clock.pauseAt(new Date('2024-12-10T10:00:00'));
|
||||
```
|
||||
|
||||
```python async
|
||||
# Initialize clock with some time before the test time and let the page load
|
||||
# naturally. `Date.now` will progress as the timers fire.
|
||||
await page.clock.install(time=datetime.datetime(2024, 12, 10, 8, 0, 0))
|
||||
await page.goto("http://localhost:3333")
|
||||
await page.clock.pause_at(datetime.datetime(2024, 12, 10, 10, 0, 0))
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Initialize clock with some time before the test time and let the page load
|
||||
# naturally. `Date.now` will progress as the timers fire.
|
||||
page.clock.install(time=datetime.datetime(2024, 12, 10, 8, 0, 0))
|
||||
page.goto("http://localhost:3333")
|
||||
page.clock.pause_at(datetime.datetime(2024, 12, 10, 10, 0, 0))
|
||||
```
|
||||
|
||||
```java
|
||||
// Initialize clock with some time before the test time and let the page load
|
||||
// naturally. `Date.now` will progress as the timers fire.
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
|
||||
page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
|
||||
page.navigate("http://localhost:3333");
|
||||
page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
|
||||
```
|
||||
|
||||
### param: Clock.pauseAt.time
|
||||
* langs: js, java
|
||||
* since: v1.45
|
||||
|
|
|
|||
|
|
@ -633,11 +633,13 @@ properties:
|
|||
You can also specify [JSHandle] as the property value if you want live objects to be passed into the event:
|
||||
|
||||
```js
|
||||
// Note you can only create DataTransfer in Chromium and Firefox
|
||||
const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
|
||||
await locator.dispatchEvent('dragstart', { dataTransfer });
|
||||
```
|
||||
|
||||
```java
|
||||
// Note you can only create DataTransfer in Chromium and Firefox
|
||||
JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
|
||||
Map<String, Object> arg = new HashMap<>();
|
||||
arg.put("dataTransfer", dataTransfer);
|
||||
|
|
@ -645,11 +647,13 @@ locator.dispatchEvent("dragstart", arg);
|
|||
```
|
||||
|
||||
```python async
|
||||
# note you can only create data_transfer in chromium and firefox
|
||||
data_transfer = await page.evaluate_handle("new DataTransfer()")
|
||||
await locator.dispatch_event("#source", "dragstart", {"dataTransfer": data_transfer})
|
||||
```
|
||||
|
||||
```python sync
|
||||
# note you can only create data_transfer in chromium and firefox
|
||||
data_transfer = page.evaluate_handle("new DataTransfer()")
|
||||
locator.dispatch_event("#source", "dragstart", {"dataTransfer": data_transfer})
|
||||
```
|
||||
|
|
@ -864,6 +868,31 @@ If [`param: expression`] throws or rejects, this method throws.
|
|||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
const tweets = page.locator('.tweet .retweets');
|
||||
expect(await tweets.evaluate(node => node.innerText)).toBe('10 retweets');
|
||||
```
|
||||
|
||||
```java
|
||||
Locator tweets = page.locator(".tweet .retweets");
|
||||
assertEquals("10 retweets", tweets.evaluate("node => node.innerText"));
|
||||
```
|
||||
|
||||
```python async
|
||||
tweets = page.locator(".tweet .retweets")
|
||||
assert await tweets.evaluate("node => node.innerText") == "10 retweets"
|
||||
```
|
||||
|
||||
```python sync
|
||||
tweets = page.locator(".tweet .retweets")
|
||||
assert tweets.evaluate("node => node.innerText") == "10 retweets"
|
||||
```
|
||||
|
||||
```csharp
|
||||
var tweets = page.Locator(".tweet .retweets");
|
||||
Assert.AreEqual("10 retweets", await tweets.EvaluateAsync("node => node.innerText"));
|
||||
```
|
||||
|
||||
### param: Locator.evaluate.expression = %%-evaluate-expression-%%
|
||||
* since: v1.14
|
||||
|
||||
|
|
@ -1090,9 +1119,6 @@ await rowLocator
|
|||
### option: Locator.filter.hasNotText = %%-locator-option-has-not-text-%%
|
||||
* since: v1.33
|
||||
|
||||
### option: Locator.filter.visible = %%-locator-option-visible-%%
|
||||
* since: v1.51
|
||||
|
||||
## method: Locator.first
|
||||
* since: v1.14
|
||||
- returns: <[Locator]>
|
||||
|
|
@ -1457,7 +1483,7 @@ Boolean disabled = await page.GetByRole(AriaRole.Button).IsDisabledAsync();
|
|||
* since: v1.14
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is [editable](../actionability.md#editable). If the target element is not an `<input>`, `<textarea>`, `<select>`, `[contenteditable]` and does not have a role allowing `[aria-readonly]`, this method throws an error.
|
||||
Returns whether the element is [editable](../actionability.md#editable).
|
||||
|
||||
:::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.
|
||||
|
|
@ -1691,21 +1717,16 @@ var banana = await page.GetByRole(AriaRole.Listitem).Nth(2);
|
|||
|
||||
Creates a locator matching all elements that match one or both of the two locators.
|
||||
|
||||
Note that when both locators match something, the resulting locator will have multiple matches, potentially causing a [locator strictness](../locators.md#strictness) violation.
|
||||
Note that when both locators match something, the resulting locator will have multiple matches and violate [locator strictness](../locators.md#strictness) guidelines.
|
||||
|
||||
**Usage**
|
||||
|
||||
Consider a scenario where you'd like to click on a "New email" button, but sometimes a security settings dialog shows up instead. In this case, you can wait for either a "New email" button, or a dialog and act accordingly.
|
||||
|
||||
:::note
|
||||
If both "New email" button and security dialog appear on screen, the "or" locator will match both of them,
|
||||
possibly throwing the ["strict mode violation" error](../locators.md#strictness). In this case, you can use [`method: Locator.first`] to only match one of them.
|
||||
:::
|
||||
|
||||
```js
|
||||
const newEmail = page.getByRole('button', { name: 'New' });
|
||||
const dialog = page.getByText('Confirm security settings');
|
||||
await expect(newEmail.or(dialog).first()).toBeVisible();
|
||||
await expect(newEmail.or(dialog)).toBeVisible();
|
||||
if (await dialog.isVisible())
|
||||
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||
await newEmail.click();
|
||||
|
|
@ -1714,7 +1735,7 @@ await newEmail.click();
|
|||
```java
|
||||
Locator newEmail = page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("New"));
|
||||
Locator dialog = page.getByText("Confirm security settings");
|
||||
assertThat(newEmail.or(dialog).first()).isVisible();
|
||||
assertThat(newEmail.or(dialog)).isVisible();
|
||||
if (dialog.isVisible())
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Dismiss")).click();
|
||||
newEmail.click();
|
||||
|
|
@ -1723,7 +1744,7 @@ newEmail.click();
|
|||
```python async
|
||||
new_email = page.get_by_role("button", name="New")
|
||||
dialog = page.get_by_text("Confirm security settings")
|
||||
await expect(new_email.or_(dialog).first).to_be_visible()
|
||||
await expect(new_email.or_(dialog)).to_be_visible()
|
||||
if (await dialog.is_visible()):
|
||||
await page.get_by_role("button", name="Dismiss").click()
|
||||
await new_email.click()
|
||||
|
|
@ -1732,7 +1753,7 @@ await new_email.click()
|
|||
```python sync
|
||||
new_email = page.get_by_role("button", name="New")
|
||||
dialog = page.get_by_text("Confirm security settings")
|
||||
expect(new_email.or_(dialog).first).to_be_visible()
|
||||
expect(new_email.or_(dialog)).to_be_visible()
|
||||
if (dialog.is_visible()):
|
||||
page.get_by_role("button", name="Dismiss").click()
|
||||
new_email.click()
|
||||
|
|
@ -1741,7 +1762,7 @@ new_email.click()
|
|||
```csharp
|
||||
var newEmail = page.GetByRole(AriaRole.Button, new() { Name = "New" });
|
||||
var dialog = page.GetByText("Confirm security settings");
|
||||
await Expect(newEmail.Or(dialog).First).ToBeVisibleAsync();
|
||||
await Expect(newEmail.Or(dialog)).ToBeVisibleAsync();
|
||||
if (await dialog.IsVisibleAsync())
|
||||
await page.GetByRole(AriaRole.Button, new() { Name = "Dismiss" }).ClickAsync();
|
||||
await newEmail.ClickAsync();
|
||||
|
|
@ -2038,9 +2059,9 @@ Triggers a `change` and `input` event once all the provided options have been se
|
|||
|
||||
```html
|
||||
<select multiple>
|
||||
<option value="red">Red</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="red">Red</div>
|
||||
<option value="green">Green</div>
|
||||
<option value="blue">Blue</div>
|
||||
</select>
|
||||
```
|
||||
|
||||
|
|
@ -2335,7 +2356,7 @@ This method expects [Locator] to point to an
|
|||
## async method: Locator.tap
|
||||
* since: v1.14
|
||||
|
||||
Perform a tap gesture on the element matching the locator. For examples of emulating other gestures by manually dispatching touch events, see the [emulating legacy touch events](../touch-events.md) page.
|
||||
Perform a tap gesture on the element matching the locator.
|
||||
|
||||
**Details**
|
||||
|
||||
|
|
|
|||
|
|
@ -240,24 +240,6 @@ Expected accessible description.
|
|||
### option: LocatorAssertions.NotToHaveAccessibleDescription.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.44
|
||||
|
||||
## async method: LocatorAssertions.NotToHaveAccessibleErrorMessage
|
||||
* since: v1.50
|
||||
* langs: python
|
||||
|
||||
The opposite of [`method: LocatorAssertions.toHaveAccessibleErrorMessage`].
|
||||
|
||||
### param: LocatorAssertions.NotToHaveAccessibleErrorMessage.errorMessage
|
||||
* since: v1.50
|
||||
- `errorMessage` <[string]|[RegExp]>
|
||||
|
||||
Expected accessible error message.
|
||||
|
||||
### option: LocatorAssertions.NotToHaveAccessibleErrorMessage.ignoreCase = %%-assertions-ignore-case-%%
|
||||
* since: v1.50
|
||||
|
||||
### option: LocatorAssertions.NotToHaveAccessibleErrorMessage.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.50
|
||||
|
||||
|
||||
## async method: LocatorAssertions.NotToHaveAccessibleName
|
||||
* since: v1.44
|
||||
|
|
@ -559,16 +541,6 @@ await Expect(locator).ToBeCheckedAsync();
|
|||
* since: v1.18
|
||||
- `checked` <[boolean]>
|
||||
|
||||
Provides state to assert for. Asserts for input to be checked by default.
|
||||
This option can't be used when [`option: LocatorAssertions.toBeChecked.indeterminate`] is set to true.
|
||||
|
||||
### option: LocatorAssertions.toBeChecked.indeterminate
|
||||
* since: v1.50
|
||||
- `indeterminate` <[boolean]>
|
||||
|
||||
Asserts that the element is in the indeterminate (mixed) state. Only supported for checkboxes and radio buttons.
|
||||
This option can't be true when [`option: LocatorAssertions.toBeChecked.checked`] is provided.
|
||||
|
||||
### option: LocatorAssertions.toBeChecked.timeout = %%-js-assertions-timeout-%%
|
||||
* since: v1.18
|
||||
|
||||
|
|
@ -746,7 +718,7 @@ expect(locator).to_be_enabled()
|
|||
|
||||
```csharp
|
||||
var locator = Page.Locator("button.submit");
|
||||
await Expect(locator).ToBeEnabledAsync();
|
||||
await Expect(locator).toBeEnabledAsync();
|
||||
```
|
||||
|
||||
### option: LocatorAssertions.toBeEnabled.enabled
|
||||
|
|
@ -1226,7 +1198,7 @@ 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");
|
||||
await Expect(locator).toHaveAccessibleDescriptionAsync("Save results to disk");
|
||||
```
|
||||
|
||||
### param: LocatorAssertions.toHaveAccessibleDescription.description
|
||||
|
|
@ -1245,56 +1217,6 @@ Expected accessible description.
|
|||
* since: v1.44
|
||||
|
||||
|
||||
## async method: LocatorAssertions.toHaveAccessibleErrorMessage
|
||||
* since: v1.50
|
||||
* langs:
|
||||
- alias-java: hasAccessibleErrorMessage
|
||||
|
||||
Ensures the [Locator] points to an element with a given [aria errormessage](https://w3c.github.io/aria/#aria-errormessage).
|
||||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
const locator = page.getByTestId('username-input');
|
||||
await expect(locator).toHaveAccessibleErrorMessage('Username is required.');
|
||||
```
|
||||
|
||||
```java
|
||||
Locator locator = page.getByTestId("username-input");
|
||||
assertThat(locator).hasAccessibleErrorMessage("Username is required.");
|
||||
```
|
||||
|
||||
```python async
|
||||
locator = page.get_by_test_id("username-input")
|
||||
await expect(locator).to_have_accessible_error_message("Username is required.")
|
||||
```
|
||||
|
||||
```python sync
|
||||
locator = page.get_by_test_id("username-input")
|
||||
expect(locator).to_have_accessible_error_message("Username is required.")
|
||||
```
|
||||
|
||||
```csharp
|
||||
var locator = Page.GetByTestId("username-input");
|
||||
await Expect(locator).ToHaveAccessibleErrorMessageAsync("Username is required.");
|
||||
```
|
||||
|
||||
### param: LocatorAssertions.toHaveAccessibleErrorMessage.errorMessage
|
||||
* since: v1.50
|
||||
- `errorMessage` <[string]|[RegExp]>
|
||||
|
||||
Expected accessible error message.
|
||||
|
||||
### option: LocatorAssertions.toHaveAccessibleErrorMessage.timeout = %%-js-assertions-timeout-%%
|
||||
* since: v1.50
|
||||
|
||||
### option: LocatorAssertions.toHaveAccessibleErrorMessage.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.50
|
||||
|
||||
### option: LocatorAssertions.toHaveAccessibleErrorMessage.ignoreCase = %%-assertions-ignore-case-%%
|
||||
* since: v1.50
|
||||
|
||||
|
||||
## async method: LocatorAssertions.toHaveAccessibleName
|
||||
* since: v1.44
|
||||
* langs:
|
||||
|
|
@ -1326,7 +1248,7 @@ expect(locator).to_have_accessible_name("Save to disk")
|
|||
|
||||
```csharp
|
||||
var locator = Page.GetByTestId("save-button");
|
||||
await Expect(locator).ToHaveAccessibleNameAsync("Save to disk");
|
||||
await Expect(locator).toHaveAccessibleNameAsync("Save to disk");
|
||||
```
|
||||
|
||||
### param: LocatorAssertions.toHaveAccessibleName.name
|
||||
|
|
@ -1431,48 +1353,49 @@ Attribute name.
|
|||
* langs:
|
||||
- alias-java: hasClass
|
||||
|
||||
Ensures the [Locator] points to an element with given CSS classes. When a string is provided, it must fully match the element's `class` attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
Ensures the [Locator] points to an element with given CSS classes. This needs to be a full match
|
||||
or using a relaxed regular expression.
|
||||
|
||||
**Usage**
|
||||
|
||||
```html
|
||||
<div class='middle selected row' id='component'></div>
|
||||
<div class='selected row' id='component'></div>
|
||||
```
|
||||
|
||||
```js
|
||||
const locator = page.locator('#component');
|
||||
await expect(locator).toHaveClass('middle selected row');
|
||||
await expect(locator).toHaveClass(/(^|\s)selected(\s|$)/);
|
||||
await expect(locator).toHaveClass(/selected/);
|
||||
await expect(locator).toHaveClass('selected row');
|
||||
```
|
||||
|
||||
```java
|
||||
assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
assertThat(page.locator("#component")).hasClass("selected row");
|
||||
```
|
||||
|
||||
```python async
|
||||
from playwright.async_api import expect
|
||||
|
||||
locator = page.locator("#component")
|
||||
await expect(locator).to_have_class(re.compile(r"(^|\\s)selected(\\s|$)"))
|
||||
await expect(locator).to_have_class("middle selected row")
|
||||
await expect(locator).to_have_class(re.compile(r"selected"))
|
||||
await expect(locator).to_have_class("selected row")
|
||||
```
|
||||
|
||||
```python sync
|
||||
from playwright.sync_api import expect
|
||||
|
||||
locator = page.locator("#component")
|
||||
expect(locator).to_have_class(re.compile(r"(^|\\s)selected(\\s|$)"))
|
||||
expect(locator).to_have_class("middle selected row")
|
||||
expect(locator).to_have_class(re.compile(r"selected"))
|
||||
expect(locator).to_have_class("selected row")
|
||||
```
|
||||
|
||||
```csharp
|
||||
var locator = Page.Locator("#component");
|
||||
await Expect(locator).ToHaveClassAsync(new Regex("(^|\\s)selected(\\s|$)"));
|
||||
await Expect(locator).ToHaveClassAsync("middle selected row");
|
||||
await Expect(locator).ToHaveClassAsync(new Regex("selected"));
|
||||
await Expect(locator).ToHaveClassAsync("selected row");
|
||||
```
|
||||
|
||||
When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected class values. Each element's class attribute is matched against the corresponding string or regular expression in the array:
|
||||
Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
|
||||
```js
|
||||
const locator = page.locator('list > .component');
|
||||
|
|
@ -2256,32 +2179,3 @@ assertThat(page.locator("body")).matchesAriaSnapshot("""
|
|||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.49
|
||||
|
||||
## async method: LocatorAssertions.toMatchAriaSnapshot#2
|
||||
* since: v1.50
|
||||
* langs: js
|
||||
|
||||
Asserts that the target element matches the given [accessibility snapshot](../aria-snapshots.md).
|
||||
|
||||
Snapshot is stored in a separate `.snapshot.yml` file in a location configured by `expect.toMatchAriaSnapshot.pathTemplate` and/or `snapshotPathTemplate` properties in the configuration file.
|
||||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot();
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'body.snapshot.yml' });
|
||||
```
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#2.name
|
||||
* since: v1.50
|
||||
* langs: js
|
||||
- `name` <[string]>
|
||||
|
||||
Name of the snapshot to store in the snapshot folder corresponding to this test.
|
||||
Generates sequential names if not specified.
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#2.timeout = %%-js-assertions-timeout-%%
|
||||
* since: v1.50
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#2.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.50
|
||||
|
|
|
|||
|
|
@ -1309,18 +1309,6 @@ Emulates `'forced-colors'` media feature, supported values are `'active'` and `'
|
|||
* langs: csharp, python
|
||||
- `forcedColors` <[ForcedColors]<"active"|"none"|"null">>
|
||||
|
||||
### option: Page.emulateMedia.contrast
|
||||
* since: v1.51
|
||||
* langs: js, java
|
||||
- `contrast` <null|[Contrast]<"no-preference"|"more">>
|
||||
|
||||
Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. Passing `null` disables contrast emulation.
|
||||
|
||||
### option: Page.emulateMedia.contrast
|
||||
* since: v1.51
|
||||
* langs: csharp, python
|
||||
- `contrast` <[Contrast]<"no-preference"|"more"|"null">>
|
||||
|
||||
## async method: Page.evalOnSelector
|
||||
* since: v1.9
|
||||
* discouraged: This method does not wait for the element to pass actionability
|
||||
|
|
@ -2774,6 +2762,10 @@ This method requires Playwright to be started in a headed mode, with a falsy [`o
|
|||
|
||||
Returns the PDF buffer.
|
||||
|
||||
:::note
|
||||
Generating a pdf is currently only supported in Chromium headless.
|
||||
:::
|
||||
|
||||
`page.pdf()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call
|
||||
[`method: Page.emulateMedia`] before calling `page.pdf()`:
|
||||
|
||||
|
|
@ -3982,7 +3974,7 @@ This setting will change the default maximum time for all the methods accepting
|
|||
* since: v1.8
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds. Pass `0` to disable timeout.
|
||||
Maximum time in milliseconds
|
||||
|
||||
## async method: Page.setExtraHTTPHeaders
|
||||
* since: v1.8
|
||||
|
|
|
|||
|
|
@ -296,18 +296,7 @@ Ensures the page is navigated to the given URL.
|
|||
**Usage**
|
||||
|
||||
```js
|
||||
// Check for the page URL to be 'https://playwright.dev/docs/intro' (including query string)
|
||||
await expect(page).toHaveURL('https://playwright.dev/docs/intro');
|
||||
|
||||
// Check for the page URL to contain 'doc', followed by an optional 's', followed by '/'
|
||||
await expect(page).toHaveURL(/docs?\//);
|
||||
|
||||
// Check for the predicate to be satisfied
|
||||
// For example: verify query strings
|
||||
await expect(page).toHaveURL(url => {
|
||||
const params = url.searchParams;
|
||||
return params.has('search') && params.has('options') && params.get('id') === '5';
|
||||
});
|
||||
await expect(page).toHaveURL(/.*checkout/);
|
||||
```
|
||||
|
||||
```java
|
||||
|
|
@ -334,18 +323,17 @@ expect(page).to_have_url(re.compile(".*checkout"))
|
|||
await Expect(Page).ToHaveURLAsync(new Regex(".*checkout"));
|
||||
```
|
||||
|
||||
### param: PageAssertions.toHaveURL.url
|
||||
### param: PageAssertions.toHaveURL.urlOrRegExp
|
||||
* since: v1.18
|
||||
- `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]>
|
||||
- `urlOrRegExp` <[string]|[RegExp]>
|
||||
|
||||
Expected URL string, RegExp, or predicate receiving [URL] to match.
|
||||
When [`option: Browser.newContext.baseURL`] is provided via the context options and the `url` argument is a string, the two values are merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor and used for the comparison against the current browser URL.
|
||||
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 parameter if specified. A provided predicate ignores this flag.
|
||||
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
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@
|
|||
The Touchscreen class operates in main-frame CSS pixels relative to the top-left corner of the viewport. Methods on the
|
||||
touchscreen can only be used in browser contexts that have been initialized with `hasTouch` set to true.
|
||||
|
||||
This class is limited to emulating tap gestures. For examples of other gestures simulated by manually dispatching touch events, see the [emulating legacy touch events](../touch-events.md) page.
|
||||
|
||||
## async method: Touchscreen.tap
|
||||
* since: v1.8
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
# class: WebSocket
|
||||
* since: v1.8
|
||||
|
||||
The [WebSocket] class represents WebSocket connections within a page. It provides the ability to inspect and manipulate the data being transmitted and received.
|
||||
|
||||
If you want to intercept or modify WebSocket frames, consider using [WebSocketRoute].
|
||||
The [WebSocket] class represents websocket connections in the page.
|
||||
|
||||
## event: WebSocket.close
|
||||
* since: v1.8
|
||||
|
|
|
|||
|
|
@ -259,12 +259,11 @@ Specify environment variables that will be visible to the browser. Defaults to `
|
|||
- `httpOnly` <[boolean]>
|
||||
- `secure` <[boolean]>
|
||||
- `sameSite` <[SameSiteAttribute]<"Strict"|"Lax"|"None">> sameSite flag
|
||||
- `origins` <[Array]<[Object]>>
|
||||
- `origins` <[Array]<[Object]>> localStorage to set for context
|
||||
- `origin` <[string]>
|
||||
- `localStorage` <[Array]<[Object]>> localStorage to set for context
|
||||
- `localStorage` <[Array]<[Object]>>
|
||||
- `name` <[string]>
|
||||
- `value` <[string]>
|
||||
- `indexedDB` ?<[Array]<[unknown]>> indexedDB to set for context
|
||||
|
||||
Learn more about [storage state and auth](../auth.md).
|
||||
|
||||
|
|
@ -674,18 +673,6 @@ Emulates `'forced-colors'` media feature, supported values are `'active'`, `'non
|
|||
|
||||
Emulates `'forced-colors'` media feature, supported values are `'active'`, `'none'`. See [`method: Page.emulateMedia`] for more details. Passing `'null'` resets emulation to system defaults. Defaults to `'none'`.
|
||||
|
||||
## context-option-contrast
|
||||
* langs: js, java
|
||||
- `contrast` <null|[ForcedColors]<"no-preference"|"more">>
|
||||
|
||||
Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. See [`method: Page.emulateMedia`] for more details. Passing `null` resets emulation to system defaults. Defaults to `'no-preference'`.
|
||||
|
||||
## context-option-contrast-csharp-python
|
||||
* langs: csharp, python
|
||||
- `contrast` <[ForcedColors]<"no-preference"|"more"|"null">>
|
||||
|
||||
Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. See [`method: Page.emulateMedia`] for more details. Passing `'null'` resets emulation to system defaults. Defaults to `'no-preference'`.
|
||||
|
||||
## context-option-logger
|
||||
* langs: js
|
||||
- `logger` <[Logger]>
|
||||
|
|
@ -986,8 +973,6 @@ between the same pixel in compared images, between zero (strict) and one (lax),
|
|||
- %%-context-option-reducedMotion-csharp-python-%%
|
||||
- %%-context-option-forcedColors-%%
|
||||
- %%-context-option-forcedColors-csharp-python-%%
|
||||
- %%-context-option-contrast-%%
|
||||
- %%-context-option-contrast-csharp-python-%%
|
||||
- %%-context-option-logger-%%
|
||||
- %%-context-option-videospath-%%
|
||||
- %%-context-option-videosize-%%
|
||||
|
|
@ -1018,7 +1003,7 @@ Additional arguments to pass to the browser instance. The list of Chromium flags
|
|||
|
||||
Browser distribution channel.
|
||||
|
||||
Use "chromium" to [opt in to new headless mode](../browsers.md#chromium-new-headless-mode).
|
||||
Use "chromium" to [opt in to new headless mode](../browsers.md#opt-in-to-new-headless-mode).
|
||||
|
||||
Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to use branded [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).
|
||||
|
||||
|
|
@ -1155,11 +1140,6 @@ Note that outer and inner locators must belong to the same frame. Inner locator
|
|||
|
||||
Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When passed a [string], matching is case-insensitive and searches for a substring.
|
||||
|
||||
## locator-option-visible
|
||||
- `visible` <[boolean]>
|
||||
|
||||
Only matches visible or invisible elements.
|
||||
|
||||
## locator-options-list-v1.14
|
||||
- %%-locator-option-has-text-%%
|
||||
- %%-locator-option-has-%%
|
||||
|
|
@ -1210,7 +1190,6 @@ Specify screenshot type, defaults to `png`.
|
|||
|
||||
Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with
|
||||
a pink box `#FF00FF` (customized by [`option: maskColor`]) that completely covers its bounding box.
|
||||
The mask is also applied to invisible elements, see [Matching only visible elements](../locators.md#matching-only-visible-elements) to disable that.
|
||||
|
||||
## screenshot-option-mask-color
|
||||
* since: v1.35
|
||||
|
|
@ -1779,9 +1758,7 @@ await Expect(Page.GetByTitle("Issues count")).toHaveText("25 issues");
|
|||
- `type` ?<[string]>
|
||||
* langs: js
|
||||
|
||||
This option configures a template controlling location of snapshots generated by [`method: PageAssertions.toHaveScreenshot#1`], [`method: LocatorAssertions.toMatchAriaSnapshot#2`] and [`method: SnapshotAssertions.toMatchSnapshot#1`].
|
||||
|
||||
You can configure templates for each assertion separately in [`property: TestConfig.expect`].
|
||||
This option configures a template controlling location of snapshots generated by [`method: PageAssertions.toHaveScreenshot#1`] and [`method: SnapshotAssertions.toMatchSnapshot#1`].
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
@ -1790,19 +1767,7 @@ import { defineConfig } from '@playwright/test';
|
|||
|
||||
export default defineConfig({
|
||||
testDir: './tests',
|
||||
|
||||
// Single template for all assertions
|
||||
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
||||
|
||||
// Assertion-specific templates
|
||||
expect: {
|
||||
toHaveScreenshot: {
|
||||
pathTemplate: '{testDir}/__screenshots__{/projectName}/{testFilePath}/{arg}{ext}',
|
||||
},
|
||||
toMatchAriaSnapshot: {
|
||||
pathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}{ext}',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -1833,22 +1798,22 @@ test.describe('suite', () => {
|
|||
|
||||
The list of supported tokens:
|
||||
|
||||
* `{arg}` - Relative snapshot path **without extension**. This comes from the arguments passed to `toHaveScreenshot()`, `toMatchAriaSnapshot()` or `toMatchSnapshot()`; if called without arguments, this will be an auto-generated snapshot name.
|
||||
* `{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 the leading dot).
|
||||
* `{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: TestProject.snapshotDir`].
|
||||
* `{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: TestProject.testDir`].
|
||||
* Value: `/home/playwright/tests` (absolute path since `testDir` is resolved relative to directory with config)
|
||||
* `{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.
|
||||
* Value: `page-click.spec.ts`
|
||||
* `{testFilePath}` - Relative path from `testDir` to **test file**.
|
||||
* `{testFilePath}` - Relative path from `testDir` to **test file**
|
||||
* Value: `page/page-click.spec.ts`
|
||||
* `{testName}` - File-system-sanitized test title, including parent describes but excluding file name.
|
||||
* Value: `suite-test-should-work`
|
||||
|
|
|
|||
|
|
@ -1,142 +1,36 @@
|
|||
---
|
||||
id: aria-snapshots
|
||||
title: "Snapshot testing"
|
||||
title: "Aria snapshots"
|
||||
---
|
||||
import LiteYouTube from '@site/src/components/LiteYouTube';
|
||||
|
||||
## Overview
|
||||
|
||||
With Playwright's Snapshot testing you can assert the accessibility tree of a page against a predefined snapshot template.
|
||||
|
||||
```js
|
||||
await page.goto('https://playwright.dev/');
|
||||
await expect(page.getByRole('banner')).toMatchAriaSnapshot(`
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
`);
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.goto('https://playwright.dev/')
|
||||
expect(page.query_selector('banner')).to_match_aria_snapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""")
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.goto('https://playwright.dev/')
|
||||
await expect(page.query_selector('banner')).to_match_aria_snapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""")
|
||||
```
|
||||
|
||||
```java
|
||||
page.navigate("https://playwright.dev/");
|
||||
assertThat(page.locator("banner")).matchesAriaSnapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""");
|
||||
```
|
||||
|
||||
```csharp
|
||||
await page.GotoAsync("https://playwright.dev/");
|
||||
await Expect(page.Locator("banner")).ToMatchAriaSnapshotAsync(@"
|
||||
- banner:
|
||||
- heading ""Playwright enables reliable end-to-end testing for modern web apps."" [level=1]
|
||||
- link ""Get started""
|
||||
- link ""Star microsoft/playwright on GitHub""
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
");
|
||||
```
|
||||
In Playwright, aria snapshots provide a YAML representation of the accessibility tree of a page.
|
||||
These snapshots can be stored and compared later to verify if the page structure remains consistent or meets defined
|
||||
expectations.
|
||||
|
||||
<LiteYouTube
|
||||
id="P4R6hnsE0UY"
|
||||
title="Getting started with ARIA Snapshots"
|
||||
/>
|
||||
|
||||
## Assertion testing vs Snapshot testing
|
||||
|
||||
Snapshot testing and assertion testing serve different purposes in test automation:
|
||||
|
||||
### Assertion testing
|
||||
Assertion testing is a targeted approach where you assert specific values or conditions about elements or components. For instance, with Playwright, [`method: LocatorAssertions.toHaveText`]
|
||||
verifies that an element contains the expected text, and [`method: LocatorAssertions.toHaveValue`]
|
||||
confirms that an input field has the expected value.
|
||||
Assertion tests are specific and generally check the current state of an element or property
|
||||
against an expected, predefined state.
|
||||
They work well for predictable, single-value checks but are limited in scope when testing the
|
||||
broader structure or variations.
|
||||
|
||||
**Advantages**
|
||||
- **Clarity**: The intent of the test is explicit and easy to understand.
|
||||
- **Specificity**: Tests focus on particular aspects of functionality, making them more robust
|
||||
against unrelated changes.
|
||||
- **Debugging**: Failures provide targeted feedback, pointing directly to the problematic aspect.
|
||||
|
||||
**Disadvantages**
|
||||
- **Verbose for complex outputs**: Writing assertions for complex data structures or large outputs
|
||||
can be cumbersome and error-prone.
|
||||
- **Maintenance overhead**: As code evolves, manually updating assertions can be time-consuming.
|
||||
|
||||
### Snapshot testing
|
||||
Snapshot testing captures a “snapshot” or representation of the entire
|
||||
state of an element, component, or data at a given moment, which is then saved for future
|
||||
comparisons. When re-running tests, the current state is compared to the snapshot, and if there
|
||||
are differences, the test fails. This approach is especially useful for complex or dynamic
|
||||
structures, where manually asserting each detail would be too time-consuming. Snapshot testing
|
||||
is broader and more holistic than assertion testing, allowing you to track more complex changes over time.
|
||||
|
||||
**Advantages**
|
||||
- **Simplifies complex outputs**: For example, testing a UI component's rendered output can be tedious with traditional assertions. Snapshots capture the entire output for easy comparison.
|
||||
- **Quick Feedback loop**: Developers can easily spot unintended changes in the output.
|
||||
- **Encourages consistency**: Helps maintain consistent output as code evolves.
|
||||
|
||||
**Disadvantages**
|
||||
- **Over-Reliance**: It can be tempting to accept changes to snapshots without fully understanding
|
||||
them, potentially hiding bugs.
|
||||
- **Granularity**: Large snapshots may be hard to interpret when differences arise, especially
|
||||
if minor changes affect large portions of the output.
|
||||
- **Suitability**: Not ideal for highly dynamic content where outputs change frequently or
|
||||
unpredictably.
|
||||
|
||||
### When to use
|
||||
|
||||
- **Snapshot testing** is ideal for:
|
||||
- UI testing of whole pages and components.
|
||||
- Broad structural checks for complex UI components.
|
||||
- Regression testing for outputs that rarely change structure.
|
||||
|
||||
- **Assertion testing** is ideal for:
|
||||
- Core logic validation.
|
||||
- Computed value testing.
|
||||
- Fine-grained tests requiring precise conditions.
|
||||
|
||||
By combining snapshot testing for broad, structural checks and assertion testing for specific functionality, you can achieve a well-rounded testing strategy.
|
||||
|
||||
## Aria snapshots
|
||||
|
||||
In Playwright, aria snapshots provide a YAML representation of the accessibility tree of a page.
|
||||
These snapshots can be stored and compared later to verify if the page structure remains consistent or meets defined
|
||||
expectations.
|
||||
|
||||
The YAML format describes the hierarchical structure of accessible elements on the page, detailing **roles**, **attributes**, **values**, and **text content**.
|
||||
The structure follows a tree-like syntax, where each node represents an accessible element, and indentation indicates
|
||||
nested elements.
|
||||
|
||||
Following is a simple example of an aria snapshot for the playwright.dev homepage:
|
||||
|
||||
```yaml
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- main:
|
||||
- img "Browsers (Chromium, Firefox, WebKit)"
|
||||
- heading "Any browser • Any platform • One API"
|
||||
```
|
||||
|
||||
Each accessible element in the tree is represented as a YAML node:
|
||||
|
||||
```yaml
|
||||
|
|
@ -222,7 +116,7 @@ attributes.
|
|||
```
|
||||
|
||||
In this example, the button role is matched, but the accessible name ("Submit") is not specified, allowing the test to
|
||||
pass regardless of the button's label.
|
||||
pass regardless of the button’s label.
|
||||
|
||||
<hr/>
|
||||
|
||||
|
|
@ -280,12 +174,12 @@ support regex patterns.
|
|||
|
||||
## Generating snapshots
|
||||
|
||||
Creating aria snapshots in Playwright helps ensure and maintain your application's structure.
|
||||
Creating aria snapshots in Playwright helps ensure and maintain your application’s structure.
|
||||
You can generate snapshots in various ways depending on your testing setup and workflow.
|
||||
|
||||
### Generating snapshots with the Playwright code generator
|
||||
### 1. Generating snapshots with the Playwright code generator
|
||||
|
||||
If you're using Playwright's [Code Generator](./codegen.md), generating aria snapshots is streamlined with its
|
||||
If you’re using Playwright’s [Code Generator](./codegen.md), generating aria snapshots is streamlined with its
|
||||
interactive interface:
|
||||
|
||||
- **"Assert snapshot" Action**: In the code generator, you can use the "Assert snapshot" action to automatically create
|
||||
|
|
@ -296,18 +190,20 @@ recorded test flow.
|
|||
aria snapshot for a selected locator, letting you explore, inspect, and verify element roles, attributes, and
|
||||
accessible names to aid snapshot creation and review.
|
||||
|
||||
### Updating snapshots with `@playwright/test` and the `--update-snapshots` flag
|
||||
* langs: js
|
||||
### 2. Updating snapshots with `@playwright/test` and the `--update-snapshots` flag
|
||||
|
||||
When using the Playwright test runner (`@playwright/test`), you can automatically update snapshots with the `--update-snapshots` flag, `-u` for short.
|
||||
|
||||
Running tests with the `--update-snapshots` flag will update snapshots that did not match. Matching snapshots will not be updated.
|
||||
When using the Playwright test runner (`@playwright/test`), you can automatically update snapshots by running tests with
|
||||
the `--update-snapshots` flag:
|
||||
|
||||
```bash
|
||||
npx playwright test --update-snapshots
|
||||
```
|
||||
|
||||
Updating snapshots is useful when application structure changes require new snapshots as a baseline. Note that Playwright will wait for the maximum expect timeout specified in the test runner configuration to ensure the page is settled before taking the snapshot. It might be necessary to adjust the `--timeout` if the test hits the timeout while generating snapshots.
|
||||
This command regenerates snapshots for assertions, including aria snapshots, replacing outdated ones. It’s
|
||||
useful when application structure changes require new snapshots as a baseline. Note that Playwright will wait for the
|
||||
maximum expect timeout specified in the test runner configuration to ensure the
|
||||
page is settled before taking the snapshot. It might be necessary to adjust the `--timeout` if the test hits the timeout
|
||||
while generating snapshots.
|
||||
|
||||
#### Empty template for snapshot generation
|
||||
|
||||
|
|
@ -327,40 +223,10 @@ When updating snapshots, Playwright creates patch files that capture differences
|
|||
applied, and committed to source control, allowing teams to track structural changes over time and ensure updates are
|
||||
consistent with application requirements.
|
||||
|
||||
The way source code is updated can be changed using the `--update-source-method` flag. There are several options available:
|
||||
|
||||
- **"patch"** (default): Generates a unified diff file that can be applied to the source code using `git apply`.
|
||||
- **"3way"**: Generates merge conflict markers in your source code, allowing you to choose whether to accept changes.
|
||||
- **"overwrite"**: Overwrites the source code with the new snapshot values.
|
||||
|
||||
```bash
|
||||
npx playwright test --update-snapshots --update-source-mode=3way
|
||||
```
|
||||
|
||||
#### Snapshots as separate files
|
||||
|
||||
To store your snapshots in a separate file, use the `toMatchAriaSnapshot` method with the `name` option, specifying a `.snapshot.yml` file extension.
|
||||
|
||||
```js
|
||||
await expect(page.getByRole('main')).toMatchAriaSnapshot({ name: 'main.snapshot.yml' });
|
||||
```
|
||||
|
||||
By default, snapshots from a test file `example.spec.ts` are placed in the `example.spec.ts-snapshots` directory. As snapshots should be the same across browsers, only one snapshot is saved even if testing with multiple browsers. Should you wish, you can customize the [snapshot path template](./api/class-testconfig#test-config-snapshot-path-template) using the following configuration:
|
||||
|
||||
```js
|
||||
export default defineConfig({
|
||||
expect: {
|
||||
toMatchAriaSnapshot: {
|
||||
pathTemplate: '__snapshots__/{testFilePath}/{arg}{ext}',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Using the `Locator.ariaSnapshot` method
|
||||
### 3. Using the `Locator.ariaSnapshot` method
|
||||
|
||||
The [`method: Locator.ariaSnapshot`] method allows you to programmatically create a YAML representation of accessible
|
||||
elements within a locator's scope, especially helpful for generating snapshots dynamically during test execution.
|
||||
elements within a locator’s scope, especially helpful for generating snapshots dynamically during test execution.
|
||||
|
||||
**Example**:
|
||||
|
||||
|
|
@ -389,7 +255,7 @@ var snapshot = await page.Locator("body").AriaSnapshotAsync();
|
|||
Console.WriteLine(snapshot);
|
||||
```
|
||||
|
||||
This command outputs the aria snapshot within the specified locator's scope in YAML format, which you can validate
|
||||
This command outputs the aria snapshot within the specified locator’s scope in YAML format, which you can validate
|
||||
or store as needed.
|
||||
|
||||
## Accessibility tree examples
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ await page.goto('https://github.com/login')
|
|||
# Interact with login form
|
||||
await page.get_by_label("Username or email address").fill("username")
|
||||
await page.get_by_label("Password").fill("password")
|
||||
await page.get_by_role("button", name="Sign in").click()
|
||||
await page.page.get_by_role("button", name="Sign in").click()
|
||||
# Continue with the test
|
||||
```
|
||||
|
||||
|
|
@ -266,9 +266,9 @@ existing authentication state instead.
|
|||
Playwright provides a way to reuse the signed-in state in the tests. That way you can log
|
||||
in only once and then skip the log in step for all of the tests.
|
||||
|
||||
Web apps use cookie-based or token-based authentication, where authenticated state is stored as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies), in [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage) or in [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). Playwright provides [`method: BrowserContext.storageState`] method that can be used to retrieve storage state from authenticated contexts and then create new contexts with prepopulated state.
|
||||
Web apps use cookie-based or token-based authentication, where authenticated state is stored as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) or in [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage). Playwright provides [`method: BrowserContext.storageState`] method that can be used to retrieve storage state from authenticated contexts and then create new contexts with prepopulated state.
|
||||
|
||||
Cookies, local storage and IndexedDB state can be used across different browsers. They depend on your application's authentication model which may require some combination of cookies, local storage or IndexedDB.
|
||||
Cookies and local storage state can be used across different browsers. They depend on your application's authentication model: some apps might require both cookies and local storage.
|
||||
|
||||
The following code snippet retrieves state from an authenticated context and creates a new context with that state.
|
||||
|
||||
|
|
@ -583,7 +583,7 @@ test('admin and user', async ({ adminPage, userPage }) => {
|
|||
|
||||
### Session storage
|
||||
|
||||
Reusing authenticated state covers [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies), [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage) and [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) based authentication. Rarely, [session storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) is used for storing information associated with the signed-in state. Session storage is specific to a particular domain and is not persisted across page loads. Playwright does not provide API to persist session storage, but the following snippet can be used to save/load session storage.
|
||||
Reusing authenticated state covers [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage) based authentication. Rarely, [session storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) is used for storing information associated with the signed-in state. Session storage is specific to a particular domain and is not persisted across page loads. Playwright does not provide API to persist session storage, but the following snippet can be used to save/load session storage.
|
||||
|
||||
```js
|
||||
// Get session storage and store as env variable
|
||||
|
|
|
|||
|
|
@ -338,11 +338,11 @@ dotnet test --settings:webkit.runsettings
|
|||
|
||||
For Google Chrome, Microsoft Edge and other Chromium-based browsers, by default, Playwright uses open source Chromium builds. Since the Chromium project is ahead of the branded browsers, when the world is on Google Chrome N, Playwright already supports Chromium N+1 that will be released in Google Chrome and Microsoft Edge a few weeks later.
|
||||
|
||||
### Chromium: headless shell
|
||||
Playwright ships a regular Chromium build for headed operations and a separate [chromium headless shell](https://developer.chrome.com/blog/chrome-headless-shell) for headless mode. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for details.
|
||||
|
||||
Playwright ships a regular Chromium build for headed operations and a separate [chromium headless shell](https://developer.chrome.com/blog/chrome-headless-shell) for headless mode.
|
||||
#### Optimize download size on CI
|
||||
|
||||
If you are only running tests in headless shell (i.e. the `channel` option is **not** specified), for example on CI, you can avoid downloading the full Chromium browser by passing `--only-shell` during installation.
|
||||
If you are only running tests in headless mode, for example on CI, you can avoid downloading a regular version of Chromium by passing `--only-shell` during installation.
|
||||
|
||||
```bash js
|
||||
# only running tests headlessly
|
||||
|
|
@ -364,7 +364,7 @@ playwright install --with-deps --only-shell
|
|||
pwsh bin/Debug/netX/playwright.ps1 install --with-deps --only-shell
|
||||
```
|
||||
|
||||
### Chromium: new headless mode
|
||||
#### Opt-in to new headless mode
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
|
|
@ -419,28 +419,6 @@ pytest test_login.py --browser-channel chromium
|
|||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Channel=chromium
|
||||
```
|
||||
|
||||
With the new headless mode, you can skip downloading the headless shell during browser installation by using the `--no-shell` option:
|
||||
|
||||
```bash js
|
||||
# only running tests headlessly
|
||||
npx playwright install --with-deps --no-shell
|
||||
```
|
||||
|
||||
```bash java
|
||||
# only running tests headlessly
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps --no-shell"
|
||||
```
|
||||
|
||||
```bash python
|
||||
# only running tests headlessly
|
||||
playwright install --with-deps --no-shell
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
# only running tests headlessly
|
||||
pwsh bin/Debug/netX/playwright.ps1 install --with-deps --no-shell
|
||||
```
|
||||
|
||||
### Google Chrome & Microsoft Edge
|
||||
|
||||
While Playwright can download and use the recent Chromium build, it can operate against the branded Google Chrome and Microsoft Edge browsers available on the machine (note that Playwright doesn't install them by default). In particular, the current Playwright version will support Stable and Beta channels of these browsers.
|
||||
|
|
@ -452,7 +430,7 @@ Certain Enterprise Browser Policies may impact Playwright's ability to launch an
|
|||
:::
|
||||
|
||||
:::warning
|
||||
Google Chrome and Microsoft Edge have switched to a [new headless mode](https://developer.chrome.com/docs/chromium/headless) implementation that is closer to a regular headed mode. This differs from [chromium headless shell](https://developer.chrome.com/blog/chrome-headless-shell) that is used in Playwright by default when running headless, so expect different behavior in some cases. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for details.
|
||||
Google Chrome and Microsoft Edge have switched to a [new headless mode](https://developer.chrome.com/docs/chromium/headless) implementation that is closer to a regular headed mode. This differs from [chromium headless shell](https://developer.chrome.com/blog/chrome-headless-shell) that is used in Playwright by default when running headless, so expect different behavior in some cases. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) fore details.
|
||||
:::
|
||||
|
||||
```js
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ title: "Chrome extensions"
|
|||
Extensions only work in Chrome / Chromium launched with a persistent context. Use custom browser args at your own risk, as some of them may break Playwright functionality.
|
||||
:::
|
||||
|
||||
The snippet below retrieves the [background page](https://developer.chrome.com/extensions/background_pages) of a [Manifest v2](https://developer.chrome.com/docs/extensions/mv2/) extension whose source is located in `./my-extension`.
|
||||
|
||||
Note the use of the `chromium` channel that allows to run extensions in headless mode. Alternatively, you can launch the browser in headed mode.
|
||||
The following is code for getting a handle to the [background page](https://developer.chrome.com/extensions/background_pages) of a [Manifest v2](https://developer.chrome.com/docs/extensions/mv2/) extension whose source is located in `./my-extension`:
|
||||
|
||||
```js
|
||||
const { chromium } = require('playwright');
|
||||
|
|
@ -20,7 +18,7 @@ const { chromium } = require('playwright');
|
|||
const pathToExtension = require('path').join(__dirname, 'my-extension');
|
||||
const userDataDir = '/tmp/test-user-data-dir';
|
||||
const browserContext = await chromium.launchPersistentContext(userDataDir, {
|
||||
channel: 'chromium',
|
||||
headless: false,
|
||||
args: [
|
||||
`--disable-extensions-except=${pathToExtension}`,
|
||||
`--load-extension=${pathToExtension}`
|
||||
|
|
@ -46,7 +44,7 @@ user_data_dir = "/tmp/test-user-data-dir"
|
|||
async def run(playwright: Playwright):
|
||||
context = await playwright.chromium.launch_persistent_context(
|
||||
user_data_dir,
|
||||
channel="chromium",
|
||||
headless=False,
|
||||
args=[
|
||||
f"--disable-extensions-except={path_to_extension}",
|
||||
f"--load-extension={path_to_extension}",
|
||||
|
|
@ -80,7 +78,7 @@ user_data_dir = "/tmp/test-user-data-dir"
|
|||
def run(playwright: Playwright):
|
||||
context = playwright.chromium.launch_persistent_context(
|
||||
user_data_dir,
|
||||
channel="chromium",
|
||||
headless=False,
|
||||
args=[
|
||||
f"--disable-extensions-except={path_to_extension}",
|
||||
f"--load-extension={path_to_extension}",
|
||||
|
|
@ -103,8 +101,6 @@ with sync_playwright() as playwright:
|
|||
|
||||
To have the extension loaded when running tests you can use a test fixture to set the context. You can also dynamically retrieve the extension id and use it to load and test the popup page for example.
|
||||
|
||||
Note the use of the `chromium` channel that allows to run extensions in headless mode. Alternatively, you can launch the browser in headed mode.
|
||||
|
||||
First, add fixtures that will load the extension:
|
||||
|
||||
```js title="fixtures.ts"
|
||||
|
|
@ -118,7 +114,7 @@ export const test = base.extend<{
|
|||
context: async ({ }, use) => {
|
||||
const pathToExtension = path.join(__dirname, 'my-extension');
|
||||
const context = await chromium.launchPersistentContext('', {
|
||||
channel: 'chromium',
|
||||
headless: false,
|
||||
args: [
|
||||
`--disable-extensions-except=${pathToExtension}`,
|
||||
`--load-extension=${pathToExtension}`,
|
||||
|
|
@ -159,7 +155,7 @@ def context(playwright: Playwright) -> Generator[BrowserContext, None, None]:
|
|||
path_to_extension = Path(__file__).parent.joinpath("my-extension")
|
||||
context = playwright.chromium.launch_persistent_context(
|
||||
"",
|
||||
channel="chromium",
|
||||
headless=False,
|
||||
args=[
|
||||
f"--disable-extensions-except={path_to_extension}",
|
||||
f"--load-extension={path_to_extension}",
|
||||
|
|
@ -215,3 +211,33 @@ def test_popup_page(page: Page, extension_id: str) -> None:
|
|||
page.goto(f"chrome-extension://{extension_id}/popup.html")
|
||||
expect(page.locator("body")).to_have_text("my-extension popup")
|
||||
```
|
||||
|
||||
## Headless mode
|
||||
|
||||
By default, Chrome's headless mode in Playwright does not support Chrome extensions. To overcome this limitation, you can run Chrome's persistent context with a new headless mode by using [channel `chromium`](./browsers.md#opt-in-to-new-headless-mode):
|
||||
|
||||
```js title="fixtures.ts"
|
||||
// ...
|
||||
|
||||
const pathToExtension = path.join(__dirname, 'my-extension');
|
||||
const context = await chromium.launchPersistentContext('', {
|
||||
channel: 'chromium',
|
||||
args: [
|
||||
`--disable-extensions-except=${pathToExtension}`,
|
||||
`--load-extension=${pathToExtension}`,
|
||||
],
|
||||
});
|
||||
// ...
|
||||
```
|
||||
|
||||
```python title="conftest.py"
|
||||
path_to_extension = Path(__file__).parent.joinpath("my-extension")
|
||||
context = playwright.chromium.launch_persistent_context(
|
||||
"",
|
||||
channel="chromium",
|
||||
args=[
|
||||
f"--disable-extensions-except={path_to_extension}",
|
||||
f"--load-extension={path_to_extension}",
|
||||
],
|
||||
)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Playwright tests can be run on any CI provider. This guide covers one way of run
|
|||
## Introduction
|
||||
* langs: python, java, csharp
|
||||
|
||||
Playwright tests can be run on any CI provider. In this section we will cover running tests on GitHub using GitHub actions. If you would like to see how to configure other CI providers check out our detailed doc on Continuous Integration.
|
||||
Playwright tests can be ran on any CI provider. In this section we will cover running tests on GitHub using GitHub actions. If you would like to see how to configure other CI providers check out our detailed doc on Continuous Integration.
|
||||
|
||||
#### You will learn
|
||||
* langs: python, java, csharp
|
||||
|
|
|
|||
|
|
@ -454,11 +454,11 @@ jobs:
|
|||
|
||||
### Docker
|
||||
|
||||
We have a [pre-built Docker image](./docker.md) which can either be used directly or as a reference to update your existing Docker definitions.
|
||||
We have a [pre-built Docker image](./docker.md) which can either be used directly, or as a reference to update your existing Docker definitions.
|
||||
|
||||
Suggested configuration
|
||||
1. Using `--ipc=host` is also recommended when using Chromium. Without it Chromium can run out of memory
|
||||
and crash. Learn more about this option in [Docker docs](https://docs.docker.com/reference/cli/docker/container/run/#ipc).
|
||||
and crash. Learn more about this option in [Docker docs](https://docs.docker.com/engine/reference/run/#ipc-settings---ipc).
|
||||
1. Seeing other weird errors when launching Chromium? Try running your container
|
||||
with `docker run --cap-add=SYS_ADMIN` when developing locally.
|
||||
1. Using `--init` Docker flag or [dumb-init](https://github.com/Yelp/dumb-init) is recommended to avoid special
|
||||
|
|
@ -466,7 +466,7 @@ Suggested configuration
|
|||
|
||||
### Azure Pipelines
|
||||
|
||||
For Windows or macOS agents, no additional configuration is required, just install Playwright and run your tests.
|
||||
For Windows or macOS agents, no additional configuration required, just install Playwright and run your tests.
|
||||
|
||||
For Linux agents, you can use [our Docker container](./docker.md) with Azure
|
||||
Pipelines support [running containerized
|
||||
|
|
@ -804,7 +804,7 @@ Sharding in CircleCI is indexed with 0 which means that you will need to overrid
|
|||
executor: pw-noble-development
|
||||
parallelism: 4
|
||||
steps:
|
||||
- run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npx playwright test --shard=${SHARD}/${CIRCLE_NODE_TOTAL}
|
||||
- run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npx playwright test -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL}
|
||||
```
|
||||
|
||||
### Jenkins
|
||||
|
|
|
|||
|
|
@ -34,10 +34,6 @@ The recommended approach is to use `setFixedTime` to set the time to a specific
|
|||
- `Event.timeStamp`
|
||||
:::
|
||||
|
||||
:::warning
|
||||
If you call `install` at any point in your test, the call _MUST_ occur before any other clock related calls (see note above for list). Calling these methods out of order will result in undefined behavior. For example, you cannot call `setInterval`, followed by `install`, then `clearInterval`, as `install` overrides the native definition of the clock functions.
|
||||
:::
|
||||
|
||||
## Test with predefined time
|
||||
|
||||
Often you only need to fake `Date.now` while keeping the timers going.
|
||||
|
|
@ -168,11 +164,11 @@ await Page.GotoAsync("http://localhost:3333");
|
|||
await Page.Clock.PauseAtAsync(new DateTime(2024, 2, 2, 10, 0, 0));
|
||||
|
||||
// Assert the page state.
|
||||
await Expect(Page.GetByTestId("current-time")).ToHaveTextAsync("2/2/2024, 10:00:00 AM");
|
||||
await Expect(Page.GetByTestId("current-time")).ToHaveText("2/2/2024, 10:00:00 AM");
|
||||
|
||||
// Close the laptop lid again and open it at 10:30am.
|
||||
await Page.Clock.FastForwardAsync("30:00");
|
||||
await Expect(Page.GetByTestId("current-time")).ToHaveTextAsync("2/2/2024, 10:30:00 AM");
|
||||
await Expect(Page.GetByTestId("current-time")).ToHaveText("2/2/2024, 10:30:00 AM");
|
||||
```
|
||||
|
||||
## Test inactivity monitoring
|
||||
|
|
|
|||
|
|
@ -164,19 +164,19 @@ You can use the test generator to generate tests using emulation so as to genera
|
|||
Playwright opens a browser window with its viewport set to a specific width and height and is not responsive as tests need to be run under the same conditions. Use the `--viewport` option to generate tests with a different viewport size.
|
||||
|
||||
```bash js
|
||||
npx playwright codegen --viewport-size="800,600" playwright.dev
|
||||
npx playwright codegen --viewport-size=800,600 playwright.dev
|
||||
```
|
||||
|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="codegen --viewport-size='800,600' playwright.dev"
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="codegen --viewport-size=800,600 playwright.dev"
|
||||
```
|
||||
|
||||
```bash python
|
||||
playwright codegen --viewport-size="800,600" playwright.dev
|
||||
playwright codegen --viewport-size=800,600 playwright.dev
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 codegen --viewport-size="800,600" playwright.dev
|
||||
pwsh bin/Debug/netX/playwright.ps1 codegen --viewport-size=800,600 playwright.dev
|
||||
```
|
||||
######
|
||||
* langs: js
|
||||
|
|
@ -325,7 +325,7 @@ pwsh bin/Debug/netX/playwright.ps1 codegen --timezone="Europe/Rome" --geolocatio
|
|||
|
||||
### Preserve authenticated state
|
||||
|
||||
Run `codegen` with `--save-storage` to save [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies), [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) data at the end of the session. This is useful to separately record an authentication step and reuse it later when recording more tests.
|
||||
Run `codegen` with `--save-storage` to save [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) at the end of the session. This is useful to separately record an authentication step and reuse it later when recording more tests.
|
||||
|
||||
```bash js
|
||||
npx playwright codegen github.com/microsoft/playwright --save-storage=auth.json
|
||||
|
|
@ -375,7 +375,7 @@ Make sure you only use the `auth.json` locally as it contains sensitive informat
|
|||
|
||||
#### Load authenticated state
|
||||
|
||||
Run with `--load-storage` to consume the previously loaded storage from the `auth.json`. This way, all [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies), [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) data will be restored, bringing most web apps to the authenticated state without the need to login again. This means you can continue generating tests from the logged in state.
|
||||
Run with `--load-storage` to consume the previously loaded storage from the `auth.json`. This way, all [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) will be restored, bringing most web apps to the authenticated state without the need to login again. This means you can continue generating tests from the logged in state.
|
||||
|
||||
```bash js
|
||||
npx playwright codegen --load-storage=auth.json github.com/microsoft/playwright
|
||||
|
|
|
|||
|
|
@ -103,88 +103,6 @@ Using `--ipc=host` is recommended when using Chrome ([Docker docs](https://docs.
|
|||
|
||||
See our [Continuous Integration guides](./ci.md) for sample configs.
|
||||
|
||||
### Remote Connection
|
||||
|
||||
You can run Playwright Server in Docker while keeping your tests running on the host system or another machine. This is useful for running tests on unsupported Linux distributions or remote execution scenarios.
|
||||
|
||||
#### Running the Playwright Server
|
||||
|
||||
Start the Playwright Server in Docker:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v%%VERSION%%-noble /bin/sh -c "npx -y playwright@%%VERSION%% run-server --port 3000 --host 0.0.0.0"
|
||||
```
|
||||
|
||||
#### Connecting to the Server
|
||||
* langs: js
|
||||
|
||||
There are two ways to connect to the remote Playwright server:
|
||||
|
||||
1. Using environment variable with `@playwright/test`:
|
||||
|
||||
```bash
|
||||
PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:3000/ npx playwright test
|
||||
```
|
||||
|
||||
2. Using the [`method: BrowserType.connect`] API for other applications:
|
||||
|
||||
```js
|
||||
const browser = await playwright['chromium'].connect('ws://127.0.0.1:3000/');
|
||||
```
|
||||
|
||||
#### Connecting to the Server
|
||||
* langs: python, csharp, java
|
||||
|
||||
```python sync
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.connect("ws://127.0.0.1:3000/")
|
||||
```
|
||||
|
||||
```python async
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.connect("ws://127.0.0.1:3000/")
|
||||
```
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright;
|
||||
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.Chromium.ConnectAsync("ws://127.0.0.1:3000/");
|
||||
```
|
||||
|
||||
```java
|
||||
package org.example;
|
||||
|
||||
import com.microsoft.playwright.*;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().connect("ws://127.0.0.1:3000/");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Network Configuration
|
||||
|
||||
If you need to access local servers from within the Docker container:
|
||||
|
||||
```bash
|
||||
docker run --add-host=hostmachine:host-gateway -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v%%VERSION%%-noble /bin/sh -c "npx -y playwright@%%VERSION%% run-server --port 3000 --host 0.0.0.0"
|
||||
```
|
||||
|
||||
This makes `hostmachine` point to the host's localhost. Your tests should use `hostmachine` instead of `localhost` when accessing local servers.
|
||||
|
||||
:::note
|
||||
When running tests remotely, ensure the Playwright version in your tests matches the version running in the Docker container.
|
||||
:::
|
||||
|
||||
## Image tags
|
||||
|
||||
See [all available image tags].
|
||||
|
|
@ -214,7 +132,7 @@ Browser builds for Firefox and WebKit are built for the [glibc](https://en.wikip
|
|||
You can use the [.NET install script](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script) in order to install different SDK versions:
|
||||
|
||||
```bash
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --install-dir /usr/share/dotnet --channel 9.0
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --install-dir /usr/share/dotnet --channel 6.0
|
||||
```
|
||||
|
||||
## Build your own image
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ await page.getByText('Item').click({ button: 'right' });
|
|||
// Shift + click
|
||||
await page.getByText('Item').click({ modifiers: ['Shift'] });
|
||||
|
||||
// Ctrl + click on Windows and Linux
|
||||
// Ctrl + click or Windows and Linux
|
||||
// Meta + click on macOS
|
||||
await page.getByText('Item').click({ modifiers: ['ControlOrMeta'] });
|
||||
|
||||
|
|
@ -241,7 +241,7 @@ 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 on Windows and Linux
|
||||
// Ctrl + click or Windows and Linux
|
||||
// Meta + click on macOS
|
||||
page.getByText("Item").click(new Locator.ClickOptions().setModifiers(Arrays.asList(KeyboardModifier.CONTROL_OR_META)));
|
||||
|
||||
|
|
@ -265,7 +265,7 @@ await page.get_by_text("Item").click(button="right")
|
|||
# Shift + click
|
||||
await page.get_by_text("Item").click(modifiers=["Shift"])
|
||||
|
||||
# Ctrl + click on Windows and Linux
|
||||
# Ctrl + click or Windows and Linux
|
||||
# Meta + click on macOS
|
||||
await page.get_by_text("Item").click(modifiers=["ControlOrMeta"])
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ await page.GetByText("Item").ClickAsync(new() { Button = MouseButton.Right });
|
|||
// Shift + click
|
||||
await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.Shift } });
|
||||
|
||||
// Ctrl + click on Windows and Linux
|
||||
// Ctrl + click or Windows and Linux
|
||||
// Meta + click on macOS
|
||||
await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.ControlOrMeta } });
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ title: "Installation"
|
|||
|
||||
Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation.
|
||||
|
||||
You can choose to use MSTest, NUnit, or xUnit [base classes](./test-runners.md) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
|
||||
You can choose to use [MSTest base classes](./test-runners.md#mstest) or [NUnit base classes](./test-runners.md#nunit) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
|
||||
|
||||
1. Start by creating a new project with `dotnet new`. This will create the `PlaywrightTests` directory which includes a `UnitTest1.cs` file:
|
||||
|
||||
|
|
@ -17,7 +17,6 @@ You can choose to use MSTest, NUnit, or xUnit [base classes](./test-runners.md)
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -35,14 +34,6 @@ dotnet new mstest -n PlaywrightTests
|
|||
cd PlaywrightTests
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```bash
|
||||
dotnet new xunit -n PlaywrightTests
|
||||
cd PlaywrightTests
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -54,7 +45,6 @@ cd PlaywrightTests
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -70,13 +60,6 @@ dotnet add package Microsoft.Playwright.NUnit
|
|||
dotnet add package Microsoft.Playwright.MSTest
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```bash
|
||||
dotnet add package Microsoft.Playwright.Xunit
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -104,7 +87,6 @@ Edit the `UnitTest1.cs` file with the code below to create an example end-to-end
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -182,41 +164,6 @@ public class ExampleTest : PageTest
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task HasTitle()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
|
||||
// Click the get started link.
|
||||
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();
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Running the Example Tests
|
||||
|
|
@ -243,4 +190,4 @@ See our doc on [Running and Debugging Tests](./running-tests.md) to learn more a
|
|||
- [Generate tests with Codegen](./codegen-intro.md)
|
||||
- [See a trace of your tests](./trace-viewer-intro.md)
|
||||
- [Run tests on CI](./ci-intro.md)
|
||||
- [Learn more about the MSTest, NUnit, and xUnit base classes](./test-runners.md)
|
||||
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ The `tests` folder contains a basic example test to help you get started with te
|
|||
|
||||
## Running the Example Test
|
||||
|
||||
By default tests will be run on all 3 browsers, Chromium, Firefox and WebKit using 3 workers. This can be configured in the [playwright.config file](./test-configuration.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.
|
||||
By default tests will be run on all 3 browsers, chromium, firefox and webkit using 3 workers. This can be configured in the [playwright.config file](./test-configuration.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
|
||||
defaultValue="npm"
|
||||
|
|
@ -286,7 +286,7 @@ pnpm exec playwright --version
|
|||
|
||||
## System requirements
|
||||
|
||||
- Latest version of Node.js 18, 20 or 22.
|
||||
- Node.js 18+
|
||||
- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL).
|
||||
- macOS 13 Ventura, or later.
|
||||
- Debian 12, Ubuntu 22.04, Ubuntu 24.04, on x86-64 and arm64 architecture.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ You can choose any testing framework such as JUnit or TestNG based on your proje
|
|||
|
||||
## .NET
|
||||
|
||||
Playwright for .NET comes with MSTest, NUnit, and xUnit [base classes](https://playwright.dev/dotnet/docs/test-runners) for writing end-to-end tests.
|
||||
Playwright for .NET comes with [MSTest base classes](https://playwright.dev/dotnet/docs/test-runners#mstest) and [NUnit base classes](https://playwright.dev/dotnet/docs/test-runners#nunit) for writing end-to-end tests.
|
||||
|
||||
* [Documentation](https://playwright.dev/dotnet/docs/intro)
|
||||
* [GitHub repo](https://github.com/microsoft/playwright-dotnet)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ title: "Getting started - Library"
|
|||
|
||||
## Introduction
|
||||
|
||||
Playwright can either be used with the [MSTest, NUnit, or xUnit base classes](./test-runners.md) or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
|
||||
Playwright can either be used with the [MSTest](./test-runners.md#mstest) or [NUnit](./test-runners.md#nunit), or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
|
|||
|
|
@ -751,10 +751,10 @@ page.locator("x-details", new Page.LocatorOptions().setHasText("Details"))
|
|||
.click();
|
||||
```
|
||||
```python async
|
||||
await page.locator("x-details", has_text="Details").click()
|
||||
await page.locator("x-details", has_text="Details" ).click()
|
||||
```
|
||||
```python sync
|
||||
page.locator("x-details", has_text="Details").click()
|
||||
page.locator("x-details", has_text="Details" ).click()
|
||||
```
|
||||
```csharp
|
||||
await page
|
||||
|
|
@ -1310,19 +1310,19 @@ Consider a page with two buttons, the first invisible and the second [visible](.
|
|||
* This will only find a second button, because it is visible, and then click it.
|
||||
|
||||
```js
|
||||
await page.locator('button').filter({ visible: true }).click();
|
||||
await page.locator('button').locator('visible=true').click();
|
||||
```
|
||||
```java
|
||||
page.locator("button").filter(new Locator.FilterOptions.setVisible(true)).click();
|
||||
page.locator("button").locator("visible=true").click();
|
||||
```
|
||||
```python async
|
||||
await page.locator("button").filter(visible=True).click()
|
||||
await page.locator("button").locator("visible=true").click()
|
||||
```
|
||||
```python sync
|
||||
page.locator("button").filter(visible=True).click()
|
||||
page.locator("button").locator("visible=true").click()
|
||||
```
|
||||
```csharp
|
||||
await page.Locator("button").Filter(new() { Visible = true }).ClickAsync();
|
||||
await page.Locator("button").Locator("visible=true").ClickAsync();
|
||||
```
|
||||
|
||||
## Lists
|
||||
|
|
|
|||
|
|
@ -115,7 +115,8 @@ await page.GotoAsync("https://example.com");
|
|||
You can configure pages to load over the HTTP(S) proxy or SOCKSv5. Proxy can be either set globally
|
||||
for the entire browser, or for each browser context individually.
|
||||
|
||||
You can optionally specify username and password for HTTP(S) proxy, you can also specify hosts to bypass the [`option: Browser.newContext.proxy`] for.
|
||||
You can optionally specify username and password for HTTP(S) proxy, you can also specify hosts to
|
||||
bypass proxy for.
|
||||
|
||||
Here is an example of a global proxy:
|
||||
|
||||
|
|
@ -708,13 +709,9 @@ Playwright uses simplified glob patterns for URL matching in network interceptio
|
|||
- A double `**` matches any characters including `/`
|
||||
1. Question mark `?` matches any single character except `/`
|
||||
1. Curly braces `{}` can be used to match a list of options separated by commas `,`
|
||||
1. Square brackets `[]` can be used to match a set of characters
|
||||
1. Backslash `\` can be used to escape any of special characters (note to escape backslash itself as `\\`)
|
||||
|
||||
Examples:
|
||||
- `https://example.com/*.js` matches `https://example.com/file.js` but not `https://example.com/path/file.js`
|
||||
- `https://example.com/\\?page=1` matches `https://example.com/?page=1` but not `https://example.com`
|
||||
- `**/v[0-9]*` matches `https://example.com/v1/` but not `https://example.com/vote/`
|
||||
- `**/*.js` matches both `https://example.com/file.js` and `https://example.com/path/file.js`
|
||||
- `**/*.{png,jpg,jpeg}` matches all image requests
|
||||
|
||||
|
|
|
|||
|
|
@ -4,37 +4,6 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.50
|
||||
|
||||
### Support for Xunit
|
||||
|
||||
* Support for xUnit 2.8+ via [Microsoft.Playwright.Xunit](https://www.nuget.org/packages/Microsoft.Playwright.Xunit). Follow our [Getting Started](./intro.md) guide to learn more.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* Added method [`method: LocatorAssertions.toHaveAccessibleErrorMessage`] to assert the Locator points to an element with a given [aria errormessage](https://w3c.github.io/aria/#aria-errormessage).
|
||||
|
||||
### UI updates
|
||||
|
||||
* New button in Codegen for picking elements to produce aria snapshots.
|
||||
* Additional details (such as keys pressed) are now displayed alongside action API calls in traces.
|
||||
* Display of `canvas` content in traces is error-prone. Display is now disabled by default, and can be enabled via the `Display canvas content` UI setting.
|
||||
* `Call` and `Network` panels now display additional time information.
|
||||
|
||||
### Breaking
|
||||
|
||||
* [`method: LocatorAssertions.toBeEditable`] and [`method: Locator.isEditable`] now throw if the target element is not `<input>`, `<select>`, or a number of other editable elements.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 133.0.6943.16
|
||||
* Mozilla Firefox 134.0
|
||||
* WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 132
|
||||
* Microsoft Edge 132
|
||||
|
||||
## Version 1.49
|
||||
|
||||
|
|
@ -824,9 +793,9 @@ This version was also tested against the following stable channels:
|
|||
|
||||
```html
|
||||
<select multiple>
|
||||
<option value="red">Red</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="red">Red</div>
|
||||
<option value="green">Green</div>
|
||||
<option value="blue">Blue</div>
|
||||
</select>
|
||||
```
|
||||
|
||||
|
|
@ -904,7 +873,7 @@ All the same methods are also available on [Locator], [FrameLocator] and [Frame]
|
|||
- [`method: LocatorAssertions.toHaveAttribute`] with an empty value does not match missing attribute anymore. For example, the following snippet will succeed when `button` **does not** have a `disabled` attribute.
|
||||
|
||||
```csharp
|
||||
await Expect(Page.GetByRole(AriaRole.Button)).ToHaveAttributeAsync("disabled", "");
|
||||
await Expect(Page.GetByRole(AriaRole.Button)).ToHaveAttribute("disabled", "");
|
||||
```
|
||||
|
||||
### Browser Versions
|
||||
|
|
|
|||
|
|
@ -4,34 +4,6 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.50
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* Added method [`method: LocatorAssertions.toHaveAccessibleErrorMessage`] to assert the Locator points to an element with a given [aria errormessage](https://w3c.github.io/aria/#aria-errormessage).
|
||||
|
||||
### UI updates
|
||||
|
||||
* New button in Codegen for picking elements to produce aria snapshots.
|
||||
* Additional details (such as keys pressed) are now displayed alongside action API calls in traces.
|
||||
* Display of `canvas` content in traces is error-prone. Display is now disabled by default, and can be enabled via the `Display canvas content` UI setting.
|
||||
* `Call` and `Network` panels now display additional time information.
|
||||
|
||||
### Breaking
|
||||
|
||||
* [`method: LocatorAssertions.toBeEditable`] and [`method: Locator.isEditable`] now throw if the target element is not `<input>`, `<select>`, or a number of other editable elements.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 133.0.6943.16
|
||||
* Mozilla Firefox 134.0
|
||||
* WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 132
|
||||
* Microsoft Edge 132
|
||||
|
||||
## Version 1.49
|
||||
|
||||
### Aria snapshots
|
||||
|
|
@ -888,9 +860,9 @@ This version was also tested against the following stable channels:
|
|||
|
||||
```html
|
||||
<select multiple>
|
||||
<option value="red">Red</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="red">Red</div>
|
||||
<option value="green">Green</div>
|
||||
<option value="blue">Blue</div>
|
||||
</select>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -6,75 +6,6 @@ toc_max_heading_level: 2
|
|||
|
||||
import LiteYouTube from '@site/src/components/LiteYouTube';
|
||||
|
||||
## Version 1.50
|
||||
|
||||
### Test runner
|
||||
|
||||
* New option [`option: Test.step.timeout`] allows specifying a maximum run time for an individual test step. A timed-out step will fail the execution of the test.
|
||||
|
||||
```js
|
||||
test('some test', async ({ page }) => {
|
||||
await test.step('a step', async () => {
|
||||
// This step can time out separately from the test
|
||||
}, { timeout: 1000 });
|
||||
});
|
||||
```
|
||||
|
||||
* New method [`method: Test.step.skip`] to disable execution of a test step.
|
||||
|
||||
```js
|
||||
test('some test', async ({ page }) => {
|
||||
await test.step('before running step', async () => {
|
||||
// Normal step
|
||||
});
|
||||
|
||||
await test.step.skip('not yet ready', async () => {
|
||||
// This step is skipped
|
||||
});
|
||||
|
||||
await test.step('after running step', async () => {
|
||||
// This step still runs even though the previous one was skipped
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
* Expanded [`method: LocatorAssertions.toMatchAriaSnapshot#2`] to allow storing of aria snapshots in separate YAML files.
|
||||
* Added method [`method: LocatorAssertions.toHaveAccessibleErrorMessage`] to assert the Locator points to an element with a given [aria errormessage](https://w3c.github.io/aria/#aria-errormessage).
|
||||
* Option [`property: TestConfig.updateSnapshots`] added the configuration enum `changed`. `changed` updates only the snapshots that have changed, whereas `all` now updates all snapshots, regardless of whether there are any differences.
|
||||
* New option [`property: TestConfig.updateSourceMethod`] defines the way source code is updated when [`property: TestConfig.updateSnapshots`] is configured. Added `overwrite` and `3-way` modes that write the changes into source code, on top of existing `patch` mode that creates a patch file.
|
||||
|
||||
```bash
|
||||
npx playwright test --update-snapshots=changed --update-source-method=3way
|
||||
```
|
||||
|
||||
* Option [`property: TestConfig.webServer`] added a `gracefulShutdown` field for specifying a process kill signal other than the default `SIGKILL`.
|
||||
* Exposed [`property: TestStep.attachments`] from the reporter API to allow retrieval of all attachments created by that step.
|
||||
* New option `pathTemplate` for `toHaveScreenshot` and `toMatchAriaSnapshot` assertions in the [`property: TestConfig.expect`] configuration.
|
||||
|
||||
### UI updates
|
||||
|
||||
* Updated default HTML reporter to improve display of attachments.
|
||||
* New button in Codegen for picking elements to produce aria snapshots.
|
||||
* Additional details (such as keys pressed) are now displayed alongside action API calls in traces.
|
||||
* Display of `canvas` content in traces is error-prone. Display is now disabled by default, and can be enabled via the `Display canvas content` UI setting.
|
||||
* `Call` and `Network` panels now display additional time information.
|
||||
|
||||
### Breaking
|
||||
|
||||
* [`method: LocatorAssertions.toBeEditable`] and [`method: Locator.isEditable`] now throw if the target element is not `<input>`, `<select>`, or a number of other editable elements.
|
||||
* Option [`property: TestConfig.updateSnapshots`] now updates all snapshots when set to `all`, rather than only the failed/changed snapshots. Use the new enum `changed` to keep the old functionality of only updating the changed snapshots.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 133.0.6943.16
|
||||
* Mozilla Firefox 134.0
|
||||
* WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 132
|
||||
* Microsoft Edge 132
|
||||
|
||||
## Version 1.49
|
||||
|
||||
<LiteYouTube
|
||||
|
|
@ -112,7 +43,7 @@ Learn more in the [aria snapshots guide](./aria-snapshots).
|
|||
- Added "previous" and "next" buttons to the HTML report to quickly switch between test cases.
|
||||
- New properties [`property: TestInfoError.cause`] and [`property: TestError.cause`] mirroring [`Error.cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause).
|
||||
|
||||
### Breaking: `chrome` and `msedge` channels switch to new headless mode
|
||||
### Breaking: channels `chrome`, `msedge` and similar switch to new headless
|
||||
|
||||
This change affects you if you're using one of the following channels in your `playwright.config.ts`:
|
||||
- `chrome`, `chrome-dev`, `chrome-beta`, or `chrome-canary`
|
||||
|
|
@ -1498,9 +1429,9 @@ This version was also tested against the following stable channels:
|
|||
|
||||
```html
|
||||
<select multiple>
|
||||
<option value="red">Red</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="red">Red</div>
|
||||
<option value="green">Green</div>
|
||||
<option value="blue">Blue</div>
|
||||
</select>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -4,38 +4,6 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.50
|
||||
|
||||
### Async Pytest Plugin
|
||||
|
||||
* [Playwright's Pytest plugin](./test-runners.md) now has support for [Async Fixtures](https://playwright.dev/python/docs/test-runners#async-fixtures).
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* Added method [`method: LocatorAssertions.toHaveAccessibleErrorMessage`] to assert the Locator points to an element with a given [aria errormessage](https://w3c.github.io/aria/#aria-errormessage).
|
||||
|
||||
### UI updates
|
||||
|
||||
* New button in Codegen for picking elements to produce aria snapshots.
|
||||
* Additional details (such as keys pressed) are now displayed alongside action API calls in traces.
|
||||
* Display of `canvas` content in traces is error-prone. Display is now disabled by default, and can be enabled via the `Display canvas content` UI setting.
|
||||
* `Call` and `Network` panels now display additional time information.
|
||||
|
||||
### Breaking
|
||||
|
||||
* [`method: LocatorAssertions.toBeEditable`] and [`method: Locator.isEditable`] now throw if the target element is not `<input>`, `<select>`, or a number of other editable elements.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 133.0.6943.16
|
||||
* Mozilla Firefox 134.0
|
||||
* WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 132
|
||||
* Microsoft Edge 132
|
||||
|
||||
## Version 1.49
|
||||
|
||||
### Aria snapshots
|
||||
|
|
@ -800,9 +768,9 @@ This version was also tested against the following stable channels:
|
|||
|
||||
```html
|
||||
<select multiple>
|
||||
<option value="red">Red</option>
|
||||
<option value="green">Green</option>
|
||||
<option value="blue">Blue</option>
|
||||
<option value="red">Red</div>
|
||||
<option value="green">Green</div>
|
||||
<option value="blue">Blue</div>
|
||||
</select>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,6 @@ dotnet test --filter "Name~GetStartedLink"
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -129,19 +128,6 @@ dotnet test -- NUnit.NumberOfTestWorkers=5
|
|||
dotnet test -- MSTest.Parallelize.Workers=5
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```bash
|
||||
dotnet test -- xUnit.MaxParallelThreads=5
|
||||
```
|
||||
|
||||
See [here](https://xunit.net/docs/running-tests-in-parallel.html) for more information to run tests in parallel with xUnit.
|
||||
|
||||
:::note
|
||||
We recommend xUnit 2.8+ which uses the [`conservative` parallelism algorithm](https://xunit.net/docs/running-tests-in-parallel.html#algorithms) by default.
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ npx playwright test --ui
|
|||
|
||||

|
||||
|
||||
Check out or [detailed guide on UI Mode](./test-ui-mode.md) to learn more about its features.
|
||||
Check out or [detailed guide on UI Mode](./test-ui-mode.md) to learn more about it's features.
|
||||
|
||||
### Run tests in headed mode
|
||||
|
||||
|
|
@ -112,11 +112,11 @@ npx playwright test --ui
|
|||
|
||||

|
||||
|
||||
While debugging you can use the Pick Locator button to select an element on the page and see the locator that Playwright would use to find that element. You can also edit the locator in the locator playground and see it highlighting live on the Browser window. Use the Copy Locator button to copy the locator to your clipboard and then paste it into your test.
|
||||
While debugging you can use the Pick Locator button to select an element on the page and see the locator that Playwright would use to find that element. You can also edit the locator in the locator playground and see it highlighting live on the Browser window. Use the Copy Locator button to copy the locator to your clipboard and then paste it into you test.
|
||||
|
||||

|
||||
|
||||
Check out our [detailed guide on UI Mode](./test-ui-mode.md) to learn more about its features.
|
||||
Check out our [detailed guide on UI Mode](./test-ui-mode.md) to learn more about it's features.
|
||||
|
||||
### Debug tests with the Playwright Inspector
|
||||
|
||||
|
|
|
|||
|
|
@ -93,8 +93,8 @@ See [`property: TestConfig.reporter`].
|
|||
## property: FullConfig.reportSlowTests
|
||||
* since: v1.10
|
||||
- type: <[null]|[Object]>
|
||||
- `max` <[int]> The maximum number of slow test files to report.
|
||||
- `threshold` <[float]> Test file duration in milliseconds that is considered slow.
|
||||
- `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`].
|
||||
|
||||
|
|
@ -114,16 +114,10 @@ See [`property: TestConfig.shard`].
|
|||
|
||||
## property: FullConfig.updateSnapshots
|
||||
* since: v1.10
|
||||
- type: <[UpdateSnapshots]<"all"|"changed"|"missing"|"none">>
|
||||
- type: <[UpdateSnapshots]<"all"|"none"|"missing">>
|
||||
|
||||
See [`property: TestConfig.updateSnapshots`].
|
||||
|
||||
## property: FullConfig.updateSourceMethod
|
||||
* since: v1.50
|
||||
- type: <[UpdateSourceMethod]<"overwrite"|"3way"|"patch">>
|
||||
|
||||
See [`property: TestConfig.updateSourceMethod`].
|
||||
|
||||
## property: FullConfig.version
|
||||
* since: v1.20
|
||||
- type: <[string]>
|
||||
|
|
|
|||
|
|
@ -1751,7 +1751,7 @@ Step name.
|
|||
|
||||
### param: Test.step.body
|
||||
* since: v1.10
|
||||
- `body` <[function]\([TestStepInfo]\):[Promise]<[any]>>
|
||||
- `body` <[function]\(\):[Promise]<[any]>>
|
||||
|
||||
Step body.
|
||||
|
||||
|
|
@ -1767,63 +1767,6 @@ Whether to box the step in the report. Defaults to `false`. When the step is box
|
|||
|
||||
Specifies a custom location for the step to be shown in test reports and trace viewer. By default, location of the [`method: Test.step`] call is shown.
|
||||
|
||||
## async method: Test.step.skip
|
||||
* since: v1.50
|
||||
- returns: <[void]>
|
||||
|
||||
Mark a test step as "skip" to temporarily disable its execution, useful for steps that are currently failing and planned for a near-term fix. Playwright will not run the step.
|
||||
|
||||
**Usage**
|
||||
|
||||
You can declare a skipped step, and Playwright will not run it.
|
||||
|
||||
```js
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('my test', async ({ page }) => {
|
||||
// ...
|
||||
await test.step.skip('not yet ready', async () => {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### param: Test.step.skip.title
|
||||
* since: v1.50
|
||||
- `title` <[string]>
|
||||
|
||||
Step name.
|
||||
|
||||
### param: Test.step.skip.body
|
||||
* since: v1.50
|
||||
- `body` <[function]\(\):[Promise]<[any]>>
|
||||
|
||||
Step body.
|
||||
|
||||
### option: Test.step.skip.box
|
||||
* since: v1.50
|
||||
- `box` <boolean>
|
||||
|
||||
Whether to box the step in the report. Defaults to `false`. When the step is boxed, errors thrown from the step internals point to the step call site. See below for more details.
|
||||
|
||||
### option: Test.step.skip.location
|
||||
* since: v1.50
|
||||
- `location` <[Location]>
|
||||
|
||||
Specifies a custom location for the step to be shown in test reports and trace viewer. By default, location of the [`method: Test.step`] call is shown.
|
||||
|
||||
### option: Test.step.skip.timeout
|
||||
* since: v1.50
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds for the step to finish. Defaults to `0` (no timeout).
|
||||
|
||||
### option: Test.step.timeout
|
||||
* since: v1.50
|
||||
- `timeout` <[float]>
|
||||
|
||||
The maximum time, in milliseconds, allowed for the step to complete. If the step does not complete within the specified timeout, the [`method: Test.step`] method will throw a [TimeoutError]. Defaults to `0` (no timeout).
|
||||
|
||||
## method: Test.use
|
||||
* since: v1.10
|
||||
|
||||
|
|
|
|||
|
|
@ -48,9 +48,6 @@ export default defineConfig({
|
|||
- `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: Page.screenshot.scale`] in [`method: Page.screenshot`]. Defaults to `"css"`.
|
||||
- `stylePath` ?<[string]|[Array]<[string]>> See [`option: Page.screenshot.style`] in [`method: Page.screenshot`].
|
||||
- `threshold` ?<[float]> An acceptable perceived color difference between the same pixel in compared images, ranging from `0` (strict) and `1` (lax). `"pixelmatch"` comparator computes color difference in [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`.
|
||||
- `pathTemplate` ?<[string]> A template controlling location of the screenshots. See [`property: TestConfig.snapshotPathTemplate`] for details.
|
||||
- `toMatchAriaSnapshot` ?<[Object]> Configuration for the [`method: LocatorAssertions.toMatchAriaSnapshot#2`] method.
|
||||
- `pathTemplate` ?<[string]> A template controlling location of the aria snapshots. See [`property: TestConfig.snapshotPathTemplate`] for details.
|
||||
- `toMatchSnapshot` ?<[Object]> Configuration for the [`method: SnapshotAssertions.toMatchSnapshot#1`] method.
|
||||
- `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.
|
||||
|
|
@ -237,12 +234,7 @@ export default defineConfig({
|
|||
* since: v1.10
|
||||
- type: ?<[Metadata]>
|
||||
|
||||
Metadata contains key-value pairs to be included in the report. For example, HTML report will display it as key-value pairs, and JSON report will include metadata serialized as json.
|
||||
|
||||
* Providing `gitCommit: 'generate'` property will populate it with the git commit details.
|
||||
* Providing `gitDiff: 'generate'` property will populate it with the git diff details.
|
||||
|
||||
On selected CI providers, both will be generated automatically. Specifying values will prevent the automatic generation.
|
||||
Metadata that will be put directly to the test report serialized as JSON.
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
@ -250,7 +242,7 @@ On selected CI providers, both will be generated automatically. Specifying value
|
|||
import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
metadata: { title: 'acceptance tests' },
|
||||
metadata: 'acceptance tests',
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -429,7 +421,7 @@ export default defineConfig({
|
|||
* since: v1.10
|
||||
- type: ?<[null]|[Object]>
|
||||
- `max` <[int]> The maximum number of slow test files to report. Defaults to `5`.
|
||||
- `threshold` <[float]> Test file duration in milliseconds that is considered slow. Defaults to 5 minutes.
|
||||
- `threshold` <[float]> Test duration in milliseconds that is considered slow. Defaults to 15 seconds.
|
||||
|
||||
Whether to report slow test files. Pass `null` to disable this feature.
|
||||
|
||||
|
|
@ -578,13 +570,12 @@ export default defineConfig({
|
|||
|
||||
## property: TestConfig.updateSnapshots
|
||||
* since: v1.10
|
||||
- type: ?<[UpdateSnapshots]<"all"|"changed"|"missing"|"none">>
|
||||
- type: ?<[UpdateSnapshots]<"all"|"none"|"missing">>
|
||||
|
||||
Whether to update expected snapshots with the actual results produced by the test run. Defaults to `'missing'`.
|
||||
* `'all'` - All tests that are executed will update snapshots.
|
||||
* `'changed'` - All tests that are executed will update snapshots that did not match. Matching snapshots will not be updated.
|
||||
* `'missing'` - Missing snapshots are created, for example when authoring a new test and running it for the first time. This is the default.
|
||||
* `'all'` - All tests that are executed will update snapshots that did not match. Matching snapshots will not be updated.
|
||||
* `'none'` - No snapshots are updated.
|
||||
* `'missing'` - Missing snapshots are created, for example when authoring a new test and running it for the first time. This is the default.
|
||||
|
||||
Learn more about [snapshots](../test-snapshots.md).
|
||||
|
||||
|
|
@ -598,15 +589,6 @@ export default defineConfig({
|
|||
});
|
||||
```
|
||||
|
||||
## property: TestConfig.updateSourceMethod
|
||||
* since: v1.50
|
||||
- type: ?<[UpdateSourceMethod]<"overwrite"|"3way"|"patch">>
|
||||
|
||||
Defines how to update snapshots in the source code.
|
||||
* `'patch'` - Create a unified diff file that can be used to update the source code later. This is the default.
|
||||
* `'3way'` - Generate merge conflict markers in source code. This allows user to manually pick relevant changes, as if they are resolving a merge conflict in the IDE.
|
||||
* `'overwrite'` - Overwrite the source code with the new snapshot values.
|
||||
|
||||
## property: TestConfig.use
|
||||
* since: v1.10
|
||||
- type: ?<[TestOptions]>
|
||||
|
|
@ -637,9 +619,6 @@ export default defineConfig({
|
|||
- `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"`.
|
||||
- `timeout` ?<[int]> How long to wait for the process to start up and be available in milliseconds. Defaults to 60000.
|
||||
- `gracefulShutdown` ?<[Object]> How to shut down the process. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ signal: 'SIGTERM', timeout: 500 }`, the process group is sent a `SIGTERM` signal, followed by `SIGKILL` if it doesn't exit within 500ms. You can also use `SIGINT` as the signal instead. A `0` timeout means no `SIGKILL` will be sent. Windows doesn't support `SIGTERM` and `SIGINT` signals, so this option is ignored on Windows. Note that shutting down a Docker container requires `SIGTERM`.
|
||||
- `signal` <["SIGINT"|"SIGTERM"]>
|
||||
- `timeout` <[int]>
|
||||
- `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.
|
||||
|
|
@ -663,7 +642,7 @@ import { defineConfig } from '@playwright/test';
|
|||
export default defineConfig({
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
url: 'http://localhost:3000',
|
||||
url: 'http://127.0.0.1:3000',
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
|
|
@ -692,19 +671,19 @@ export default defineConfig({
|
|||
webServer: [
|
||||
{
|
||||
command: 'npm run start',
|
||||
url: 'http://localhost:3000',
|
||||
url: 'http://127.0.0.1:3000',
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
{
|
||||
command: 'npm run backend',
|
||||
url: 'http://localhost:3333',
|
||||
url: 'http://127.0.0.1:3333',
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
}
|
||||
],
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000',
|
||||
baseURL: 'http://127.0.0.1:3000',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
|
|
|||
|
|
@ -392,11 +392,8 @@ export default defineConfig({
|
|||
});
|
||||
```
|
||||
|
||||
## property: TestOptions.locale
|
||||
## property: TestOptions.locale = %%-context-option-locale-%%
|
||||
* since: v1.10
|
||||
- type: <[string]>
|
||||
|
||||
Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value, `Accept-Language` request header value as well as number and date formatting rules. Defaults to `en-US`. Learn more about emulation in our [emulation guide](../emulation.md#locale--timezone).
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
@ -507,27 +504,6 @@ export default defineConfig({
|
|||
|
||||
Learn more about [automatic screenshots](../test-use-options.md#recording-options).
|
||||
|
||||
## property: TestOptions.pageSnapshot
|
||||
* since: v1.51
|
||||
- type: <[PageSnapshotMode]<"off"|"on"|"only-on-failure">>
|
||||
|
||||
Whether to automatically capture a ARIA snapshot of the page after each test. Defaults to `'only-on-failure'`.
|
||||
* `'off'`: Do not capture page snapshots.
|
||||
* `'on'`: Capture page snapshot after each test.
|
||||
* `'only-on-failure'`: Capture page snapshot after each test failure.
|
||||
|
||||
**Usage**
|
||||
|
||||
```js title="playwright.config.ts"
|
||||
import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
use: {
|
||||
pageSnapshot: 'on',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## property: TestOptions.storageState = %%-js-python-context-option-storage-state-%%
|
||||
* since: v1.10
|
||||
|
||||
|
|
|
|||
|
|
@ -98,9 +98,6 @@ export default defineConfig({
|
|||
- `caret` ?<[ScreenshotCaret]<"hide"|"initial">> See [`option: Page.screenshot.caret`] in [`method: Page.screenshot`]. Defaults to `"hide"`.
|
||||
- `scale` ?<[ScreenshotScale]<"css"|"device">> See [`option: Page.screenshot.scale`] in [`method: Page.screenshot`]. Defaults to `"css"`.
|
||||
- `stylePath` ?<[string]|[Array]<[string]>> See [`option: Page.screenshot.style`] in [`method: Page.screenshot`].
|
||||
- `pathTemplate` ?<[string]> A template controlling location of the screenshots. See [`property: TestProject.snapshotPathTemplate`] for details.
|
||||
- `toMatchAriaSnapshot` ?<[Object]> Configuration for the [`method: LocatorAssertions.toMatchAriaSnapshot#2`] method.
|
||||
- `pathTemplate` ?<[string]> A template controlling location of the aria snapshots. See [`property: TestProject.snapshotPathTemplate`] for details.
|
||||
- `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.
|
||||
|
|
@ -183,10 +180,6 @@ Metadata that will be put directly to the test report serialized as JSON.
|
|||
|
||||
Project name is visible in the report and during test execution.
|
||||
|
||||
:::warning
|
||||
Playwright executes the configuration file multiple times. Do not dynamically produce non-stable values in your configuration.
|
||||
:::
|
||||
|
||||
## property: TestProject.snapshotDir
|
||||
* since: v1.10
|
||||
- type: ?<[string]>
|
||||
|
|
|
|||
|
|
@ -1,103 +0,0 @@
|
|||
# class: TestStepInfo
|
||||
* since: v1.51
|
||||
* langs: js
|
||||
|
||||
`TestStepInfo` contains information about currently running test step. It is passed as an argument to the step function. `TestStepInfo` provides utilities to control test step execution.
|
||||
|
||||
```js
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('basic test', async ({ page, browserName }, TestStepInfo) => {
|
||||
await test.step('check some behavior', async step => {
|
||||
await step.skip(browserName === 'webkit', 'The feature is not available in WebKit');
|
||||
// ... rest of the step code
|
||||
await page.check('input');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## async method: TestStepInfo.attach
|
||||
* since: v1.51
|
||||
|
||||
Attach a value or a file from disk to the current test step. Some reporters show test step attachments. Either [`option: path`] or [`option: body`] must be specified, but not both. Calling this method will attribute the attachment to the step, as opposed to [`method: TestInfo.attach`] which stores all attachments at the test level.
|
||||
|
||||
For example, you can attach a screenshot to the test step:
|
||||
|
||||
```js
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('basic test', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev');
|
||||
await test.step('check page rendering', async step => {
|
||||
const screenshot = await page.screenshot();
|
||||
await step.attach('screenshot', { body: screenshot, contentType: 'image/png' });
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Or you can attach files returned by your APIs:
|
||||
|
||||
```js
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { download } from './my-custom-helpers';
|
||||
|
||||
test('basic test', async ({}) => {
|
||||
await test.step('check download behavior', async step => {
|
||||
const tmpPath = await download('a');
|
||||
await step.attach('downloaded', { path: tmpPath });
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
:::note
|
||||
[`method: TestStepInfo.attach`] automatically takes care of copying attached files to a
|
||||
location that is accessible to reporters. You can safely remove the attachment
|
||||
after awaiting the attach call.
|
||||
:::
|
||||
|
||||
### param: TestStepInfo.attach.name
|
||||
* since: v1.51
|
||||
- `name` <[string]>
|
||||
|
||||
Attachment name. The name will also be sanitized and used as the prefix of file name
|
||||
when saving to disk.
|
||||
|
||||
### option: TestStepInfo.attach.body
|
||||
* since: v1.51
|
||||
- `body` <[string]|[Buffer]>
|
||||
|
||||
Attachment body. Mutually exclusive with [`option: path`].
|
||||
|
||||
### option: TestStepInfo.attach.contentType
|
||||
* since: v1.51
|
||||
- `contentType` <[string]>
|
||||
|
||||
Content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`. If omitted, content type is inferred based on the [`option: path`], or defaults to `text/plain` for [string] attachments and `application/octet-stream` for [Buffer] attachments.
|
||||
|
||||
### option: TestStepInfo.attach.path
|
||||
* since: v1.51
|
||||
- `path` <[string]>
|
||||
|
||||
Path on the filesystem to the attached file. Mutually exclusive with [`option: body`].
|
||||
|
||||
## method: TestStepInfo.skip#1
|
||||
* since: v1.51
|
||||
|
||||
Unconditionally skip the currently running step. Test step is immediately aborted. This is similar to [`method: Test.step.skip`].
|
||||
|
||||
## method: TestStepInfo.skip#2
|
||||
* since: v1.51
|
||||
|
||||
Conditionally skips the currently running step with an optional description. This is similar to [`method: Test.step.skip`].
|
||||
|
||||
### param: TestStepInfo.skip#2.condition
|
||||
* since: v1.51
|
||||
- `condition` <[boolean]>
|
||||
|
||||
A skip condition. Test step is skipped when the condition is `true`.
|
||||
|
||||
### param: TestStepInfo.skip#2.description
|
||||
* since: v1.51
|
||||
- `description` ?<[string]>
|
||||
|
||||
Optional description that will be reflected in a test report.
|
||||
|
|
@ -81,7 +81,6 @@ expect.set_options(timeout=10_000)
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -128,24 +127,6 @@ public class UnitTest1 : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
UnitTest1()
|
||||
{
|
||||
SetDefaultExpectTimeout(10_000);
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
|||
|
|
@ -76,41 +76,33 @@ Here are the most common options available in the command line.
|
|||
|
||||
Complete set of Playwright Test options is available in the [configuration file](./test-use-options.md). Following options can be passed to a command line and take priority over the configuration file:
|
||||
|
||||
<!-- // Note: packages/playwright/src/program.ts is the source of truth. -->
|
||||
|
||||
| Option | Description |
|
||||
| :- | :- |
|
||||
| Non-option arguments | Each argument is treated as a regular expression matched against the full test file path. Only tests from 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. |
|
||||
| `-c <file>` or `--config <file>` | Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}". 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. |
|
||||
| `--fail-on-flaky-tests` | Fail if any test is flagged as flaky (default: false). |
|
||||
| `--forbid-only` | Fail if `test.only` is called (default: false). Useful on CI. |
|
||||
| `--fully-parallel` | Run all tests in parallel (default: false). |
|
||||
| `--global-timeout <timeout>` | Maximum time this test suite can run in milliseconds (default: unlimited). |
|
||||
| `-g <grep>` or `--grep <grep>` | Only run tests matching this regular expression (default: ".*"). |
|
||||
| `-gv <grep>` or `--grep-invert <grep>` | Only run tests that do not match this regular expression. |
|
||||
| `--headed` | Run tests in headed browsers (default: headless). |
|
||||
| `--ignore-snapshots` | Ignore screenshot and snapshot expectations. |
|
||||
| `--last-failed` | Only re-run the failures. |
|
||||
| `--list` | Collect all the tests and report them, but do not run. |
|
||||
| `--max-failures <N>` or `-x` | Stop after the first `N` failures. Passing `-x` stops after the first failure. |
|
||||
| `--no-deps` | Do not run project dependencies. |
|
||||
| `--output <dir>` | Folder for output artifacts (default: "test-results"). |
|
||||
| `--only-changed [ref]` | Only run test files that have been changed between 'HEAD' and 'ref'. Defaults to running all uncommitted changes. Only supports Git. |
|
||||
| `--pass-with-no-tests` | Makes test run succeed even if no tests were found. |
|
||||
| `--project <project-name...>` | Only run tests from the specified list of projects, supports '*' wildcard (default: run all projects). |
|
||||
| `--quiet` | Suppress stdio. |
|
||||
| `--repeat-each <N>` | Run each test `N` times (default: 1). |
|
||||
| `--reporter <reporter>` | Reporter to use, comma-separated, can be "dot", "line", "list", or others (default: "list"). You can also pass a path to a custom reporter file. |
|
||||
| `--retries <retries>` | Maximum retry count for flaky tests, zero for no retries (default: no retries). |
|
||||
| `--shard <shard>` | Shard tests and execute only the selected shard, specified in the form "current/all", 1-based, e.g., "3/5". |
|
||||
| `--timeout <timeout>` | Specify test timeout threshold in milliseconds, zero for unlimited (default: 30 seconds). |
|
||||
| `--trace <mode>` | Force tracing mode, can be "on", "off", "on-first-retry", "on-all-retries", "retain-on-failure", "retain-on-first-failure". |
|
||||
| `--tsconfig <path>` | Path to a single tsconfig applicable to all imported files (default: look up tsconfig for each imported file separately). |
|
||||
| `--ui` | Run tests in interactive UI mode. |
|
||||
| `--ui-host <host>` | Host to serve UI on; specifying this option opens UI in a browser tab. |
|
||||
| `--ui-port <port>` | Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab. |
|
||||
| `-u` or `--update-snapshots [mode]` | Update snapshots with actual results. Possible values are "all", "changed", "missing", and "none". Running tests without the flag defaults to "missing"; running tests with the flag but without a value defaults to "changed". |
|
||||
| `--update-source-method [mode]` | Update snapshots with actual results. Possible values are "patch" (default), "3way" and "overwrite". "Patch" creates a unified diff file that can be used to update the source code later. "3way" generates merge conflict markers in source code. "Overwrite" overwrites the source code with the new snapshot values.|
|
||||
| `-j <workers>` or `--workers <workers>` | Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%). |
|
||||
| `-x` | Stop after the first failure. |
|
||||
| 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. |
|
||||
| `-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.|
|
||||
| `--fail-on-flaky-tests` | Fails test runs that contain flaky tests. By default flaky tests count as successes. |
|
||||
| `--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 project name, test file name, `test.describe` titles if any, test title and all test tags, separated by spaces, e.g. `chromium my-test.spec.ts my-suite my-test @smoke`. The filter does not apply to the tests from dependency 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 dependency projects, i.e. Playwright will still run all tests from [project dependencies](./test-projects.md#dependencies).|
|
||||
| `--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. |
|
||||
| `--output <dir>` | Directory for artifacts produced by tests, defaults to `test-results`. |
|
||||
| `--only-changed [ref]` | Only run test files that have been changed between working tree and "ref". Defaults to running all uncommitted changes with ref=HEAD. Only supports Git. |
|
||||
| `--pass-with-no-tests` | Allows the test suite to pass when no files are found. |
|
||||
| `--project <name>` | Only run tests from the specified [projects](./test-projects.md), supports '*' wildcard. Defaults to running all projects defined in the configuration file.|
|
||||
| `--quiet` | Whether to suppress stdout and stderr from the tests. |
|
||||
| `--repeat-each <N>` | Run each test `N` times, defaults to one. |
|
||||
| `--reporter <reporter>` | Choose a reporter: minimalist `dot`, concise `line` or detailed `list`. See [reporters](./test-reporters.md) for more information. You can also pass a path to a [custom reporter](./test-reporters.md#custom-reporters) file. |
|
||||
| `--retries <number>` | The maximum number of [retries](./test-retries.md#retries) for flaky tests, defaults to zero (no retries). |
|
||||
| `--shard <shard>` | [Shard](./test-parallel.md#shard-tests-between-multiple-machines) tests and execute only selected shard, specified in the form `current/all`, 1-based, for example `3/5`.|
|
||||
| `--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` |
|
||||
| `--tsconfig <path>` | Path to a single tsconfig applicable to all imported files. See [tsconfig resolution](./test-typescript.md#tsconfig-resolution) for more details. |
|
||||
| `--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). |
|
||||
|
|
|
|||
|
|
@ -853,14 +853,6 @@ export default defineConfig({
|
|||
});
|
||||
```
|
||||
|
||||
### How do I use CSS imports?
|
||||
|
||||
If you have a component that imports CSS, Vite will handle it automatically. You can also use CSS pre-processors such as Sass, Less, or Stylus, and Vite will handle them as well without any additional configuration. However, corresponding CSS pre-processor needs to be installed.
|
||||
|
||||
Vite has a hard requirement that all CSS Modules are named `*.module.[css extension]`. If you have a custom build config for your project normally and have imports of the form `import styles from 'styles.css'` you must rename your files to properly indicate they are to be treated as modules. You could also write a Vite plugin to handle this for you.
|
||||
|
||||
Check [Vite documentation](https://vite.dev/guide/features#css) for more details.
|
||||
|
||||
### How can I test components that uses Pinia?
|
||||
|
||||
Pinia needs to be initialized in `playwright/index.{js,ts,jsx,tsx}`. If you do this inside a `beforeMount` hook, the `initialState` can be overwritten on a per-test basis:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default defineConfig({
|
|||
|
||||
use: {
|
||||
// Base URL to use in actions like `await page.goto('/')`.
|
||||
baseURL: 'http://localhost:3000',
|
||||
baseURL: 'http://127.0.0.1:3000',
|
||||
|
||||
// Collect trace when retrying the failed test.
|
||||
trace: 'on-first-retry',
|
||||
|
|
@ -50,7 +50,7 @@ export default defineConfig({
|
|||
// Run your local dev server before starting the tests.
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
url: 'http://localhost:3000',
|
||||
url: 'http://127.0.0.1:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@ test.describe('todo tests', () => {
|
|||
### With fixtures
|
||||
|
||||
Fixtures have a number of advantages over before/after hooks:
|
||||
- Fixtures **encapsulate** setup and teardown in the same place so it is easier to write. So if you have an after hook that tears down what was created in a before hook, consider turning them into a fixture.
|
||||
- Fixtures are **reusable** between test files - you can define them once and use in all your tests. That's how Playwright's built-in `page` fixture works. So if you have a helper function that is used in multiple tests, consider turning it into a fixture.
|
||||
- Fixtures **encapsulate** setup and teardown in the same place so it is easier to write.
|
||||
- Fixtures are **reusable** between test files - you can define them once and use in all your tests. That's how Playwright's built-in `page` fixture works.
|
||||
- Fixtures are **on-demand** - you can define as many fixtures as you'd like, and Playwright Test will setup only the ones needed by your test and nothing else.
|
||||
- Fixtures are **composable** - they can depend on each other to provide complex behaviors.
|
||||
- Fixtures are **flexible**. Tests can use any combinations of the fixtures to tailor precise environment they need, without affecting other tests.
|
||||
|
|
@ -407,8 +407,8 @@ Automatic fixtures are set up for each test/worker, even when the test does not
|
|||
Here is an example fixture that automatically attaches debug logs when the test fails, so we can later review the logs in the reporter. Note how it uses [TestInfo] object that is available in each test/fixture to retrieve metadata about the test being run.
|
||||
|
||||
```js title="my-test.ts"
|
||||
import debug from 'debug';
|
||||
import fs from 'fs';
|
||||
import * as debug from 'debug';
|
||||
import * as fs from 'fs';
|
||||
import { test as base } from '@playwright/test';
|
||||
|
||||
export const test = base.extend<{ saveLogs: void }>({
|
||||
|
|
@ -695,7 +695,7 @@ test('passes', async ({ database, page, a11y }) => {
|
|||
|
||||
## Box fixtures
|
||||
|
||||
Usually, custom fixtures are reported as separate steps in the UI mode, Trace Viewer and various test reports. They also appear in error messages from the test runner. For frequently-used fixtures, this can mean lots of noise. You can stop the fixtures steps from being shown in the UI by "boxing" it.
|
||||
Usually, custom fixtures are reported as separate steps in in the UI mode, Trace Viewer and various test reports. They also appear in error messages from the test runner. For frequently-used fixtures, this can mean lots of noise. You can stop the fixtures steps from being shown in the UI by "boxing" it.
|
||||
|
||||
```js
|
||||
import { test as base } from '@playwright/test';
|
||||
|
|
|
|||
|
|
@ -129,13 +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
|
||||
Beware of `globalSetup` and `globalTeardown` caveats:
|
||||
|
||||
- These methods will not produce traces or artifacts unless explictly enabled, as described in [Capturing trace of failures during global setup](#capturing-trace-of-failures-during-global-setup).
|
||||
- Options sush as `headless` or `testIdAttribute` specified in the config file are not applied,
|
||||
- An uncaught exception thrown in `globalSetup` will prevent Playwright from running tests, and no test results will appear in reporters.
|
||||
|
||||
Consider using [project dependencies](#option-1-project-dependencies) to produce traces, artifacts, respect config options and get test results in reporters even in case of a setup failure.
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -50,24 +50,6 @@ Start time of this particular test step.
|
|||
|
||||
List of steps inside this step.
|
||||
|
||||
## property: TestStep.annotations
|
||||
* since: v1.51
|
||||
- type: <[Array]<[Object]>>
|
||||
- `type` <[string]> Annotation type, for example `'skip'`.
|
||||
- `description` ?<[string]> Optional description.
|
||||
|
||||
The list of annotations applicable to the current test step.
|
||||
|
||||
## property: TestStep.attachments
|
||||
* since: v1.50
|
||||
- type: <[Array]<[Object]>>
|
||||
- `name` <[string]> Attachment name.
|
||||
- `contentType` <[string]> Content type of this attachment to properly present in the report, for example `'application/json'` or `'image/png'`.
|
||||
- `path` ?<[string]> Optional path on the filesystem to the attached file.
|
||||
- `body` ?<[Buffer]> Optional attachment body used instead of a file.
|
||||
|
||||
The list of files or buffers attached in the step execution through [`method: TestInfo.attach`].
|
||||
|
||||
## property: TestStep.title
|
||||
* since: v1.10
|
||||
- type: <[string]>
|
||||
|
|
|
|||
|
|
@ -5,64 +5,19 @@ title: "Test Runners"
|
|||
|
||||
## Introduction
|
||||
|
||||
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience the easiest way of getting started is by using the base classes we provide for MSTest, NUnit, or xUnit. These classes support running tests on multiple browser engines, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box.
|
||||
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience the easiest way of getting started is by using the base classes we provide for [MSTest](#mstest) and [NUnit](#nunit). These classes support running tests on multiple browser engines, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box.
|
||||
|
||||
Playwright and Browser instances will 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.
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
## MSTest
|
||||
|
||||
Playwright provides base classes to write tests with MSTest via the [`Microsoft.Playwright.MSTest`](https://www.nuget.org/packages/Microsoft.Playwright.MSTest) package.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
Playwright provides base classes to write tests with xUnit via the [`Microsoft.Playwright.Xunit`](https://www.nuget.org/packages/Microsoft.Playwright.Xunit) package.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Check out the [installation guide](./intro.md) to get started.
|
||||
|
||||
## Running tests in Parallel
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
By default NUnit will run all test files in parallel, while running tests inside each file sequentially (`ParallelScope.Self`). It will create as many processes as there are cores on the host system. You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter.
|
||||
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
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
### Running MSTest tests in Parallel
|
||||
|
||||
By default MSTest will run all classes in parallel, while running tests inside each class sequentially (`ExecutionScope.ClassLevel`). It will create as many processes as there are cores on the host system. You can adjust this behavior by using the following CLI parameter or using a `.runsettings` file, see below.
|
||||
Running tests in parallel at the method level (`ExecutionScope.MethodLevel`) is not supported.
|
||||
|
|
@ -71,73 +26,7 @@ Running tests in parallel at the method level (`ExecutionScope.MethodLevel`) is
|
|||
dotnet test --settings:.runsettings -- MSTest.Parallelize.Workers=4
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
By default xUnit will run all classes in parallel, while running tests inside each class sequentially.
|
||||
It will create by default as many processes as there are cores on the system. You can adjust this behavior by using the following CLI parameter or using a `.runsettings` file, see below.
|
||||
|
||||
```bash
|
||||
dotnet test -- xUnit.MaxParallelThreads=5
|
||||
```
|
||||
|
||||
:::note
|
||||
We recommend xUnit 2.8+ which uses the [`conservative` parallelism algorithm](https://xunit.net/docs/running-tests-in-parallel.html#algorithms) by default.
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Customizing [BrowserContext] options
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
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:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]
|
||||
public class MyTest : PageTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestWithCustomContextOptions()
|
||||
{
|
||||
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||
await Page.GotoAsync("/login");
|
||||
}
|
||||
|
||||
public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return new BrowserNewContextOptions()
|
||||
{
|
||||
ColorScheme = ColorScheme.Light,
|
||||
ViewportSize = new()
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080
|
||||
},
|
||||
BaseURL = "https://github.com",
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
### 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:
|
||||
|
||||
|
|
@ -176,46 +65,7 @@ public class ExampleTest : PageTest
|
|||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.Xunit.PageTest` or `Microsoft.Playwright.Xunit.ContextTest`. See the following example:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1 : PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task TestWithCustomContextOptions()
|
||||
{
|
||||
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||
await Page.GotoAsync("/login");
|
||||
}
|
||||
public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return new BrowserNewContextOptions()
|
||||
{
|
||||
ColorScheme = ColorScheme.Light,
|
||||
ViewportSize = new()
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080
|
||||
},
|
||||
BaseURL = "https://github.com",
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Customizing [Browser]/launch options
|
||||
### Customizing [Browser]/launch options
|
||||
|
||||
[Browser]/launch options can be overridden either using a run settings file or by setting the run settings options directly via the
|
||||
CLI. See the following example:
|
||||
|
|
@ -237,56 +87,14 @@ CLI. See the following example:
|
|||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
|
||||
```
|
||||
|
||||
## Using Verbose API Logs
|
||||
### Using Verbose API Logs
|
||||
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. Within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In MSTest, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
|
||||
## Using the .runsettings file
|
||||
### Using the .runsettings file
|
||||
|
||||
When running tests from Visual Studio, you can take advantage of the `.runsettings` file. The following shows a reference of the supported values.
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
For example, to specify the number of workers you can use `NUnit.NumberOfTestWorkers` or to enable `DEBUG` logs `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<!-- NUnit adapter -->
|
||||
<NUnit>
|
||||
<NumberOfTestWorkers>24</NumberOfTestWorkers>
|
||||
</NUnit>
|
||||
<!-- General run configuration -->
|
||||
<RunConfiguration>
|
||||
<EnvironmentVariables>
|
||||
<!-- For debugging selectors, it's recommend to set the following environment variable -->
|
||||
<DEBUG>pw:api</DEBUG>
|
||||
</EnvironmentVariables>
|
||||
</RunConfiguration>
|
||||
<!-- Playwright -->
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<ExpectTimeout>5000</ExpectTimeout>
|
||||
<LaunchOptions>
|
||||
<Headless>false</Headless>
|
||||
<Channel>msedge</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
For example, to specify the number of workers, you can use `MSTest.Parallelize.Workers`. You can also enable `DEBUG` logs using `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
|
|
@ -317,18 +125,109 @@ For example, to specify the number of workers, you can use `MSTest.Parallelize.W
|
|||
</RunSettings>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
### Base MSTest classes for Playwright
|
||||
|
||||
For example, to specify the number of workers, you can use `xUnit.MaxParallelThreads`. You can also enable `DEBUG` logs using `RunConfiguration.EnvironmentVariables`.
|
||||
There are a few base classes available to you in `Microsoft.Playwright.MSTest` namespace:
|
||||
|
||||
|Test |Description|
|
||||
|--------------|-----------|
|
||||
|PageTest |Each test gets a fresh copy of a web [Page] created in its own unique [BrowserContext]. Extending this class is the simplest way of writing a fully-functional Playwright test.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|ContextTest |Each test will get a fresh copy of a [BrowserContext]. You can create as many pages in this context as you'd like. Using this test is the easiest way to test multi-page scenarios where you need more than one tab.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|BrowserTest |Each test will get a browser and can create as many contexts as it likes. Each test is responsible for cleaning up all the contexts it created.|
|
||||
|PlaywrightTest|This gives each test a Playwright object so that the test could start and stop as many browsers as it likes.|
|
||||
|
||||
## NUnit
|
||||
|
||||
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
|
||||
|
||||
Check out the [installation guide](./intro.md) to get started.
|
||||
|
||||
### Running NUnit tests in Parallel
|
||||
|
||||
By default NUnit will run all test files in parallel, while running tests inside each file sequentially (`ParallelScope.Self`). It will create as many processes as there are cores on the host system. You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter.
|
||||
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:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]
|
||||
public class MyTest : PageTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestWithCustomContextOptions()
|
||||
{
|
||||
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||
await Page.GotoAsync("/login");
|
||||
}
|
||||
|
||||
public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return new BrowserNewContextOptions()
|
||||
{
|
||||
ColorScheme = ColorScheme.Light,
|
||||
ViewportSize = new()
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080
|
||||
},
|
||||
BaseURL = "https://github.com",
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Customizing [Browser]/launch options
|
||||
|
||||
[Browser]/launch options can be overridden either using a run settings file or by setting the run settings options directly via the
|
||||
CLI. See the following example:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<!-- See https://xunit.net/docs/runsettings -->
|
||||
<xUnit>
|
||||
<MaxParallelThreads>1</MaxParallelThreads>
|
||||
</xUnit>
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<LaunchOptions>
|
||||
<Headless>false</Headless>
|
||||
<Channel>msedge</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
```bash
|
||||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
|
||||
```
|
||||
|
||||
### Using Verbose API Logs
|
||||
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In NUnit, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
|
||||
### Using the .runsettings file
|
||||
|
||||
When running tests from Visual Studio, you can take advantage of the `.runsettings` file. The following shows a reference of the supported values.
|
||||
|
||||
For example, to specify the amount of workers you can use `NUnit.NumberOfTestWorkers` or to enable `DEBUG` logs `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<!-- NUnit adapter -->
|
||||
<NUnit>
|
||||
<NumberOfTestWorkers>24</NumberOfTestWorkers>
|
||||
</NUnit>
|
||||
<!-- General run configuration -->
|
||||
<RunConfiguration>
|
||||
<EnvironmentVariables>
|
||||
|
|
@ -347,40 +246,19 @@ For example, to specify the number of workers, you can use `xUnit.MaxParallelThr
|
|||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Base classes for Playwright
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
### Base NUnit classes for Playwright
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.NUnit` namespace:
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.MSTest` namespace:
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.Xunit` namespace:
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|Test |Description|
|
||||
|--------------|-----------|
|
||||
|PageTest |Each test gets a fresh copy of a web [Page] created in its own unique [BrowserContext]. Extending this class is the simplest way of writing a fully-functional Playwright test.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|ContextTest |Each test will get a fresh copy of a [BrowserContext]. You can create as many pages in this context as you'd like. Using this test is the easiest way to test multi-page scenarios where you need more than one tab.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|BrowserTest |Each test will get a browser and can create as many contexts as it likes. Each test is responsible for cleaning up all the contexts it created.|
|
||||
|PlaywrightTest|This gives each test a Playwright object so that the test could start and stop as many browsers as it likes.|
|
||||
|
||||
## xUnit support
|
||||
|
||||
While using xUnit is also supported, we do not support running parallel tests. This is a well known problem/design limitation
|
||||
outlined by the maintainers across [several](https://github.com/xunit/xunit/issues/2003) [issues](https://github.com/xunit/xunit/issues/2111#issuecomment-650004247).
|
||||
|
|
|
|||
|
|
@ -259,18 +259,3 @@ def test_bing_is_working(page):
|
|||
## Deploy to CI
|
||||
|
||||
See the [guides for CI providers](./ci.md) to deploy your tests to CI/CD.
|
||||
|
||||
## Async Fixtures
|
||||
|
||||
If you want to use async fixtures, you can use the [`pytest-playwright-asyncio`](https://pypi.org/project/pytest-playwright-asyncio/) plugin.
|
||||
Make sure to use `pytest-asyncio>=0.24.0` and make your tests use of [`loop_scope=session`](https://pytest-asyncio.readthedocs.io/en/latest/how-to-guides/run_session_tests_in_same_loop.html).
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from playwright.async_api import Page
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_foo(page: Page):
|
||||
await page.goto("https://github.com")
|
||||
# ...
|
||||
```
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ When `fullyParallel: true` is enabled, Playwright Test runs individual tests in
|
|||
|
||||
**Sharding without fullyParallel**
|
||||
|
||||
Without the fullyParallel setting, Playwright Test defaults to file-level granularity, meaning entire test files are assigned to shards (note that the same file may be assigned to different shards across different projects). In this case, the number of tests per file can greatly influence shard distribution. If your test files are not evenly sized (i.e., some files contain many more tests than others), certain shards may end up running significantly more tests, while others may run fewer or even none.
|
||||
Without the fullyParallel setting, Playwright Test defaults to file-level granularity, meaning entire test files are assigned to shards. In this case, the number of tests per file can greatly influence shard distribution. If your test files are not evenly sized (i.e., some files contain many more tests than others), certain shards may end up running significantly more tests, while others may run fewer or even none.
|
||||
|
||||
**Key Takeaways:**
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ The snapshot name `example-test-1-chromium-darwin.png` consists of a few parts:
|
|||
|
||||
- `chromium-darwin` - the browser name and the platform. Screenshots differ between browsers and platforms due to different rendering, fonts and more, so you will need different snapshots for them. If you use multiple projects in your [configuration file](./test-configuration.md), project name will be used instead of `chromium`.
|
||||
|
||||
The snapshot name and path can be configured with [`property: TestConfig.snapshotPathTemplate`] in the playwright config.
|
||||
The snapshot name and path can be configured with [`snapshotPathTemplate`](./api/class-testproject#test-project-snapshot-path-template) in the playwright config.
|
||||
|
||||
## Updating screenshots
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
|
|||
|
||||
## Introduction
|
||||
|
||||
UI Mode lets you explore, run, and debug tests with a time travel experience complete with a watch mode. All test files are displayed in the testing sidebar, allowing you to expand each file and describe block to individually run, view, watch, and debug each test. Filter tests by **name**, [**projects**](./test-projects) (set in your `playwright.config` file), **@tag**, or by the execution status of **passed**, **failed**, and **skipped**. See a full trace of your tests and hover back and forward over each action to see what was happening during each step. You can also pop out the DOM snapshot of a given moment into 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"
|
||||
|
|
@ -18,10 +18,9 @@ UI Mode lets you explore, run, and debug tests with a time travel experience com
|
|||
|
||||
To open UI mode, run the following command in your terminal:
|
||||
|
||||
```bash
|
||||
npx playwright test --ui
|
||||
```
|
||||
|
||||
```bash
|
||||
npx playwright test --ui
|
||||
```
|
||||
## Running your tests
|
||||
|
||||
Once you launch UI Mode you will see a list of all your test files. You can run all your tests by clicking the triangle icon in the sidebar. You can also run a single test file, a block of tests or a single test by hovering over the name and clicking on the triangle next to it.
|
||||
|
|
@ -34,12 +33,14 @@ Filter tests by text or `@tag` or by passed, failed or skipped tests. You can al
|
|||
|
||||

|
||||
|
||||
|
||||
## Timeline view
|
||||
|
||||
At the top of the trace you can see a timeline view of your test with different colors to highlight navigation and actions. Hover back and forth to see an image snapshot for each action. Double click on an action to see the time range for that action. You can use the slider in the timeline to increase the actions selected and these will be shown in the Actions tab and all console logs and network logs will be filtered to only show the logs for the actions selected.
|
||||
|
||||

|
||||
|
||||
|
||||
## Actions
|
||||
|
||||
In the Actions tab you can see what locator was used for every action and how long each one took to run. Hover over each action of your test and visually see the change in the DOM snapshot. Go back and forward in time and click an action to inspect and debug. Use the Before and After tabs to visually see what happened before and after the action.
|
||||
|
|
@ -59,7 +60,7 @@ Click on the pick locator button and hover over the DOM snapshot to see the loca
|
|||
|
||||
## Source
|
||||
|
||||
As you hover over each action of your test the line of code for that action is highlighted in the source panel. The button "Open in VSCode" is at the top-right of this section. Upon clicking the button, it will open your test in VS Code right at the line of code that you clicked on.
|
||||
As you hover over each action of your test the line of code for that action is highlighted in the source panel.
|
||||
|
||||

|
||||
|
||||
|
|
@ -107,7 +108,7 @@ Next to the Actions tab you will find the Metadata tab which will show you more
|
|||
|
||||
## Watch mode
|
||||
|
||||
Next to the name of each test in the sidebar you will find an eye icon. Clicking on the icon will activate watch mode which will re-run the test when you make changes to it. You can watch a number of tests at the same time be clicking the eye icon next to each one or all tests by clicking the eye icon at the top of the sidebar.
|
||||
Next to the name of each test in the sidebar you will find an eye icon. Clicking on the icon will activate watch mode which will re-run the test when you make changes to it. You can watch a number of tests at the same time be clicking the eye icon next to each one or all tests by clicking the eye icon at the top of the sidebar. If you are using VS Code then you can easily open your test by clicking on the file icon next to the eye icon. This will open your test in VS Code right at the line of code that you clicked on.
|
||||
|
||||

|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { defineConfig } from '@playwright/test';
|
|||
export default defineConfig({
|
||||
use: {
|
||||
// Base URL to use in actions like `await page.goto('/')`.
|
||||
baseURL: 'http://localhost:3000',
|
||||
baseURL: 'http://127.0.0.1:3000',
|
||||
|
||||
// Populates context with given storage state.
|
||||
storageState: 'state.json',
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default defineConfig({
|
|||
// Run your local dev server before starting the tests
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
url: 'http://localhost:3000',
|
||||
url: 'http://127.0.0.1:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
stdout: 'ignore',
|
||||
stderr: 'pipe',
|
||||
|
|
@ -36,8 +36,7 @@ export default defineConfig({
|
|||
| `cwd` | Current working directory of the spawned process, defaults to the directory of the configuration file. |
|
||||
| `stdout` | If `"pipe"`, it will pipe the stdout of the command to the process stdout. If `"ignore"`, it will ignore the stdout of the command. Default to `"ignore"`. |
|
||||
| `stderr` | Whether to pipe the stderr of the command to the process stderr or ignore it. Defaults to `"pipe"`. |
|
||||
| `timeout` | How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. |
|
||||
| `gracefulShutdown` | How to shut down the process. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ signal: 'SIGTERM', timeout: 500 }`, the process group is sent a `SIGTERM` signal, followed by `SIGKILL` if it doesn't exit within 500ms. You can also use `SIGINT` as the signal instead. A `0` timeout means no `SIGKILL` will be sent. Windows doesn't support `SIGTERM` and `SIGINT` signals, so this option is ignored on Windows. Note that shutting down a Docker container requires `SIGTERM`. |
|
||||
| `timeout` | `How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. |
|
||||
|
||||
## Adding a server timeout
|
||||
|
||||
|
|
@ -52,7 +51,7 @@ export default defineConfig({
|
|||
// Run your local dev server before starting the tests
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
url: 'http://localhost:3000',
|
||||
url: 'http://127.0.0.1:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
timeout: 120 * 1000,
|
||||
},
|
||||
|
|
@ -63,7 +62,7 @@ export default defineConfig({
|
|||
|
||||
It is also recommended to specify the `baseURL` in the `use: {}` section of your config, so that tests can use relative urls and you don't have to specify the full URL over and over again.
|
||||
|
||||
When using [`method: Page.goto`], [`method: Page.route`], [`method: Page.waitForURL`], [`method: Page.waitForRequest`], or [`method: Page.waitForResponse`] it takes the base URL in consideration by using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the corresponding URL. For Example, by setting the baseURL to `http://localhost:3000` and navigating to `/login` in your tests, Playwright will run the test using `http://localhost:3000/login`.
|
||||
When using [`method: Page.goto`], [`method: Page.route`], [`method: Page.waitForURL`], [`method: Page.waitForRequest`], or [`method: Page.waitForResponse`] it takes the base URL in consideration by using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the corresponding URL. For Example, by setting the baseURL to `http://127.0.0.1:3000` and navigating to `/login` in your tests, Playwright will run the test using `http://127.0.0.1:3000/login`.
|
||||
|
||||
```js title="playwright.config.ts"
|
||||
import { defineConfig } from '@playwright/test';
|
||||
|
|
@ -74,11 +73,11 @@ export default defineConfig({
|
|||
// Run your local dev server before starting the tests
|
||||
webServer: {
|
||||
command: 'npm run start',
|
||||
url: 'http://localhost:3000',
|
||||
url: 'http://127.0.0.1:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000',
|
||||
baseURL: 'http://127.0.0.1:3000',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
|
@ -89,7 +88,7 @@ Now you can use a relative path when navigating the page:
|
|||
import { test } from '@playwright/test';
|
||||
|
||||
test('test', async ({ page }) => {
|
||||
// This will navigate to http://localhost:3000/login
|
||||
// This will navigate to http://127.0.0.1:3000/login
|
||||
await page.goto('./login');
|
||||
});
|
||||
```
|
||||
|
|
@ -106,19 +105,19 @@ export default defineConfig({
|
|||
webServer: [
|
||||
{
|
||||
command: 'npm run start',
|
||||
url: 'http://localhost:3000',
|
||||
url: 'http://127.0.0.1:3000',
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
{
|
||||
command: 'npm run backend',
|
||||
url: 'http://localhost:3333',
|
||||
url: 'http://127.0.0.1:3333',
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
}
|
||||
],
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000',
|
||||
baseURL: 'http://127.0.0.1:3000',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,138 +0,0 @@
|
|||
---
|
||||
id: touch-events
|
||||
title: "Emulating legacy touch events"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Web applications that handle [touch events](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events) to respond to gestures like swipe, pinch, and tap can be tested by manually dispatching [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)s to the page. The examples below demonstrate how to use [`method: Locator.dispatchEvent`] and pass [Touch](https://developer.mozilla.org/en-US/docs/Web/API/Touch) points as arguments.
|
||||
|
||||
### Emulating pan gesture
|
||||
|
||||
In the example below, we emulate pan gesture that is expected to move the map. The app under test only uses `clientX/clientY` coordinates of the touch point, so we initialize just that. In a more complex scenario you may need to also set `pageX/pageY/screenX/screenY`, if your app needs them.
|
||||
|
||||
```js
|
||||
import { test, expect, devices, type Locator } from '@playwright/test';
|
||||
|
||||
test.use({ ...devices['Pixel 7'] });
|
||||
|
||||
async function pan(locator: Locator, deltaX?: number, deltaY?: number, steps?: number) {
|
||||
const { centerX, centerY } = await locator.evaluate((target: HTMLElement) => {
|
||||
const bounds = target.getBoundingClientRect();
|
||||
const centerX = bounds.left + bounds.width / 2;
|
||||
const centerY = bounds.top + bounds.height / 2;
|
||||
return { centerX, centerY };
|
||||
});
|
||||
|
||||
// Providing only clientX and clientY as the app only cares about those.
|
||||
const touches = [{
|
||||
identifier: 0,
|
||||
clientX: centerX,
|
||||
clientY: centerY,
|
||||
}];
|
||||
await locator.dispatchEvent('touchstart',
|
||||
{ touches, changedTouches: touches, targetTouches: touches });
|
||||
|
||||
steps = steps ?? 5;
|
||||
deltaX = deltaX ?? 0;
|
||||
deltaY = deltaY ?? 0;
|
||||
for (let i = 1; i <= steps; i++) {
|
||||
const touches = [{
|
||||
identifier: 0,
|
||||
clientX: centerX + deltaX * i / steps,
|
||||
clientY: centerY + deltaY * i / steps,
|
||||
}];
|
||||
await locator.dispatchEvent('touchmove',
|
||||
{ touches, changedTouches: touches, targetTouches: touches });
|
||||
}
|
||||
|
||||
await locator.dispatchEvent('touchend');
|
||||
}
|
||||
|
||||
test(`pan gesture to move the map`, async ({ page }) => {
|
||||
await page.goto('https://www.google.com/maps/place/@37.4117722,-122.0713234,15z',
|
||||
{ waitUntil: 'commit' });
|
||||
await page.getByRole('button', { name: 'Keep using web' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Keep using web' })).not.toBeVisible();
|
||||
// Get the map element.
|
||||
const met = page.locator('[data-test-id="met"]');
|
||||
for (let i = 0; i < 5; i++)
|
||||
await pan(met, 200, 100);
|
||||
// Ensure the map has been moved.
|
||||
await expect(met).toHaveScreenshot();
|
||||
});
|
||||
```
|
||||
|
||||
### Emulating pinch gesture
|
||||
|
||||
In the example below, we emulate pinch gesture, i.e. two touch points moving closer to each other. It is expected to zoom out the map. The app under test only uses `clientX/clientY` coordinates of touch points, so we initialize just that. In a more complex scenario you may need to also set `pageX/pageY/screenX/screenY`, if your app needs them.
|
||||
|
||||
```js
|
||||
import { test, expect, devices, type Locator } from '@playwright/test';
|
||||
|
||||
test.use({ ...devices['Pixel 7'] });
|
||||
|
||||
async function pinch(locator: Locator,
|
||||
arg: { deltaX?: number, deltaY?: number, steps?: number, direction?: 'in' | 'out' }) {
|
||||
const { centerX, centerY } = await locator.evaluate((target: HTMLElement) => {
|
||||
const bounds = target.getBoundingClientRect();
|
||||
const centerX = bounds.left + bounds.width / 2;
|
||||
const centerY = bounds.top + bounds.height / 2;
|
||||
return { centerX, centerY };
|
||||
});
|
||||
|
||||
const deltaX = arg.deltaX ?? 50;
|
||||
const steps = arg.steps ?? 5;
|
||||
const stepDeltaX = deltaX / (steps + 1);
|
||||
|
||||
// Two touch points equally distant from the center of the element.
|
||||
const touches = [
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX - (arg.direction === 'in' ? deltaX : stepDeltaX),
|
||||
clientY: centerY,
|
||||
},
|
||||
{
|
||||
identifier: 1,
|
||||
clientX: centerX + (arg.direction === 'in' ? deltaX : stepDeltaX),
|
||||
clientY: centerY,
|
||||
},
|
||||
];
|
||||
await locator.dispatchEvent('touchstart',
|
||||
{ touches, changedTouches: touches, targetTouches: touches });
|
||||
|
||||
// Move the touch points towards or away from each other.
|
||||
for (let i = 1; i <= steps; i++) {
|
||||
const offset = (arg.direction === 'in' ? (deltaX - i * stepDeltaX) : (stepDeltaX * (i + 1)));
|
||||
const touches = [
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX - offset,
|
||||
clientY: centerY,
|
||||
},
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX + offset,
|
||||
clientY: centerY,
|
||||
},
|
||||
];
|
||||
await locator.dispatchEvent('touchmove',
|
||||
{ touches, changedTouches: touches, targetTouches: touches });
|
||||
}
|
||||
|
||||
await locator.dispatchEvent('touchend', { touches: [], changedTouches: [], targetTouches: [] });
|
||||
}
|
||||
|
||||
test(`pinch in gesture to zoom out the map`, async ({ page }) => {
|
||||
await page.goto('https://www.google.com/maps/place/@37.4117722,-122.0713234,15z',
|
||||
{ waitUntil: 'commit' });
|
||||
await page.getByRole('button', { name: 'Keep using web' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Keep using web' })).not.toBeVisible();
|
||||
// Get the map element.
|
||||
const met = page.locator('[data-test-id="met"]');
|
||||
for (let i = 0; i < 5; i++)
|
||||
await pinch(met, { deltaX: 40, direction: 'in' });
|
||||
// Ensure the map has been zoomed out.
|
||||
await expect(met).toHaveScreenshot();
|
||||
});
|
||||
```
|
||||
|
|
@ -22,7 +22,6 @@ Traces can be recorded using the [`property: BrowserContext.tracing`] API as fol
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -113,69 +112,6 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp
|
||||
using System.Reflection;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[WithTestName]
|
||||
public class UnitTest1 : PageTest
|
||||
{
|
||||
public override async Task InitializeAsync()
|
||||
{
|
||||
await base.InitializeAsync().ConfigureAwait(false);
|
||||
await Context.Tracing.StartAsync(new()
|
||||
{
|
||||
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
|
||||
Screenshots = true,
|
||||
Snapshots = true,
|
||||
Sources = true
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
await Context.Tracing.StopAsync(new()
|
||||
{
|
||||
Path = Path.Combine(
|
||||
Environment.CurrentDirectory,
|
||||
"playwright-traces",
|
||||
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
|
||||
)
|
||||
});
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
// ...
|
||||
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
|
||||
}
|
||||
}
|
||||
|
||||
public class WithTestNameAttribute : BeforeAfterTestAttribute
|
||||
{
|
||||
public static string CurrentTestName = string.Empty;
|
||||
public static string CurrentClassName = string.Empty;
|
||||
|
||||
public override void Before(MethodInfo methodInfo)
|
||||
{
|
||||
CurrentTestName = methodInfo.Name;
|
||||
CurrentClassName = methodInfo.DeclaringType!.Name;
|
||||
}
|
||||
|
||||
public override void After(MethodInfo methodInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -198,4 +134,4 @@ Check out our detailed guide on [Trace Viewer](/trace-viewer.md) to learn more a
|
|||
## What's next
|
||||
|
||||
- [Run tests on CI with GitHub Actions](/ci-intro.md)
|
||||
- [Learn more about the MSTest, NUnit, and xUnit base classes](./test-runners.md)
|
||||
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)
|
||||
|
|
|
|||
|
|
@ -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](./trace-viewer.md#tracing-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.
|
||||
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';
|
||||
|
|
@ -45,7 +45,7 @@ npx playwright test --trace on
|
|||
|
||||
## Opening the HTML report
|
||||
|
||||
The HTML report shows you a report of all your tests that have been run and on which browsers as well as how long they took. Tests can be filtered by passed tests, failed, flaky or skipped tests. You can also search for a particular test. Clicking on a test will open the detailed view where you can see more information on your tests such as the errors, the test steps and the trace.
|
||||
The HTML report shows you a report of all your tests that have been ran and on which browsers as well as how long they took. Tests can be filtered by passed tests, failed, flakey or skipped tests. You can also search for a particular test. Clicking on a test will open the detailed view where you can see more information on your tests such as the errors, the test steps and the trace.
|
||||
|
||||
```bash
|
||||
npx playwright show-report
|
||||
|
|
|
|||
|
|
@ -17,67 +17,106 @@ Playwright Trace Viewer is a GUI tool that helps you explore recorded Playwright
|
|||
title="Viewing Playwright Traces"
|
||||
/>
|
||||
|
||||
## Opening Trace Viewer
|
||||
## Trace Viewer features
|
||||
### Actions
|
||||
|
||||
You can open a saved trace using either the Playwright CLI or in the browser at [trace.playwright.dev](https://trace.playwright.dev). Make sure to add the full path to where your `trace.zip` file is located.
|
||||
In the Actions tab you can see what locator was used for every action and how long each one took to run. Hover over each action of your test and visually see the change in the DOM snapshot. Go back and forward in time and click an action to inspect and debug. Use the Before and After tabs to visually see what happened before and after the action.
|
||||
|
||||
```bash js
|
||||
npx playwright show-trace path/to/trace.zip
|
||||
```
|
||||

|
||||
|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace trace.zip"
|
||||
```
|
||||
**Selecting each action reveals:**
|
||||
- action snapshots
|
||||
- action log
|
||||
- source code location
|
||||
|
||||
```bash python
|
||||
playwright show-trace trace.zip
|
||||
```
|
||||
### Screenshots
|
||||
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 show-trace trace.zip
|
||||
```
|
||||
When tracing with the [`option: Tracing.start.screenshots`] option turned on (default), each trace records a screencast and renders it as a film strip. You can hover over the film strip to see a magnified image of for each action and state which helps you easily find the action you want to inspect.
|
||||
|
||||
### Using [trace.playwright.dev](https://trace.playwright.dev)
|
||||
Double click on an action to see the time range for that action. You can use the slider in the timeline to increase the actions selected and these will be shown in the Actions tab and all console logs and network logs will be filtered to only show the logs for the actions selected.
|
||||
|
||||
[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 or via the `Select file(s)` button.
|
||||

|
||||
|
||||
Trace Viewer loads the trace entirely in your browser and does not transmit any data externally.
|
||||
|
||||
<img width="1119" alt="Drop Playwright Trace to load" src="https://user-images.githubusercontent.com/13063165/194577918-b4d45726-2692-4093-8a28-9e73552617ef.png" />
|
||||
### Snapshots
|
||||
|
||||
### Viewing remote traces
|
||||
When tracing with the [`option: Tracing.start.snapshots`] option turned on (default), Playwright captures a set of complete DOM snapshots for each action. Depending on the type of the action, it will capture:
|
||||
|
||||
You can open remote traces directly using its URL. This makes it easy to view the remote trace without having to manually download the file from CI runs, for example.
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
|Before|A snapshot at the time action is called.|
|
||||
|Action|A snapshot at the moment of the performed input. This type of snapshot is especially useful when exploring where exactly Playwright clicked.|
|
||||
|After|A snapshot after the action.|
|
||||
|
||||
```bash js
|
||||
npx playwright show-trace https://example.com/trace.zip
|
||||
```
|
||||
Here is what the typical Action snapshot looks like:
|
||||
|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace https://example.com/trace.zip"
|
||||
```
|
||||

|
||||
|
||||
```bash python
|
||||
playwright show-trace https://example.com/trace.zip
|
||||
```
|
||||
Notice how it highlights both, the DOM Node as well as the exact click position.
|
||||
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 show-trace https://example.com/trace.zip
|
||||
```
|
||||
### Source
|
||||
|
||||
When using [trace.playwright.dev](https://trace.playwright.dev), you can also pass the URL of your uploaded trace at some accessible storage (e.g. inside your CI) as a query parameter. CORS (Cross-Origin Resource Sharing) rules might apply.
|
||||
When you click on an action in the sidebar, the line of code for that action is highlighted in the source panel.
|
||||
|
||||
```txt
|
||||
https://trace.playwright.dev/?trace=https://demo.playwright.dev/reports/todomvc/data/cb0fa77ebd9487a5c899f3ae65a7ffdbac681182.zip
|
||||
```
|
||||

|
||||
|
||||
## Recording a trace
|
||||
### Call
|
||||
|
||||
The call tab shows you information about the action such as the time it took, what locator was used, if in strict mode and what key was used.
|
||||
|
||||

|
||||
|
||||
### Log
|
||||
|
||||
See a full log of your test to better understand what Playwright is doing behind the scenes such as scrolling into view, waiting for element to be visible, enabled and stable and performing actions such as click, fill, press etc.
|
||||
|
||||

|
||||
|
||||
### Errors
|
||||
|
||||
If your test fails you will see the error messages for each test in the Errors tab. The timeline will also show a red line highlighting where the error occurred. You can also click on the source tab to see on which line of the source code the error is.
|
||||
|
||||

|
||||
|
||||
### Console
|
||||
|
||||
See console logs from the browser as well as from your test. Different icons are displayed to show you if the console log came from the browser or from the test file.
|
||||
|
||||

|
||||
|
||||
Double click on an action from your test in the actions sidebar. This will filter the console to only show the logs that were made during that action. Click the *Show all* button to see all console logs again.
|
||||
|
||||
Use the timeline to filter actions, by clicking a start point and dragging to an ending point. The console tab will also be filtered to only show the logs that were made during the actions selected.
|
||||
|
||||
|
||||
### Network
|
||||
|
||||
The Network tab shows you all the network requests that were made during your test. You can sort by different types of requests, status code, method, request, content type, duration and size. Click on a request to see more information about it such as the request headers, response headers, request body and response body.
|
||||
|
||||

|
||||
|
||||
Double click on an action from your test in the actions sidebar. This will filter the network requests to only show the requests that were made during that action. Click the *Show all* button to see all network requests again.
|
||||
|
||||
Use the timeline to filter actions, by clicking a start point and dragging to an ending point. The network tab will also be filtered to only show the network requests that were made during the actions selected.
|
||||
|
||||
### Metadata
|
||||
|
||||
Next to the Actions tab you will find the Metadata tab which will show you more information on your test such as the Browser, viewport size, test duration and more.
|
||||
|
||||

|
||||
|
||||
### Attachments
|
||||
* langs: js
|
||||
|
||||
### Tracing locally
|
||||
The "Attachments" tab allows you to explore attachments. If you're doing [visual regression testing](./test-snapshots.md), you'll be able to compare screenshots by examining the image diff, the actual image and the expected image. When you click on the expected image you can use the slider to slide one image over the other so you can easily see the differences in your screenshots.
|
||||
|
||||

|
||||
|
||||
|
||||
## Recording a trace locally
|
||||
* langs: js
|
||||
|
||||
To record a trace during development mode set the `--trace` flag to `on` when running your tests. You can also use [UI Mode](./test-ui-mode.md) for a better developer experience, as it traces each test automatically.
|
||||
To record a trace during development mode set the `--trace` flag to `on` when running your tests. You can also use [UI Mode](./test-ui-mode.md) for a better developer experience.
|
||||
|
||||
```bash
|
||||
npx playwright test --trace on
|
||||
|
|
@ -87,7 +126,7 @@ You can then open the HTML report and click on the trace icon to open the trace.
|
|||
```bash
|
||||
npx playwright show-report
|
||||
```
|
||||
### Tracing on CI
|
||||
## Recording a trace on CI
|
||||
* langs: js
|
||||
|
||||
Traces should be run on continuous integration on the first retry of a failed test
|
||||
|
|
@ -215,7 +254,6 @@ Traces can be recorded using the [`property: BrowserContext.tracing`] API as fol
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -310,70 +348,6 @@ public class UnitTest1 : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp
|
||||
using System.Reflection;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[WithTestName]
|
||||
public class UnitTest1 : PageTest
|
||||
{
|
||||
public override async Task InitializeAsync()
|
||||
{
|
||||
await base.InitializeAsync().ConfigureAwait(false);
|
||||
await Context.Tracing.StartAsync(new()
|
||||
{
|
||||
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
|
||||
Screenshots = true,
|
||||
Snapshots = true,
|
||||
Sources = true
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
await Context.Tracing.StopAsync(new()
|
||||
{
|
||||
Path = Path.Combine(
|
||||
Environment.CurrentDirectory,
|
||||
"playwright-traces",
|
||||
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
|
||||
)
|
||||
});
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
// ...
|
||||
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
|
||||
}
|
||||
}
|
||||
|
||||
public class WithTestNameAttribute : BeforeAfterTestAttribute
|
||||
{
|
||||
public static string CurrentTestName = string.Empty;
|
||||
public static string CurrentClassName = string.Empty;
|
||||
|
||||
public override void Before(MethodInfo methodInfo)
|
||||
{
|
||||
CurrentTestName = methodInfo.Name;
|
||||
CurrentClassName = methodInfo.DeclaringType!.Name;
|
||||
}
|
||||
|
||||
public override void After(MethodInfo methodInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -391,7 +365,6 @@ Setup your tests to record a trace only when the test fails:
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -486,165 +459,60 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp
|
||||
using System.Reflection;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[WithTestName]
|
||||
public class UnitTest1 : PageTest
|
||||
{
|
||||
public override async Task InitializeAsync()
|
||||
{
|
||||
await base.InitializeAsync().ConfigureAwait(false);
|
||||
await Context.Tracing.StartAsync(new()
|
||||
{
|
||||
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
|
||||
Screenshots = true,
|
||||
Snapshots = true,
|
||||
Sources = true
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
await Context.Tracing.StopAsync(new()
|
||||
{
|
||||
Path = !TestOk ? Path.Combine(
|
||||
Environment.CurrentDirectory,
|
||||
"playwright-traces",
|
||||
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
|
||||
) : null
|
||||
});
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
// ...
|
||||
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
|
||||
}
|
||||
}
|
||||
|
||||
public class WithTestNameAttribute : BeforeAfterTestAttribute
|
||||
{
|
||||
public static string CurrentTestName = string.Empty;
|
||||
public static string CurrentClassName = string.Empty;
|
||||
|
||||
public override void Before(MethodInfo methodInfo)
|
||||
{
|
||||
CurrentTestName = methodInfo.Name;
|
||||
CurrentClassName = methodInfo.DeclaringType!.Name;
|
||||
}
|
||||
|
||||
public override void After(MethodInfo methodInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Trace Viewer features
|
||||
### Actions
|
||||
## Opening the trace
|
||||
|
||||
In the Actions tab you can see what locator was used for every action and how long each one took to run. Hover over each action of your test and visually see the change in the DOM snapshot. Go back and forward in time and click an action to inspect and debug. Use the Before and After tabs to visually see what happened before and after the action.
|
||||
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.
|
||||
|
||||

|
||||
```bash js
|
||||
npx playwright show-trace path/to/trace.zip
|
||||
```
|
||||
|
||||
**Selecting each action reveals:**
|
||||
- Action snapshots
|
||||
- Action log
|
||||
- Source code location
|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace trace.zip"
|
||||
```
|
||||
|
||||
### Screenshots
|
||||
```bash python
|
||||
playwright show-trace trace.zip
|
||||
```
|
||||
|
||||
When tracing with the [`option: Tracing.start.screenshots`] option turned on (default), each trace records a screencast and renders it as a film strip. You can hover over the film strip to see a magnified image of for each action and state which helps you easily find the action you want to inspect.
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 show-trace trace.zip
|
||||
```
|
||||
|
||||
Double click on an action to see the time range for that action. You can use the slider in the timeline to increase the actions selected and these will be shown in the Actions tab and all console logs and network logs will be filtered to only show the logs for the actions selected.
|
||||
## Using [trace.playwright.dev](https://trace.playwright.dev)
|
||||
|
||||

|
||||
[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.
|
||||
|
||||
|
||||
### Snapshots
|
||||
<img width="1119" alt="Drop Playwright Trace to load" src="https://user-images.githubusercontent.com/13063165/194577918-b4d45726-2692-4093-8a28-9e73552617ef.png" />
|
||||
|
||||
When tracing with the [`option: Tracing.start.snapshots`] option turned on (default), Playwright captures a set of complete DOM snapshots for each action. Depending on the type of the action, it will capture:
|
||||
## Viewing remote traces
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
|Before|A snapshot at the time action is called.|
|
||||
|Action|A snapshot at the moment of the performed input. This type of snapshot is especially useful when exploring where exactly Playwright clicked.|
|
||||
|After|A snapshot after the action.|
|
||||
You can open remote traces using its URL. They could be generated on a CI run which makes it easy to view the remote trace without having to manually download the file.
|
||||
|
||||
Here is what the typical Action snapshot looks like:
|
||||
```bash js
|
||||
npx playwright show-trace https://example.com/trace.zip
|
||||
```
|
||||
|
||||

|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace https://example.com/trace.zip"
|
||||
```
|
||||
|
||||
Notice how it highlights both, the DOM Node as well as the exact click position.
|
||||
```bash python
|
||||
playwright show-trace https://example.com/trace.zip
|
||||
```
|
||||
|
||||
### Source
|
||||
|
||||
When you click on an action in the sidebar, the line of code for that action is highlighted in the source panel.
|
||||
|
||||

|
||||
|
||||
### Call
|
||||
|
||||
The call tab shows you information about the action such as the time it took, what locator was used, if in strict mode and what key was used.
|
||||
|
||||

|
||||
|
||||
### Log
|
||||
|
||||
See a full log of your test to better understand what Playwright is doing behind the scenes such as scrolling into view, waiting for element to be visible, enabled and stable and performing actions such as click, fill, press etc.
|
||||
|
||||

|
||||
|
||||
### Errors
|
||||
|
||||
If your test fails you will see the error messages for each test in the Errors tab. The timeline will also show a red line highlighting where the error occurred. You can also click on the source tab to see on which line of the source code the error is.
|
||||
|
||||

|
||||
|
||||
### Console
|
||||
|
||||
See console logs from the browser as well as from your test. Different icons are displayed to show you if the console log came from the browser or from the test file.
|
||||
|
||||

|
||||
|
||||
Double click on an action from your test in the actions sidebar. This will filter the console to only show the logs that were made during that action. Click the *Show all* button to see all console logs again.
|
||||
|
||||
Use the timeline to filter actions, by clicking a start point and dragging to an ending point. The console tab will also be filtered to only show the logs that were made during the actions selected.
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 show-trace https://example.com/trace.zip
|
||||
```
|
||||
|
||||
|
||||
### Network
|
||||
You can also pass the URL of your uploaded trace (e.g. inside your CI) from some accessible storage as a parameter. CORS (Cross-Origin Resource Sharing) rules might apply.
|
||||
|
||||
The Network tab shows you all the network requests that were made during your test. You can sort by different types of requests, status code, method, request, content type, duration and size. Click on a request to see more information about it such as the request headers, response headers, request body and response body.
|
||||
|
||||

|
||||
|
||||
Double click on an action from your test in the actions sidebar. This will filter the network requests to only show the requests that were made during that action. Click the *Show all* button to see all network requests again.
|
||||
|
||||
Use the timeline to filter actions, by clicking a start point and dragging to an ending point. The network tab will also be filtered to only show the network requests that were made during the actions selected.
|
||||
|
||||
### Metadata
|
||||
|
||||
Next to the Actions tab you will find the Metadata tab which will show you more information on your test such as the Browser, viewport size, test duration and more.
|
||||
|
||||

|
||||
|
||||
### Attachments
|
||||
* langs: js
|
||||
|
||||
The "Attachments" tab allows you to explore attachments. If you're doing [visual regression testing](./test-snapshots.md), you'll be able to compare screenshots by examining the image diff, the actual image and the expected image. When you click on the expected image you can use the slider to slide one image over the other so you can easily see the differences in your screenshots.
|
||||
|
||||

|
||||
```txt
|
||||
https://trace.playwright.dev/?trace=https://demo.playwright.dev/reports/todomvc/data/cb0fa77ebd9487a5c899f3ae65a7ffdbac681182.zip
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Take a look at the following example to see how to write a test.
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -118,40 +117,6 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task HasTitle()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
|
||||
// Click the get started link.
|
||||
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();
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -239,7 +204,6 @@ The Playwright NUnit and MSTest test framework base classes will isolate each te
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -284,43 +248,23 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task BasicTest()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Using Test Hooks
|
||||
|
||||
You can use `SetUp`/`TearDown` in NUnit or `TestInitialize`/`TestCleanup` in MSTest to prepare and clean up your test environment:
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
You can use `SetUp`/`TearDown` to prepare and clean up your test environment:
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
|
@ -350,8 +294,6 @@ public class ExampleTest : PageTest
|
|||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
You can use `TestInitialize`/`TestCleanup` to prepare and clean up your test environment:
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Playwright.MSTest;
|
||||
|
|
@ -377,39 +319,6 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
You can use `InitializeAsync`/`DisposeAsync` to prepare and clean up your test environment:
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task MainNavigation()
|
||||
{
|
||||
// Assertions use the expect API.
|
||||
await Expect(Page).ToHaveURLAsync("https://playwright.dev/");
|
||||
}
|
||||
|
||||
override public async Task InitializeAsync()
|
||||
{
|
||||
await base.InitializeAsync();
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
}
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
Console.WriteLine("After each test cleanup");
|
||||
await base.DisposeAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -419,4 +328,4 @@ public class UnitTest1: PageTest
|
|||
- [Generate tests with Codegen](./codegen-intro.md)
|
||||
- [See a trace of your tests](./trace-viewer-intro.md)
|
||||
- [Run tests on CI](./ci-intro.md)
|
||||
- [Learn more about the MSTest, NUnit, or xUnit base classes](./test-runners.md)
|
||||
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { fixupConfigRules } from '@eslint/compat';
|
||||
import { FlatCompat } from '@eslint/eslintrc';
|
||||
import js from '@eslint/js';
|
||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import notice from 'eslint-plugin-notice';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import { baseRules } from './eslint.config.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
});
|
||||
|
||||
const baseConfig = fixupConfigRules(compat.extends('plugin:react/recommended', 'plugin:react-hooks/recommended'));
|
||||
|
||||
const plugins = {
|
||||
'@stylistic': stylistic,
|
||||
'@typescript-eslint': typescriptEslint,
|
||||
notice,
|
||||
};
|
||||
|
||||
const ignores = [
|
||||
'.github/',
|
||||
'*.js',
|
||||
'**/.cache/',
|
||||
'**/*.d.ts',
|
||||
'**/dist/**',
|
||||
'index.d.ts',
|
||||
'node_modules/',
|
||||
'output/',
|
||||
'packages/*/lib/',
|
||||
'test-results/',
|
||||
'tests/',
|
||||
'utils/',
|
||||
];
|
||||
|
||||
export default [
|
||||
{ ignores },
|
||||
{
|
||||
plugins,
|
||||
settings: {
|
||||
react: { version: 'detect' },
|
||||
}
|
||||
},
|
||||
...baseConfig,
|
||||
packageSection('html-reporter'),
|
||||
packageSection('recorder'),
|
||||
packageSection('trace-viewer'),
|
||||
];
|
||||
|
||||
function packageSection(packageName) {
|
||||
return {
|
||||
files: [
|
||||
`packages/${packageName}/src/**/*.ts`,
|
||||
`packages/${packageName}/src/**/*.tsx`,
|
||||
`packages/web/src/**/*.ts`,
|
||||
`packages/web/src/**/*.tsx`,
|
||||
],
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 9,
|
||||
sourceType: 'module',
|
||||
parserOptions: {
|
||||
project: path.join(__dirname, 'packages', packageName, 'tsconfig.json'),
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...baseRules,
|
||||
'no-console': 2,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,292 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import notice from 'eslint-plugin-notice';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import importRules from 'eslint-plugin-import';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const plugins = {
|
||||
'@stylistic': stylistic,
|
||||
'@typescript-eslint': typescriptEslint,
|
||||
notice,
|
||||
import: importRules,
|
||||
};
|
||||
|
||||
const ignores = [
|
||||
'.github/',
|
||||
'*.js',
|
||||
'**/.cache/',
|
||||
'**/*.d.ts',
|
||||
'index.d.ts',
|
||||
'node_modules/',
|
||||
'output/',
|
||||
'packages/*/lib/',
|
||||
'packages/html-reporter/**',
|
||||
'packages/playwright-core/src/generated/*',
|
||||
'packages/playwright-core/src/third_party/',
|
||||
'packages/playwright-core/types/*',
|
||||
'packages/playwright-ct-core/src/generated/*',
|
||||
'packages/recorder/**',
|
||||
'packages/trace-viewer/**',
|
||||
'packages/web/**',
|
||||
'test-results/',
|
||||
'tests/assets/',
|
||||
'tests/components/',
|
||||
'tests/installation/fixture-scripts/',
|
||||
'tests/third_party/',
|
||||
'utils/',
|
||||
];
|
||||
|
||||
export const baseRules = {
|
||||
'@typescript-eslint/no-unused-vars': [2, { args: 'none', caughtErrors: 'none' }],
|
||||
|
||||
/**
|
||||
* Enforced rules
|
||||
*/
|
||||
// syntax preferences
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'quotes': [2, 'single', {
|
||||
'avoidEscape': true,
|
||||
'allowTemplateLiterals': true
|
||||
}],
|
||||
'jsx-quotes': [2, 'prefer-single'],
|
||||
'no-extra-semi': 2,
|
||||
'@stylistic/semi': [2],
|
||||
'comma-style': [2, 'last'],
|
||||
'wrap-iife': [2, 'inside'],
|
||||
'spaced-comment': [2, 'always', {
|
||||
'markers': ['*']
|
||||
}],
|
||||
'eqeqeq': [2],
|
||||
'accessor-pairs': [2, {
|
||||
'getWithoutSet': false,
|
||||
'setWithoutGet': false
|
||||
}],
|
||||
'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
|
||||
'curly': [2, 'multi-or-nest', 'consistent'],
|
||||
'new-parens': 2,
|
||||
'arrow-parens': [2, 'as-needed'],
|
||||
'prefer-const': 2,
|
||||
'quote-props': [2, 'consistent'],
|
||||
'nonblock-statement-body-position': [2, 'below'],
|
||||
|
||||
// anti-patterns
|
||||
'no-var': 2,
|
||||
'no-with': 2,
|
||||
'no-multi-str': 2,
|
||||
'no-caller': 2,
|
||||
'no-implied-eval': 2,
|
||||
'no-labels': 2,
|
||||
'no-new-object': 2,
|
||||
'no-octal-escape': 2,
|
||||
'no-self-compare': 2,
|
||||
'no-shadow-restricted-names': 2,
|
||||
'no-cond-assign': 2,
|
||||
'no-debugger': 2,
|
||||
'no-dupe-keys': 2,
|
||||
'no-duplicate-case': 2,
|
||||
'no-empty-character-class': 2,
|
||||
'no-unreachable': 2,
|
||||
'no-unsafe-negation': 2,
|
||||
'radix': 2,
|
||||
'valid-typeof': 2,
|
||||
'no-implicit-globals': [2],
|
||||
'no-unused-expressions': [2, { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
||||
'no-proto': 2,
|
||||
|
||||
// es2015 features
|
||||
'require-yield': 2,
|
||||
'template-curly-spacing': [2, 'never'],
|
||||
|
||||
// spacing details
|
||||
'space-infix-ops': 2,
|
||||
'space-in-parens': [2, 'never'],
|
||||
'array-bracket-spacing': [2, 'never'],
|
||||
'comma-spacing': [2, { 'before': false, 'after': true }],
|
||||
'keyword-spacing': [2, 'always'],
|
||||
'space-before-function-paren': [2, {
|
||||
'anonymous': 'never',
|
||||
'named': 'never',
|
||||
'asyncArrow': 'always'
|
||||
}],
|
||||
'no-whitespace-before-property': 2,
|
||||
'keyword-spacing': [2, {
|
||||
'overrides': {
|
||||
'if': { 'after': true },
|
||||
'else': { 'after': true },
|
||||
'for': { 'after': true },
|
||||
'while': { 'after': true },
|
||||
'do': { 'after': true },
|
||||
'switch': { 'after': true },
|
||||
'return': { 'after': true }
|
||||
}
|
||||
}],
|
||||
'arrow-spacing': [2, {
|
||||
'after': true,
|
||||
'before': true
|
||||
}],
|
||||
'@stylistic/func-call-spacing': 2,
|
||||
'@stylistic/type-annotation-spacing': 2,
|
||||
|
||||
// file whitespace
|
||||
'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 0 }],
|
||||
'no-mixed-spaces-and-tabs': 2,
|
||||
'no-trailing-spaces': 2,
|
||||
'linebreak-style': [process.platform === 'win32' ? 0 : 2, 'unix'],
|
||||
'indent': [2, 2, { 'SwitchCase': 1, 'CallExpression': { 'arguments': 2 }, 'MemberExpression': 2 }],
|
||||
'key-spacing': [2, {
|
||||
'beforeColon': false
|
||||
}],
|
||||
'eol-last': 2,
|
||||
|
||||
// copyright
|
||||
'notice/notice': [2, {
|
||||
'mustMatch': 'Copyright',
|
||||
'templateFile': path.join(__dirname, 'utils', 'copyright.js'),
|
||||
}],
|
||||
|
||||
// react
|
||||
'react/react-in-jsx-scope': 0
|
||||
};
|
||||
|
||||
const noFloatingPromisesRules = {
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
};
|
||||
|
||||
const noBooleanCompareRules = {
|
||||
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 2,
|
||||
};
|
||||
|
||||
const noWebGlobalsRules = {
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{ 'name': 'window' },
|
||||
{ 'name': 'document' },
|
||||
{ 'name': 'globalThis' },
|
||||
],
|
||||
};
|
||||
|
||||
const noNodeGlobalsRules = {
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{ 'name': 'process' },
|
||||
],
|
||||
};
|
||||
|
||||
const importOrderRules = {
|
||||
'import/order': [2, {
|
||||
'groups': ['builtin', 'external', 'internal', ['parent', 'sibling'], 'index', 'type'],
|
||||
'newlines-between': 'always',
|
||||
}],
|
||||
'import/consistent-type-specifier-style': [2, 'prefer-top-level']
|
||||
};
|
||||
|
||||
const languageOptions = {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 9,
|
||||
sourceType: 'module',
|
||||
};
|
||||
|
||||
const languageOptionsWithTsConfig = {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 9,
|
||||
sourceType: 'module',
|
||||
parserOptions: {
|
||||
project: path.join(__dirname, 'tsconfig.json'),
|
||||
},
|
||||
};
|
||||
|
||||
export default [{
|
||||
ignores,
|
||||
}, {
|
||||
files: ['**/*.ts'],
|
||||
plugins,
|
||||
languageOptions,
|
||||
rules: baseRules,
|
||||
}, {
|
||||
files: ['packages/**/*.ts'],
|
||||
languageOptions: languageOptionsWithTsConfig,
|
||||
rules: {
|
||||
'no-console': 2,
|
||||
'no-restricted-properties': [2, {
|
||||
'object': 'process',
|
||||
'property': 'exit',
|
||||
'message': 'Please use gracefullyProcessExitDoNotHang function to exit the process.',
|
||||
}],
|
||||
}
|
||||
}, {
|
||||
files: [
|
||||
'packages/**/*.ts',
|
||||
],
|
||||
rules: {
|
||||
...importOrderRules
|
||||
},
|
||||
}, {
|
||||
files: ['packages/playwright/**/*.ts'],
|
||||
rules: {
|
||||
...noFloatingPromisesRules,
|
||||
}
|
||||
}, {
|
||||
files: ['packages/playwright/src/reporters/**/*.ts'],
|
||||
languageOptions: languageOptionsWithTsConfig,
|
||||
rules: {
|
||||
'no-console': 'off'
|
||||
}
|
||||
}, {
|
||||
files: [
|
||||
'packages/playwright-core/src/server/injected/**/*.ts',
|
||||
'packages/playwright-core/src/server/isomorphic/**/*.ts',
|
||||
'packages/playwright-core/src/utils/isomorphic/**/*.ts',
|
||||
],
|
||||
languageOptions: languageOptionsWithTsConfig,
|
||||
rules: {
|
||||
...noWebGlobalsRules,
|
||||
...noFloatingPromisesRules,
|
||||
...noBooleanCompareRules,
|
||||
}
|
||||
}, {
|
||||
files: [
|
||||
'packages/playwright-core/src/client/**/*.ts',
|
||||
'packages/playwright-core/src/protocol/**/*.ts',
|
||||
'packages/playwright-core/src/utils/**/*.ts',
|
||||
],
|
||||
languageOptions: languageOptionsWithTsConfig,
|
||||
rules: {
|
||||
...noNodeGlobalsRules,
|
||||
...noFloatingPromisesRules,
|
||||
...noBooleanCompareRules,
|
||||
}
|
||||
}, {
|
||||
files: ['tests/**/*.spec.js', 'tests/**/*.ts'],
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 9,
|
||||
sourceType: 'module',
|
||||
parserOptions: {
|
||||
project: path.join(__dirname, 'tests', 'tsconfig.json'),
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...noFloatingPromisesRules,
|
||||
}
|
||||
}];
|
||||
7314
package-lock.json
generated
7314
package-lock.json
generated
File diff suppressed because it is too large
Load diff
37
package.json
37
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "playwright-internal",
|
||||
"private": true,
|
||||
"version": "1.51.0-next",
|
||||
"version": "1.49.1",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -30,14 +30,14 @@
|
|||
"ttest": "node ./tests/playwright-test/stable-test-runner/node_modules/@playwright/test/cli test --config=tests/playwright-test/playwright.config.ts",
|
||||
"ct": "playwright test tests/components/test-all.spec.js --reporter=list",
|
||||
"test": "playwright test --config=tests/library/playwright.config.ts",
|
||||
"eslint": "eslint --cache && eslint -c eslint-react.config.mjs",
|
||||
"eslint": "eslint --cache --report-unused-disable-directives --ext ts,tsx,js,jsx,mjs .",
|
||||
"tsc": "tsc -p . && tsc -p packages/html-reporter/",
|
||||
"build-installer": "babel -s --extensions \".ts\" --out-dir packages/playwright-core/lib/utils/ packages/playwright-core/src/utils",
|
||||
"doc": "node utils/doclint/cli.js",
|
||||
"lint": "npm run eslint && npm run tsc && npm run doc && npm run check-deps && node utils/generate_channels.js && node utils/generate_types/ && npm run lint-tests && npm run test-types && npm run lint-packages",
|
||||
"lint-packages": "node utils/workspace.js --ensure-consistent",
|
||||
"lint-tests": "node utils/lint_tests.js",
|
||||
"flint": "concurrently \"npm run eslint\" \"npm run tsc\" \"npm run doc\" \"npm run check-deps\" \"node utils/generate_channels.js\" \"node utils/generate_types/\" \"npm run lint-tests\" \"npm run test-types\" \"npm run lint-packages\" \"node utils/doclint/linting-code-snippets/cli.js --js-only\"",
|
||||
"flint": "concurrently \"npm run eslint\" \"npm run tsc\" \"npm run doc\" \"npm run check-deps\" \"node utils/generate_channels.js\" \"node utils/generate_types/\" \"npm run lint-tests\" \"npm run test-types\" \"npm run lint-packages\"",
|
||||
"clean": "node utils/build/clean.js",
|
||||
"build": "node utils/build/build.js",
|
||||
"watch": "node utils/build/build.js --watch --lint",
|
||||
|
|
@ -62,22 +62,18 @@
|
|||
"@babel/plugin-transform-optional-chaining": "^7.23.4",
|
||||
"@babel/plugin-transform-typescript": "^7.23.6",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@eslint/compat": "^1.2.6",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@stylistic/eslint-plugin": "^3.0.1",
|
||||
"@types/babel__core": "^7.20.2",
|
||||
"@types/codemirror": "^5.60.7",
|
||||
"@types/formidable": "^2.0.4",
|
||||
"@types/immutable": "^3.8.7",
|
||||
"@types/node": "^18.19.68",
|
||||
"@types/node": "^18.19.39",
|
||||
"@types/react": "^18.0.12",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@types/xml2js": "^0.4.9",
|
||||
"@typescript-eslint/eslint-plugin": "^8.23.0",
|
||||
"@typescript-eslint/parser": "^8.23.0",
|
||||
"@typescript-eslint/utils": "^8.23.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||
"@typescript-eslint/parser": "^7.15.0",
|
||||
"@typescript-eslint/utils": "^7.15.0",
|
||||
"@vitejs/plugin-basic-ssl": "^1.1.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"@zip.js/zip.js": "^2.7.29",
|
||||
|
|
@ -89,25 +85,24 @@
|
|||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"electron": "^30.1.2",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-notice": "^1.0.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"esbuild": "^0.18.11",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-plugin-internal-playwright": "file:utils/eslint-plugin-internal-playwright",
|
||||
"eslint-plugin-notice": "^0.9.10",
|
||||
"eslint-plugin-react": "^7.35.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"formidable": "^2.1.1",
|
||||
"immutable": "^4.3.7",
|
||||
"license-checker": "^25.0.1",
|
||||
"markdown-to-jsx": "^7.7.3",
|
||||
"mime": "^3.0.0",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"ssim.js": "^3.5.0",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.1.0",
|
||||
"typescript": "^5.5.3",
|
||||
"vite": "^5.4.6",
|
||||
"ws": "^8.17.1",
|
||||
"xml2js": "^0.5.0",
|
||||
"yaml": "2.6.0"
|
||||
"yaml": "^2.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
packages/.eslintrc.js
Normal file
21
packages/.eslintrc.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
module.exports = {
|
||||
"extends": "../.eslintrc.js",
|
||||
/**
|
||||
* ESLint rules
|
||||
*
|
||||
* All available rules: http://eslint.org/docs/rules/
|
||||
*
|
||||
* Rules take the following form:
|
||||
* "rule-name", [severity, { opts }]
|
||||
* Severity: 2 == error, 1 == warning, 0 == off.
|
||||
*/
|
||||
"rules": {
|
||||
"no-console": 2,
|
||||
"no-debugger": 2,
|
||||
"no-restricted-properties": [2, {
|
||||
"object": "process",
|
||||
"property": "exit",
|
||||
"message": "Please use gracefullyProcessExitDoNotHang function to exit the process.",
|
||||
}],
|
||||
}
|
||||
};
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html style='scrollbar-gutter: stable both-edges;'>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='UTF-8'>
|
||||
<meta name='color-scheme' content='dark light'>
|
||||
|
|
|
|||
|
|
@ -2,5 +2,10 @@
|
|||
"name": "html-reporter",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module"
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build && tsc",
|
||||
"preview": "vite preview"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,7 @@ export default defineConfig({
|
|||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
snapshotPathTemplate: '{testDir}/__screenshots__/{projectName}/{testFilePath}/{arg}{ext}',
|
||||
reporter: process.env.CI ? [
|
||||
['blob', { fileName: `${process.env.PWTEST_BOT_NAME}.zip` }],
|
||||
] : [
|
||||
['html']
|
||||
],
|
||||
reporter: process.env.CI ? 'blob' : 'html',
|
||||
use: {
|
||||
ctPort: 3101,
|
||||
ctViteConfig: {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import './colors.css';
|
|||
import './common.css';
|
||||
import * as icons from './icons';
|
||||
import { clsx } from '@web/uiUtils';
|
||||
import { type AnchorID, useAnchor } from './links';
|
||||
|
||||
export const Chip: React.FC<{
|
||||
header: JSX.Element | string,
|
||||
|
|
@ -29,9 +28,10 @@ export const Chip: React.FC<{
|
|||
setExpanded?: (expanded: boolean) => void,
|
||||
children?: any,
|
||||
dataTestId?: string,
|
||||
}> = ({ header, expanded, setExpanded, children, noInsets, dataTestId }) => {
|
||||
targetRef?: React.RefObject<HTMLDivElement>,
|
||||
}> = ({ header, expanded, setExpanded, children, noInsets, dataTestId, targetRef }) => {
|
||||
const id = React.useId();
|
||||
return <div className='chip' data-testid={dataTestId}>
|
||||
return <div className='chip' data-testid={dataTestId} ref={targetRef}>
|
||||
<div
|
||||
role='button'
|
||||
aria-expanded={!!expanded}
|
||||
|
|
@ -53,17 +53,16 @@ export const AutoChip: React.FC<{
|
|||
noInsets?: boolean,
|
||||
children?: any,
|
||||
dataTestId?: string,
|
||||
revealOnAnchorId?: AnchorID,
|
||||
}> = ({ header, initialExpanded, noInsets, children, dataTestId, revealOnAnchorId }) => {
|
||||
const [expanded, setExpanded] = React.useState(initialExpanded ?? true);
|
||||
const onReveal = React.useCallback(() => setExpanded(true), []);
|
||||
useAnchor(revealOnAnchorId, onReveal);
|
||||
targetRef?: React.RefObject<HTMLDivElement>,
|
||||
}> = ({ header, initialExpanded, noInsets, children, dataTestId, targetRef }) => {
|
||||
const [expanded, setExpanded] = React.useState(initialExpanded || initialExpanded === undefined);
|
||||
return <Chip
|
||||
header={header}
|
||||
expanded={expanded}
|
||||
setExpanded={setExpanded}
|
||||
noInsets={noInsets}
|
||||
dataTestId={dataTestId}
|
||||
targetRef={targetRef}
|
||||
>
|
||||
{children}
|
||||
</Chip>;
|
||||
|
|
|
|||
|
|
@ -267,26 +267,6 @@ article, aside, details, figcaption, figure, footer, header, main, menu, nav, se
|
|||
flex: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
flex: none;
|
||||
height: 24px;
|
||||
border: 1px solid var(--color-btn-border);
|
||||
outline: none;
|
||||
color: var(--color-btn-text);
|
||||
background: var(--color-btn-bg);
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.button:not(:disabled):hover {
|
||||
border-color: var(--color-btn-hover-border);
|
||||
background-color: var(--color-btn-hover-bg);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.subnav-item, .form-control {
|
||||
border-radius: 0 !important;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export const CopyToClipboard: React.FunctionComponent<CopyToClipboardProps> = ({
|
|||
});
|
||||
}, [value]);
|
||||
const iconElement = icon === 'check' ? icons.check() : icon === 'cross' ? icons.cross() : icons.copy();
|
||||
return <button className='copy-icon' title='Copy to clipboard' aria-label='Copy to clipboard' onClick={handleCopy}>{iconElement}</button>;
|
||||
return <button className='copy-icon' aria-label='Copy to clipboard' onClick={handleCopy}>{iconElement}</button>;
|
||||
};
|
||||
|
||||
type CopyToClipboardContainerProps = CopyToClipboardProps & {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,6 @@ test('should toggle filters', async ({ page, mount }) => {
|
|||
await expect(page).toHaveURL(/#\?q=s:flaky/);
|
||||
await component.locator('a', { hasText: 'Skipped' }).click();
|
||||
await expect(page).toHaveURL(/#\?q=s:skipped/);
|
||||
await component.getByRole('textbox').fill('annot:annotation type=annotation description');
|
||||
await component.getByRole('searchbox').fill('annot:annotation type=annotation description');
|
||||
expect(filters).toEqual(['', 's:passed', 's:failed', 's:flaky', 's:skipped', 'annot:annotation type=annotation description']);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,14 +49,12 @@ export const HeaderView: React.FC<React.PropsWithChildren<{
|
|||
<form className='subnav-search' onSubmit={
|
||||
event => {
|
||||
event.preventDefault();
|
||||
const url = new URL(window.location.href);
|
||||
url.hash = filterText ? '?' + new URLSearchParams({ q: filterText }) : '';
|
||||
navigate(url);
|
||||
navigate(`#?q=${filterText ? encodeURIComponent(filterText) : ''}`);
|
||||
}
|
||||
}>
|
||||
{icons.search()}
|
||||
{/* Use navigationId to reset defaultValue */}
|
||||
<input spellCheck={false} className='form-control subnav-search-input input-contrast width-full' value={filterText} onChange={e => {
|
||||
<input type='search' spellCheck={false} className='form-control subnav-search-input input-contrast width-full' value={filterText} onChange={e => {
|
||||
setFilterText(e.target.value);
|
||||
}}></input>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,22 @@ export const blank = () => {
|
|||
return <svg className='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'></svg>;
|
||||
};
|
||||
|
||||
export const externalLink = () => {
|
||||
return <svg className='octicon' viewBox='0 0 16 16' width='16' height='16'><path fillRule='evenodd' d='M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z'></path></svg>;
|
||||
};
|
||||
|
||||
export const calendar = () => {
|
||||
return <svg className='octicon' viewBox='0 0 16 16' width='16' height='16'><path fillRule='evenodd' d='M4.75 0a.75.75 0 01.75.75V2h5V.75a.75.75 0 011.5 0V2h1.25c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0113.25 16H2.75A1.75 1.75 0 011 14.25V3.75C1 2.784 1.784 2 2.75 2H4V.75A.75.75 0 014.75 0zm0 3.5h8.5a.25.25 0 01.25.25V6h-11V3.75a.25.25 0 01.25-.25h2zm-2.25 4v6.75c0 .138.112.25.25.25h10.5a.25.25 0 00.25-.25V7.5h-11z'></path></svg>;
|
||||
};
|
||||
|
||||
export const person = () => {
|
||||
return <svg className='octicon' viewBox='0 0 16 16' width='16' height='16'><path fillRule='evenodd' d='M10.5 5a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0zm.061 3.073a4 4 0 10-5.123 0 6.004 6.004 0 00-3.431 5.142.75.75 0 001.498.07 4.5 4.5 0 018.99 0 .75.75 0 101.498-.07 6.005 6.005 0 00-3.432-5.142z'></path></svg>;
|
||||
};
|
||||
|
||||
export const commit = () => {
|
||||
return <svg className='octicon' viewBox='0 0 16 16' width='16' height='16'><path fillRule='evenodd' d='M10.5 7.75a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0zm1.43.75a4.002 4.002 0 01-7.86 0H.75a.75.75 0 110-1.5h3.32a4.001 4.001 0 017.86 0h3.32a.75.75 0 110 1.5h-3.32z'></path></svg>;
|
||||
};
|
||||
|
||||
export const image = () => {
|
||||
return <svg className='octicon' viewBox='0 0 48 48' version='1.1' width='20' height='20' aria-hidden='true'>
|
||||
<path xmlns='http://www.w3.org/2000/svg' d='M11.85 32H36.2l-7.35-9.95-6.55 8.7-4.6-6.45ZM7 40q-1.2 0-2.1-.9Q4 38.2 4 37V11q0-1.2.9-2.1Q5.8 8 7 8h34q1.2 0 2.1.9.9.9.9 2.1v26q0 1.2-.9 2.1-.9.9-2.1.9Zm0-29v26-26Zm34 26V11H7v26Z'/>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import type { HTMLReport } from './types';
|
||||
import type * as zip from '@zip.js/zip.js';
|
||||
import type zip from '@zip.js/zip.js';
|
||||
// @ts-ignore
|
||||
import * as zipImport from '@zip.js/zip.js/lib/zip-no-worker-inflate.js';
|
||||
import * as React from 'react';
|
||||
|
|
@ -50,8 +50,6 @@ window.onload = () => {
|
|||
ReactDOM.createRoot(document.querySelector('#root')!).render(<ReportLoader />);
|
||||
};
|
||||
|
||||
const kPlaywrightReportStorageForHMR = 'playwrightReportStorageForHMR';
|
||||
|
||||
class ZipReport implements LoadedReport {
|
||||
private _entries = new Map<string, zip.Entry>();
|
||||
private _json!: HTMLReport;
|
||||
|
|
@ -60,20 +58,8 @@ class ZipReport implements LoadedReport {
|
|||
const zipURI = await new Promise<string>(resolve => {
|
||||
if (window.playwrightReportBase64)
|
||||
return resolve(window.playwrightReportBase64);
|
||||
if (window.opener) {
|
||||
window.addEventListener('message', event => {
|
||||
if (event.source === window.opener) {
|
||||
localStorage.setItem(kPlaywrightReportStorageForHMR, event.data);
|
||||
resolve(event.data);
|
||||
}
|
||||
}, { once: true });
|
||||
window.opener.postMessage('ready', '*');
|
||||
} else {
|
||||
const oldReport = localStorage.getItem(kPlaywrightReportStorageForHMR);
|
||||
if (oldReport)
|
||||
return resolve(oldReport);
|
||||
alert('couldnt find report, something with HMR is broken');
|
||||
}
|
||||
window.addEventListener('message', event => event.source === window.opener && resolve(event.data), { once: true });
|
||||
window.opener.postMessage('ready', '*');
|
||||
});
|
||||
|
||||
const zipReader = new zipjs.ZipReader(new zipjs.Data64URIReader(zipURI), { useWebWorkers: false });
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue