Merge remote-tracking branch 'origin/main' into hello-angular-ct
This commit is contained in:
commit
d5aa4d5604
|
|
@ -1,9 +1,11 @@
|
|||
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
|
||||
utils/generate_types/overrides.d.ts
|
||||
utils/generate_types/test/test.ts
|
||||
|
|
|
|||
73
.github/ISSUE_TEMPLATE/bug.md
vendored
73
.github/ISSUE_TEMPLATE/bug.md
vendored
|
|
@ -1,73 +0,0 @@
|
|||
---
|
||||
name: Bug Report
|
||||
about: Something doesn't work like it should? Tell us!
|
||||
title: "[BUG]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- ⚠️⚠️ Do not delete this template ⚠️⚠️ -->
|
||||
|
||||
<!-- 🔎 Search existing issues to avoid creating duplicates. -->
|
||||
<!-- 🧪 Test using the latest Playwright release to see if your issue has already been fixed -->
|
||||
<!-- 💡 Provide enough information for us to be able to reproduce your issue locally -->
|
||||
|
||||
### System info
|
||||
- Playwright Version: [v1.XX]
|
||||
- Operating System: [All, Windows 11, Ubuntu 20, macOS 13.2, etc.]
|
||||
- Browser: [All, Chromium, Firefox, WebKit]
|
||||
- Other info:
|
||||
|
||||
### Source code
|
||||
|
||||
- [ ] I provided exact source code that allows reproducing the issue locally.
|
||||
|
||||
<!-- For simple cases, please provide a self-contained test file along with the config file -->
|
||||
<!-- For larger cases, you can provide a GitHub repo you created for this issue -->
|
||||
<!-- If we can not reproduce the problem locally, we won't be able to act on it -->
|
||||
<!-- You can still file without the exact code and we will try to help, but if we can't repro, it will be closed -->
|
||||
|
||||
**Link to the GitHub repository with the repro**
|
||||
|
||||
[https://github.com/your_profile/playwright_issue_title]
|
||||
|
||||
or
|
||||
|
||||
**Config file**
|
||||
|
||||
```js
|
||||
// playwright.config.ts
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'], },
|
||||
},
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
**Test file (self-contained)**
|
||||
|
||||
```js
|
||||
it('should check the box using setChecked', async ({ page }) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox'></input>`);
|
||||
await page.getByRole('checkbox').check();
|
||||
await expect(page.getByRole('checkbox')).toBeChecked();
|
||||
});
|
||||
```
|
||||
|
||||
**Steps**
|
||||
- [Run the test]
|
||||
- [...]
|
||||
|
||||
**Expected**
|
||||
|
||||
[Describe expected behavior]
|
||||
|
||||
**Actual**
|
||||
|
||||
[Describe actual behavior]
|
||||
100
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
100
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
name: Bug Report 🪲
|
||||
description: Create a bug report to help us improve
|
||||
title: '[Bug]: '
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Please follow these steps first:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Troubleshoot
|
||||
If Playwright is not behaving the way you expect, we'd ask you to look at the [documentation](https://playwright.dev/docs/intro) and search the issue tracker for evidence supporting your expectation.
|
||||
Please make reasonable efforts to troubleshoot and rule out issues with your code, the configuration, or any 3rd party libraries you might be using.
|
||||
Playwright offers [several debugging tools](https://playwright.dev/docs/debug) that you can use to troubleshoot your issues.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Ask for help through appropriate channels
|
||||
If you feel unsure about the cause of the problem, consider asking for help on for example [StackOverflow](https://stackoverflow.com/questions/ask) or our [Discord channel](https://aka.ms/playwright/discord) before posting a bug report. The issue tracker is not a help forum.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Make a minimal reproduction
|
||||
To file the report, you will need a GitHub repository with a minimal (but complete) example and simple/clear steps on how to reproduce the bug.
|
||||
The simpler you can make it, the more likely we are to successfully verify and fix the bug.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> [!IMPORTANT]
|
||||
> Bug reports without a minimal reproduction will be rejected.
|
||||
|
||||
---
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: |
|
||||
The version of Playwright you are using.
|
||||
Is it the [latest](https://github.com/microsoft/playwright/releases)? Test and see if the bug has already been fixed.
|
||||
placeholder: ex. 1.41.1
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please link to a repository with a minimal reproduction and describe accurately how we can reproduce/verify the bug.
|
||||
placeholder: |
|
||||
Example steps (replace with your own):
|
||||
1. Clone my repo at https://github.com/<myuser>/example
|
||||
2. npm install
|
||||
3. npm run test
|
||||
4. You should see the error come up
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A description of what you expect to happen.
|
||||
placeholder: I expect to see X or Y
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: Actual behavior
|
||||
description: |
|
||||
A clear and concise description of the unexpected behavior.
|
||||
Please include any relevant output here, especially any error messages.
|
||||
placeholder: A bug happened!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Anything else that might be relevant
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: envinfo
|
||||
attributes:
|
||||
label: Environment
|
||||
description: |
|
||||
Please paste the output of running `npx envinfo --preset playwright`.
|
||||
This will be automatically formatted as a code block, so no need for backticks.
|
||||
placeholder: |
|
||||
System:
|
||||
OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
|
||||
CPU: (8) arm64
|
||||
Binaries:
|
||||
Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node
|
||||
npm: 10.2.3 - ~/.nvm/versions/node/v18.19.0/bin/npm
|
||||
npmPackages:
|
||||
@playwright/test: 1.41.1 => 1.41.1
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,3 +1,4 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Join our Discord Server
|
||||
url: https://aka.ms/playwright/discord
|
||||
|
|
|
|||
29
.github/ISSUE_TEMPLATE/documentation.yml
vendored
Normal file
29
.github/ISSUE_TEMPLATE/documentation.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
name: Documentation 📖
|
||||
description: Submit a request to add or update documentation
|
||||
title: '[Docs]: '
|
||||
labels: ['Documentation :book:']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Thank you for helping us improve our documentation!
|
||||
Please be sure you are looking at [the Next version of the documentation](https://playwright.dev/docs/next/intro) before opening an issue here.
|
||||
- type: textarea
|
||||
id: links
|
||||
attributes:
|
||||
label: Page(s)
|
||||
description: |
|
||||
Links to one or more documentation pages that should be modified.
|
||||
If you are reporting an issue with a specific section of a page, try to link directly to the nearest anchor.
|
||||
If you are suggesting that a new page be created, link to the parent of the proposed page.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
Describe the change you are requesting.
|
||||
If the issue pertains to a single function or matcher, be sure to specify the entire call signature.
|
||||
validations:
|
||||
required: true
|
||||
30
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
name: Feature Request 🚀
|
||||
description: Submit a proposal for a new feature
|
||||
title: '[Feature]: '
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Thank you for taking the time to suggest a new feature!
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: '🚀 Feature Request'
|
||||
description: A clear and concise description of what the feature is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: example
|
||||
attributes:
|
||||
label: Example
|
||||
description: Describe how this feature would be used.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: motivation
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: |
|
||||
Outline your motivation for the proposal. How will it make Playwright better?
|
||||
validations:
|
||||
required: true
|
||||
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Request new features to be added
|
||||
title: "[Feature]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Let us know what functionality you'd like to see in Playwright and what your use case is.
|
||||
Do you think others might benefit from this as well?
|
||||
27
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
27
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
name: 'Questions / Help 💬'
|
||||
description: If you have questions, please check StackOverflow or Discord
|
||||
title: '[Please read the message below]'
|
||||
labels: [':speech_balloon: Question']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Questions and Help 💬
|
||||
|
||||
This issue tracker is reserved for bug reports and feature requests.
|
||||
|
||||
For anything else, such as questions or getting help, please see:
|
||||
|
||||
- [The Playwright documentation](https://playwright.dev)
|
||||
- [Our Discord server](https://aka.ms/playwright/discord)
|
||||
- type: checkboxes
|
||||
id: no-post
|
||||
attributes:
|
||||
label: |
|
||||
Please do not submit this issue.
|
||||
description: |
|
||||
> [!IMPORTANT]
|
||||
> This issue will be closed.
|
||||
options:
|
||||
- label: I understand
|
||||
required: true
|
||||
32
.github/ISSUE_TEMPLATE/regression.md
vendored
32
.github/ISSUE_TEMPLATE/regression.md
vendored
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
name: Report regression
|
||||
about: Functionality that used to work and does not any more
|
||||
title: "[REGRESSION]: "
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Context:**
|
||||
- GOOD Playwright Version: [what Playwright version worked nicely?]
|
||||
- BAD Playwright Version: [what Playwright version doesn't work any more?]
|
||||
- Operating System: [e.g. Windows, Linux or Mac]
|
||||
- Extra: [any specific details about your environment]
|
||||
|
||||
**Code Snippet**
|
||||
|
||||
Help us help you! Put down a short code snippet that illustrates your bug and
|
||||
that we can run and debug locally. For example:
|
||||
|
||||
```javascript
|
||||
const {chromium, webkit, firefox} = require('playwright');
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch();
|
||||
// ...
|
||||
})();
|
||||
```
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
Add any other details about the problem here.
|
||||
96
.github/ISSUE_TEMPLATE/regression.yml
vendored
Normal file
96
.github/ISSUE_TEMPLATE/regression.yml
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
name: Report regression
|
||||
description: Functionality that used to work and does not any more
|
||||
title: "[Regression]: "
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Please follow these steps first:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Make a minimal reproduction
|
||||
To file the report, you will need a GitHub repository with a minimal (but complete) example and simple/clear steps on how to reproduce the regression.
|
||||
The simpler you can make it, the more likely we are to successfully verify and fix the regression.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> [!IMPORTANT]
|
||||
> Regression reports without a minimal reproduction will be rejected.
|
||||
|
||||
---
|
||||
- type: input
|
||||
id: goodVersion
|
||||
attributes:
|
||||
label: Last Good Version
|
||||
description: |
|
||||
Last version of Playwright where the feature was working.
|
||||
placeholder: ex. 1.40.1
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: badVersion
|
||||
attributes:
|
||||
label: First Bad Version
|
||||
description: |
|
||||
First version of Playwright where the feature was broken.
|
||||
Is it the [latest](https://github.com/microsoft/playwright/releases)? Test and see if the regression has already been fixed.
|
||||
placeholder: ex. 1.41.1
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please link to a repository with a minimal reproduction and describe accurately how we can reproduce/verify the bug.
|
||||
placeholder: |
|
||||
Example steps (replace with your own):
|
||||
1. Clone my repo at https://github.com/<myuser>/example
|
||||
2. npm install
|
||||
3. npm run test
|
||||
4. You should see the error come up
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A description of what you expect to happen.
|
||||
placeholder: I expect to see X or Y
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: Actual behavior
|
||||
description: A clear and concise description of the unexpected behavior.
|
||||
placeholder: A bug happened!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Anything else that might be relevant
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: envinfo
|
||||
attributes:
|
||||
label: Environment
|
||||
description: |
|
||||
Please paste the output of running `npx envinfo --preset playwright`.
|
||||
This will be automatically formatted as a code block, so no need for backticks.
|
||||
placeholder: |
|
||||
System:
|
||||
OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
|
||||
CPU: (8) arm64
|
||||
Binaries:
|
||||
Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node
|
||||
npm: 10.2.3 - ~/.nvm/versions/node/v18.19.0/bin/npm
|
||||
npmPackages:
|
||||
@playwright/test: 1.41.1 => 1.41.1
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
26
.github/ISSUE_TEMPLATE/vscode-extension.md
vendored
26
.github/ISSUE_TEMPLATE/vscode-extension.md
vendored
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
name: VSCode extension bug
|
||||
about: Something doesn't work like it should inside the Visual Studio Code extension or you have a feature request? Tell us!
|
||||
title: "[BUG]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Context:**
|
||||
- Playwright Version: [what Playwright version do you use?]
|
||||
- Operating System: [e.g. Windows, Linux or Mac]
|
||||
- Node.js version: [e.g. 12.22, 14.6]
|
||||
- Visual Studio Code version: [e.g. 1.65]
|
||||
- Playwright for VSCode extension version: [e.g. 1.2.3]
|
||||
- Browser: [e.g. All, Chromium, Firefox, WebKit]
|
||||
- Extra: [any specific details about your environment]
|
||||
|
||||
**Code Snippet**
|
||||
|
||||
Help us help you! Put down a short code snippet that illustrates your bug and
|
||||
that we can run and debug locally. For example:
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
Add any other details about the problem here.
|
||||
46
.github/actions/download-artifact/action.yml
vendored
46
.github/actions/download-artifact/action.yml
vendored
|
|
@ -1,8 +1,8 @@
|
|||
name: 'Download blob report'
|
||||
description: 'Download blob report from GitHub artifacts'
|
||||
name: 'Download artifacts'
|
||||
description: 'Download artifacts from GitHub'
|
||||
inputs:
|
||||
name:
|
||||
description: 'Name of the artifact to download'
|
||||
namePrefix:
|
||||
description: 'Name prefix of the artifacts to download'
|
||||
required: true
|
||||
type: string
|
||||
default: 'blob-report'
|
||||
|
|
@ -10,31 +10,37 @@ inputs:
|
|||
description: 'Directory with downloaded artifacts'
|
||||
required: true
|
||||
type: string
|
||||
default: 'blob-report'
|
||||
default: '.'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Download blob report
|
||||
uses: actions/github-script@v6
|
||||
- name: Create temp downloads dir
|
||||
shell: bash
|
||||
run: mkdir -p '${{ inputs.path }}/artifacts'
|
||||
- name: Download artifacts
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
console.log(`downloading artifacts for workflow_run: ${context.payload.workflow_run.id}`);
|
||||
console.log(`workflow_run: ${JSON.stringify(context.payload.workflow_run, null, 2)}`);
|
||||
const { data } = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
const allArtifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, {
|
||||
...context.repo,
|
||||
run_id: context.payload.workflow_run.id
|
||||
});
|
||||
console.log('total = ', data.total_count);
|
||||
const name = '${{ inputs.name }}';
|
||||
const report = data.artifacts.filter(a => a.name === name)[0];
|
||||
const result = await github.rest.actions.downloadArtifact({
|
||||
...context.repo,
|
||||
artifact_id: report.id,
|
||||
archive_format: 'zip'
|
||||
});
|
||||
console.log('download result', result);
|
||||
console.log('total = ', allArtifacts.length);
|
||||
const artifacts = allArtifacts.filter(a => a.name.startsWith('${{ inputs.namePrefix }}'));
|
||||
const fs = require('fs');
|
||||
fs.writeFileSync(`${name}.zip`, Buffer.from(result.data));
|
||||
- name: Unzip blob report
|
||||
for (const artifact of artifacts) {
|
||||
const result = await github.rest.actions.downloadArtifact({
|
||||
...context.repo,
|
||||
artifact_id: artifact.id,
|
||||
archive_format: 'zip'
|
||||
});
|
||||
console.log('downloaded artifact', result);
|
||||
fs.writeFileSync(`${{ inputs.path }}/artifacts/${artifact.name}.zip`, Buffer.from(result.data));
|
||||
}
|
||||
- name: Unzip artifacts
|
||||
shell: bash
|
||||
run: unzip ${{ inputs.name }}.zip -d ${{ inputs.path }}
|
||||
run: |
|
||||
unzip -n '${{ inputs.path }}/artifacts/*.zip' -d ${{ inputs.path }}
|
||||
rm -rf '${{ inputs.path }}/artifacts'
|
||||
|
|
|
|||
19
.github/actions/upload-blob-report/action.yml
vendored
19
.github/actions/upload-blob-report/action.yml
vendored
|
|
@ -5,16 +5,21 @@ inputs:
|
|||
description: 'Directory containing blob report'
|
||||
required: true
|
||||
type: string
|
||||
default: 'blob-report'
|
||||
default: 'test-results/blob-report'
|
||||
job_name:
|
||||
description: 'Unique job name'
|
||||
required: true
|
||||
type: string
|
||||
default: ''
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Upload blob report to GitHub
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: all-blob-reports
|
||||
path: ${{ inputs.report_dir }}
|
||||
name: blob-report-${{ inputs.job_name }}
|
||||
path: ${{ inputs.report_dir }}/**
|
||||
retention-days: 7
|
||||
- name: Write triggering pull request number in a file
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
|
|
@ -22,7 +27,7 @@ runs:
|
|||
run: echo '${{ github.event.number }}' > pull_request_number.txt;
|
||||
- name: Upload artifact with the pull request number
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pull-request
|
||||
path: pull_request_number.txt
|
||||
name: pull-request-${{ inputs.job_name }}
|
||||
path: pull_request_number.txt
|
||||
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
echo "Version is not a two digit semver version"
|
||||
exit 1
|
||||
fi
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: release-${{ github.event.inputs.version }}
|
||||
fetch-depth: 0
|
||||
|
|
|
|||
12
.github/workflows/create_test_report.yml
vendored
12
.github/workflows/create_test_report.yml
vendored
|
|
@ -12,8 +12,8 @@ jobs:
|
|||
if: ${{ github.event.workflow_run.event == 'pull_request' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
|
|
@ -26,8 +26,8 @@ jobs:
|
|||
- name: Download blob report artifact
|
||||
uses: ./.github/actions/download-artifact
|
||||
with:
|
||||
name: all-blob-reports
|
||||
path: all-blob-reports
|
||||
namePrefix: 'blob-report'
|
||||
path: 'all-blob-reports'
|
||||
|
||||
- name: Merge reports
|
||||
run: |
|
||||
|
|
@ -49,8 +49,8 @@ jobs:
|
|||
- name: Read pull request number
|
||||
uses: ./.github/actions/download-artifact
|
||||
with:
|
||||
name: 'pull-request'
|
||||
path: './'
|
||||
namePrefix: 'pull-request'
|
||||
path: '.'
|
||||
|
||||
- name: Comment on PR
|
||||
uses: actions/github-script@v6
|
||||
|
|
|
|||
8
.github/workflows/infra.yml
vendored
8
.github/workflows/infra.yml
vendored
|
|
@ -18,8 +18,8 @@ jobs:
|
|||
name: "docs & lint"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -40,8 +40,8 @@ jobs:
|
|||
name: "Lint snippets"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- uses: actions/setup-python@v4
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ on:
|
|||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'docs/src/api/**/*'
|
||||
- 'packages/playwright-core/src/client/**/*'
|
||||
- 'packages/playwright-core/src/utils/isomorphic/**/*'
|
||||
- 'packages/playwright/src/matchers/matchers.ts'
|
||||
- 'packages/protocol/src/protocol.yml'
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
|
|
|
|||
21
.github/workflows/publish_canary.yml
vendored
21
.github/workflows/publish_canary.yml
vendored
|
|
@ -13,15 +13,15 @@ env:
|
|||
|
||||
jobs:
|
||||
publish-canary:
|
||||
name: "publish canary NPM & Publish canary Docker"
|
||||
name: "publish canary NPM"
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'microsoft/playwright'
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
|
@ -57,25 +57,14 @@ jobs:
|
|||
run: |
|
||||
utils/build/build-playwright-driver.sh
|
||||
utils/build/upload-playwright-driver.sh
|
||||
- uses: azure/docker-login@v1
|
||||
with:
|
||||
login-server: playwright.azurecr.io
|
||||
username: playwright
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Set up Docker QEMU for arm64 docker builds
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: arm64
|
||||
- name: publish docker canary
|
||||
run: ./utils/docker/publish_docker.sh canary
|
||||
|
||||
publish-trace-viewer:
|
||||
name: "publish Trace Viewer to trace.playwright.dev"
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'microsoft/playwright'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Deploy Canary
|
||||
|
|
|
|||
8
.github/workflows/publish_release_docker.yml
vendored
8
.github/workflows/publish_release_docker.yml
vendored
|
|
@ -17,11 +17,11 @@ env:
|
|||
jobs:
|
||||
publish-docker-release:
|
||||
name: "publish to DockerHub"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.repository == 'microsoft/playwright'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
|
@ -31,7 +31,7 @@ jobs:
|
|||
username: playwright
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Set up Docker QEMU for arm64 docker builds
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
- run: npm ci
|
||||
|
|
|
|||
4
.github/workflows/publish_release_driver.yml
vendored
4
.github/workflows/publish_release_driver.yml
vendored
|
|
@ -13,8 +13,8 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'microsoft/playwright'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
|
|
|||
4
.github/workflows/publish_release_npm.yml
vendored
4
.github/workflows/publish_release_npm.yml
vendored
|
|
@ -16,8 +16,8 @@ jobs:
|
|||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'microsoft/playwright'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Deploy Stable
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ on:
|
|||
env:
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
roll:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
|
|||
4
.github/workflows/roll_driver_nodejs.yml
vendored
4
.github/workflows/roll_driver_nodejs.yml
vendored
|
|
@ -10,8 +10,8 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
if: github.repository == 'microsoft/playwright'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: node utils/build/update-playwright-driver-version.mjs
|
||||
|
|
|
|||
4
.github/workflows/tests_components.yml
vendored
4
.github/workflows/tests_components.yml
vendored
|
|
@ -32,8 +32,8 @@ jobs:
|
|||
node-version: 20
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
|
|
|
|||
4
.github/workflows/tests_electron.yml
vendored
4
.github/workflows/tests_electron.yml
vendored
|
|
@ -28,8 +28,8 @@ jobs:
|
|||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
|
|||
82
.github/workflows/tests_primary.yml
vendored
82
.github/workflows/tests_primary.yml
vendored
|
|
@ -42,9 +42,11 @@ jobs:
|
|||
node-version: 20
|
||||
browser: chromium
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PWTEST_BOT_NAME: "${{ matrix.browser }}-${{ matrix.os }}-node${{ matrix.node-version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
|
|
@ -54,8 +56,6 @@ jobs:
|
|||
- run: npm run build
|
||||
- run: npx playwright install --with-deps ${{ matrix.browser }} chromium
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test -- --project=${{ matrix.browser }}
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.browser }}-${{ matrix.os }}-node${{ matrix.node-version }}"
|
||||
- run: node tests/config/checkCoverage.js ${{ matrix.browser }}
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
|
|
@ -65,6 +65,7 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
test_linux_chromium_tot:
|
||||
name: ${{ matrix.os }} (chromium tip-of-tree)
|
||||
|
|
@ -73,9 +74,11 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PWTEST_BOT_NAME: "${{ matrix.os }}-chromium-tip-of-tree"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -87,7 +90,7 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test -- --project=chromium
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.os }}-chromium-tip-of-tree"
|
||||
PWTEST_BOT_NAME: "${{ matrix.os }}-chromium-tip-of-tree"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -96,6 +99,7 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
test_test_runner:
|
||||
name: Test Runner
|
||||
|
|
@ -104,24 +108,31 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
node-version: [18]
|
||||
shard: [1/2, 2/2]
|
||||
shardIndex: [1, 2]
|
||||
shardTotal: [2]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
node-version: 16
|
||||
shard: 1/2
|
||||
shardIndex: 1
|
||||
shardTotal: 2
|
||||
- os: ubuntu-latest
|
||||
node-version: 16
|
||||
shard: 2/2
|
||||
shardIndex: 2
|
||||
shardTotal: 2
|
||||
- os: ubuntu-latest
|
||||
node-version: 20
|
||||
shard: 1/2
|
||||
shardIndex: 1
|
||||
shardTotal: 2
|
||||
- os: ubuntu-latest
|
||||
node-version: 20
|
||||
shard: 2/2
|
||||
shardIndex: 2
|
||||
shardTotal: 2
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PWTEST_BOT_NAME: "${{ matrix.os }}-node${{ matrix.node-version }}-${{ matrix.shardIndex }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{matrix.node-version}}
|
||||
- run: npm ci
|
||||
|
|
@ -129,13 +140,9 @@ jobs:
|
|||
DEBUG: pw:install
|
||||
- run: npm run build
|
||||
- run: npx playwright install --with-deps
|
||||
- run: npm run ttest -- --shard ${{ matrix.shard }}
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.os }}-node${{ matrix.node-version }}"
|
||||
- run: npm run ttest -- --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
- run: xvfb-run npm run ttest -- --shard ${{ matrix.shard }}
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.os }}-node${{ matrix.node-version }}"
|
||||
- run: xvfb-run npm run ttest -- --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
|
|
@ -145,13 +152,14 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
test_web_components:
|
||||
name: Web Components
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -162,29 +170,33 @@ jobs:
|
|||
- run: npx playwright install --with-deps
|
||||
- run: npm run test-html-reporter
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "web-components-html-reporter"
|
||||
PWTEST_BOT_NAME: "web-components-html-reporter"
|
||||
- name: Upload blob report
|
||||
if: always()
|
||||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: packages/html-reporter/blob-report
|
||||
job_name: "web-components-html-reporter"
|
||||
|
||||
- run: npm run test-web
|
||||
if: always()
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "web-components-web"
|
||||
PWTEST_BOT_NAME: "web-components-web"
|
||||
- name: Upload blob report
|
||||
if: always()
|
||||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: packages/web/blob-report
|
||||
job_name: "web-components-web"
|
||||
|
||||
test_vscode_extension:
|
||||
name: VSCode Extension
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "vscode-extension"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -205,18 +217,16 @@ jobs:
|
|||
working-directory: ./playwright-vscode
|
||||
- name: Run extension tests
|
||||
run: npm run test -- --workers=1
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "vscode-extension"
|
||||
working-directory: ./playwright-vscode
|
||||
- name: Upload blob report
|
||||
if: always()
|
||||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: ./playwright-vscode/blob-report
|
||||
report_dir: playwright-vscode/blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
test_package_installations:
|
||||
name: "Installation Test ${{ matrix.os }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
|
@ -224,10 +234,13 @@ jobs:
|
|||
- ubuntu-latest
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
PWTEST_BOT_NAME: "package-installations-${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -239,12 +252,8 @@ jobs:
|
|||
- run: npm install -g pnpm@8
|
||||
- run: npm run itest
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "package-installations-${{ matrix.os }}"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run itest
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "package-installations-${{ matrix.os }}"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -253,3 +262,4 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
|
|
|||
246
.github/workflows/tests_secondary.yml
vendored
246
.github/workflows/tests_secondary.yml
vendored
|
|
@ -29,9 +29,11 @@ jobs:
|
|||
browser: [chromium, firefox, webkit]
|
||||
os: [ubuntu-20.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PWTEST_BOT_NAME: "${{ matrix.browser }}-${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -41,8 +43,6 @@ jobs:
|
|||
- run: npm run build
|
||||
- run: npx playwright install --with-deps ${{ matrix.browser }} chromium
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test -- --project=${{ matrix.browser }}
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.browser }}-${{ matrix.os }}"
|
||||
- run: node tests/config/checkCoverage.js ${{ matrix.browser }}
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
|
|
@ -52,18 +52,21 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
test_mac:
|
||||
name: ${{ matrix.os }} (${{ matrix.browser }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-12, macos-13]
|
||||
os: [macos-12, macos-13, macos-14]
|
||||
browser: [chromium, firefox, webkit]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PWTEST_BOT_NAME: "${{ matrix.browser }}-${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -73,8 +76,6 @@ jobs:
|
|||
- run: npm run build
|
||||
- run: npx playwright install --with-deps ${{ matrix.browser }} chromium
|
||||
- run: npm run test -- --project=${{ matrix.browser }}
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.browser }}-${{ matrix.os }}"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -83,6 +84,7 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
test_win:
|
||||
name: "Windows"
|
||||
|
|
@ -91,9 +93,11 @@ jobs:
|
|||
matrix:
|
||||
browser: [chromium, firefox, webkit]
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "${{ matrix.browser }}-windows-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -105,13 +109,9 @@ jobs:
|
|||
- run: npm run test -- --project=${{ matrix.browser }} --workers=1
|
||||
if: matrix.browser == 'firefox'
|
||||
shell: bash
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.browser }}-windows-latest"
|
||||
- run: npm run test -- --project=${{ matrix.browser }}
|
||||
if: matrix.browser != 'firefox'
|
||||
shell: bash
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.browser }}-windows-latest"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -120,6 +120,7 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
test-package-installations-other-node-versions:
|
||||
name: "Installation Test ${{ matrix.os }} (${{ matrix.node_version }})"
|
||||
|
|
@ -134,8 +135,8 @@ jobs:
|
|||
node_version: 20
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
- run: npm ci
|
||||
|
|
@ -161,9 +162,11 @@ jobs:
|
|||
browser: [chromium, firefox, webkit]
|
||||
os: [ubuntu-20.04, ubuntu-22.04, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PWTEST_BOT_NAME: "${{ matrix.browser }}-headed-${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -174,12 +177,8 @@ jobs:
|
|||
- run: npx playwright install --with-deps ${{ matrix.browser }} chromium
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test -- --project=${{ matrix.browser }} --headed
|
||||
if: always() && startsWith(matrix.os, 'ubuntu-')
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.browser }}-headed-${{ matrix.os }}"
|
||||
- run: npm run test -- --project=${{ matrix.browser }} --headed
|
||||
if: always() && !startsWith(matrix.os, 'ubuntu-')
|
||||
env:
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.browser }}-headed-${{ matrix.os }}"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -188,6 +187,7 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
transport_linux:
|
||||
name: "Transport"
|
||||
|
|
@ -196,9 +196,11 @@ jobs:
|
|||
matrix:
|
||||
mode: [driver, service]
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
PWTEST_BOT_NAME: "${{ matrix.mode }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -210,7 +212,6 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
|
||||
env:
|
||||
PWTEST_MODE: ${{ matrix.mode }}
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.mode }}"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -219,6 +220,7 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
tracing_linux:
|
||||
name: Tracing ${{ matrix.browser }} ${{ matrix.channel }}
|
||||
|
|
@ -232,9 +234,11 @@ jobs:
|
|||
- browser: chromium
|
||||
channel: chromium-tip-of-tree
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
PWTEST_BOT_NAME: "tracing-${{ matrix.channel || matrix.browser }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -247,7 +251,6 @@ jobs:
|
|||
env:
|
||||
PWTEST_TRACE: 1
|
||||
PWTEST_CHANNEL: ${{ matrix.channel }}
|
||||
PWTEST_BLOB_REPORT_NAME: "tracing-${{ matrix.channel || matrix.browser }}"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -256,13 +259,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
chrome_stable_linux:
|
||||
name: "Chrome Stable (Linux)"
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
PWTEST_BOT_NAME: "chrome-stable-linux"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -273,7 +279,6 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: chrome
|
||||
PWTEST_BLOB_REPORT_NAME: "chrome-stable-linux"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -282,13 +287,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
chrome_stable_win:
|
||||
name: "Chrome Stable (Win)"
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "chrome-stable-windows"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -300,7 +308,6 @@ jobs:
|
|||
shell: bash
|
||||
env:
|
||||
PWTEST_CHANNEL: chrome
|
||||
PWTEST_BLOB_REPORT_NAME: "chrome-stable-windows"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -309,13 +316,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
chrome_stable_mac:
|
||||
name: "Chrome Stable (Mac)"
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "chrome-stable-mac"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -326,7 +336,6 @@ jobs:
|
|||
- run: npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: chrome
|
||||
PWTEST_BLOB_REPORT_NAME: "chrome-stable-mac"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -335,17 +344,21 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
chromium_tot:
|
||||
name: Chromium TOT ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
PWTEST_BOT_NAME: "tip-of-tree-${{ matrix.os }}"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-12, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -355,14 +368,8 @@ jobs:
|
|||
- run: npx playwright install --with-deps chromium-tip-of-tree
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
PWTEST_BLOB_REPORT_NAME: "tip-of-tree-${{ matrix.os }}"
|
||||
- run: npm run ctest
|
||||
if: matrix.os != 'ubuntu-20.04'
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
PWTEST_BLOB_REPORT_NAME: "tip-of-tree-${{ matrix.os }}"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -371,17 +378,21 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
chromium_tot_headed:
|
||||
name: Chromium TOT headed ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
PWTEST_BOT_NAME: "tip-of-tree-headed-${{ matrix.os }}"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -391,14 +402,8 @@ jobs:
|
|||
- run: npx playwright install --with-deps chromium-tip-of-tree
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest -- --headed
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
PWTEST_BLOB_REPORT_NAME: "tip-of-tree-headed-${{ matrix.os }}"
|
||||
- run: npm run ctest -- --headed
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
PWTEST_BLOB_REPORT_NAME: "tip-of-tree-headed-${{ matrix.os }}"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -407,13 +412,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
firefox_beta_linux:
|
||||
name: "Firefox Beta (Linux)"
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
PWTEST_BOT_NAME: "firefox-beta-linux"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -424,7 +432,6 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ftest
|
||||
env:
|
||||
PWTEST_CHANNEL: firefox-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "firefox-beta-linux"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -433,13 +440,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
firefox_beta_win:
|
||||
name: "Firefox Beta (Win)"
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "firefox-beta-windows"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -451,7 +461,6 @@ jobs:
|
|||
shell: bash
|
||||
env:
|
||||
PWTEST_CHANNEL: firefox-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "firefox-beta-windows"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -460,13 +469,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
firefox_beta_mac:
|
||||
name: "Firefox Beta (Mac)"
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "firefox-beta-mac"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -477,7 +489,6 @@ jobs:
|
|||
- run: npm run ftest
|
||||
env:
|
||||
PWTEST_CHANNEL: firefox-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "firefox-beta-mac"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -486,13 +497,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_stable_mac:
|
||||
name: "Edge Stable (Mac)"
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-stable-mac"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -503,7 +517,6 @@ jobs:
|
|||
- run: npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-stable-mac"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -512,13 +525,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_stable_win:
|
||||
name: "Edge Stable (Win)"
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-stable-windows"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -530,7 +546,6 @@ jobs:
|
|||
shell: bash
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-stable-windows"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -539,13 +554,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_stable_linux:
|
||||
name: "Edge Stable (Linux)"
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-stable-linux"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -556,7 +574,6 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-stable-linux"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -565,13 +582,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_beta_mac:
|
||||
name: "Edge Beta (Mac)"
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-beta-mac"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -582,7 +602,6 @@ jobs:
|
|||
- run: npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-beta-mac"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -591,13 +610,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_beta_win:
|
||||
name: "Edge Beta (Win)"
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-beta-windows"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -609,7 +631,6 @@ jobs:
|
|||
shell: bash
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-beta-windows"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -618,13 +639,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_beta_linux:
|
||||
name: "Edge Beta (Linux)"
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-beta-linux"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -635,7 +659,6 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-beta-linux"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -644,13 +667,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_dev_mac:
|
||||
name: "Edge Dev (Mac)"
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-dev-mac"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -661,7 +687,6 @@ jobs:
|
|||
- run: npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge-dev
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-dev-mac"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -670,13 +695,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_dev_win:
|
||||
name: "Edge Dev (Win)"
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-dev-windows"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -688,7 +716,6 @@ jobs:
|
|||
shell: bash
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge-dev
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-dev-windows"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -697,13 +724,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
edge_dev_linux:
|
||||
name: "Edge Dev (Linux)"
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
PWTEST_BOT_NAME: "edge-dev-linux"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -714,7 +744,6 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: msedge-dev
|
||||
PWTEST_BLOB_REPORT_NAME: "edge-dev-linux"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -723,13 +752,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
chrome_beta_linux:
|
||||
name: "Chrome Beta (Linux)"
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
PWTEST_BOT_NAME: "chrome-beta-linux"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -740,7 +772,6 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: chrome-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "chrome-beta-linux"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -749,13 +780,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
chrome_beta_win:
|
||||
name: "Chrome Beta (Win)"
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "chrome-beta-windows"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -767,7 +801,6 @@ jobs:
|
|||
shell: bash
|
||||
env:
|
||||
PWTEST_CHANNEL: chrome-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "chrome-beta-windows"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -776,13 +809,16 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
chrome_beta_mac:
|
||||
name: "Chrome Beta (Mac)"
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "chrome-beta-mac"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -793,7 +829,6 @@ jobs:
|
|||
- run: npm run ctest
|
||||
env:
|
||||
PWTEST_CHANNEL: chrome-beta
|
||||
PWTEST_BLOB_REPORT_NAME: "chrome-beta-mac"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
shell: bash
|
||||
|
|
@ -802,13 +837,14 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
||||
build-playwright-driver:
|
||||
name: "build-playwright-driver"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -819,9 +855,11 @@ jobs:
|
|||
test_linux_chromium_headless_new:
|
||||
name: Linux Chromium Headless New
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PWTEST_BOT_NAME: "headless-new"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
@ -833,7 +871,6 @@ jobs:
|
|||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test -- --project=chromium
|
||||
env:
|
||||
PLAYWRIGHT_CHROMIUM_USE_HEADLESS_NEW: 1
|
||||
PWTEST_BLOB_REPORT_NAME: "headless-new"
|
||||
- run: node tests/config/checkCoverage.js chromium
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always()
|
||||
|
|
@ -843,3 +880,4 @@ jobs:
|
|||
uses: ./.github/actions/upload-blob-report
|
||||
with:
|
||||
report_dir: blob-report
|
||||
job_name: ${{ env.PWTEST_BOT_NAME }}
|
||||
|
|
|
|||
10
.github/workflows/tests_service.yml
vendored
10
.github/workflows/tests_service.yml
vendored
|
|
@ -17,8 +17,8 @@ jobs:
|
|||
browser: [chromium, firefox, webkit]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- run: npm ci
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
env:
|
||||
PWTEST_MODE: service2
|
||||
PWTEST_TRACE: 1
|
||||
PWTEST_BLOB_REPORT_NAME: "${{ matrix.browser }}-${{ matrix.service-os }}-service"
|
||||
PWTEST_BOT_NAME: "${{ matrix.browser }}-${{ matrix.service-os }}-service"
|
||||
PLAYWRIGHT_SERVICE_ACCESS_KEY: ${{ secrets.PLAYWRIGHT_SERVICE_ACCESS_KEY }}
|
||||
PLAYWRIGHT_SERVICE_URL: ${{ secrets.PLAYWRIGHT_SERVICE_URL }}
|
||||
PLAYWRIGHT_SERVICE_OS: ${{ matrix.service-os }}
|
||||
|
|
@ -46,8 +46,8 @@ jobs:
|
|||
if: always()
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- run: npm ci
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
|
|
|
|||
10
.github/workflows/tests_stress.yml
vendored
10
.github/workflows/tests_stress.yml
vendored
|
|
@ -27,8 +27,8 @@ jobs:
|
|||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
- run: npm ci
|
||||
|
|
@ -40,13 +40,19 @@ jobs:
|
|||
if: always()
|
||||
- run: npm run stest browsers -- --project=chromium
|
||||
if: always()
|
||||
- run: npm run stest frames -- --project=chromium
|
||||
if: always()
|
||||
- run: npm run stest contexts -- --project=webkit
|
||||
if: always()
|
||||
- run: npm run stest browsers -- --project=webkit
|
||||
if: always()
|
||||
- run: npm run stest frames -- --project=webkit
|
||||
if: always()
|
||||
- run: npm run stest contexts -- --project=firefox
|
||||
if: always()
|
||||
- run: npm run stest browsers -- --project=firefox
|
||||
if: always()
|
||||
- run: npm run stest frames -- --project=firefox
|
||||
if: always()
|
||||
- run: npm run stest heap -- --project=chromium
|
||||
if: always()
|
||||
|
|
|
|||
4
.github/workflows/tests_video.yml
vendored
4
.github/workflows/tests_video.yml
vendored
|
|
@ -22,8 +22,8 @@ jobs:
|
|||
os: [ubuntu-20.04, ubuntu-22.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
|
|
|
|||
8
.github/workflows/tests_webview2.yml
vendored
8
.github/workflows/tests_webview2.yml
vendored
|
|
@ -25,13 +25,13 @@ jobs:
|
|||
name: WebView2
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- uses: actions/setup-dotnet@v2
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
dotnet-version: '8.0.x'
|
||||
- run: npm ci
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -9,7 +9,8 @@ node_modules/
|
|||
.vscode
|
||||
.idea
|
||||
yarn.lock
|
||||
/packages/playwright-core/src/generated/*
|
||||
/packages/playwright-core/src/generated
|
||||
/packages/playwright-ct-core/src/generated
|
||||
packages/*/lib/
|
||||
drivers/
|
||||
.android-sdk/
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ We strongly recommend that you open an issue before beginning any code modificat
|
|||
|
||||
### Getting Code
|
||||
|
||||
Make sure you're running Node.js 16+ and NPM 8+, to verify and upgrade NPM do:
|
||||
Make sure you're running Node.js 20 to verify and upgrade NPM do:
|
||||
|
||||
```bash
|
||||
node --version
|
||||
|
|
|
|||
|
|
@ -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://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 -->
|
||||
|
||||
## [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 -->120.0.6099.5<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->122.0.6261.57<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->118.0.1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->122.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.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
},
|
||||
"plugins": [
|
||||
["@babel/plugin-transform-typescript", { "allowDeclareFields": true } ],
|
||||
"@babel/plugin-proposal-export-namespace-from",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-logical-assignment-operators",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-transform-export-namespace-from",
|
||||
"@babel/plugin-transform-class-properties",
|
||||
"@babel/plugin-transform-logical-assignment-operators",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator",
|
||||
"@babel/plugin-transform-optional-chaining",
|
||||
"@babel/plugin-transform-modules-commonjs"
|
||||
],
|
||||
"ignore": [
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
REMOTE_URL="https://github.com/mozilla/gecko-dev"
|
||||
BASE_BRANCH="release"
|
||||
BASE_REVISION="f5bc1abb4f0841558f7531e0c15a7577d23ed21c"
|
||||
BASE_REVISION="7ab3cc0103090dd7bfa02e072a529b9fc784ab4e"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
class Helper {
|
||||
decorateAsEventEmitter(objectToDecorate) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
"use strict";
|
||||
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||
const { ChannelEventSinkFactory } = ChromeUtils.import("chrome://remote/content/cdp/observers/ChannelEventSink.jsm");
|
||||
|
||||
|
|
@ -232,8 +231,12 @@ class NetworkRequest {
|
|||
this._expectingResumedRequest = undefined;
|
||||
|
||||
if (headers) {
|
||||
for (const header of requestHeaders(this.httpChannel))
|
||||
for (const header of requestHeaders(this.httpChannel)) {
|
||||
// We cannot remove the "host" header.
|
||||
if (header.name.toLowerCase() === 'host')
|
||||
continue;
|
||||
this.httpChannel.setRequestHeader(header.name, '', false /* merge */);
|
||||
}
|
||||
for (const header of headers)
|
||||
this.httpChannel.setRequestHeader(header.name, header.value, false /* merge */);
|
||||
} else if (this._pageNetwork) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
|
||||
const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");
|
||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ var EXPORTED_SYMBOLS = ["Juggler", "JugglerFactory"];
|
|||
|
||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const {ComponentUtils} = ChromeUtils.import("resource://gre/modules/ComponentUtils.jsm");
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const {Dispatcher} = ChromeUtils.import("chrome://juggler/content/protocol/Dispatcher.js");
|
||||
const {BrowserHandler} = ChromeUtils.import("chrome://juggler/content/protocol/BrowserHandler.js");
|
||||
const {NetworkObserver} = ChromeUtils.import("chrome://juggler/content/NetworkObserver.js");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
const { Helper } = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { initialize } = ChromeUtils.import('chrome://juggler/content/content/main.js');
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
|
|
@ -462,6 +461,10 @@ class PageAgent {
|
|||
}
|
||||
|
||||
async _dispatchKeyEvent({type, keyCode, code, key, repeat, location, text}) {
|
||||
if (code === 'OSLeft')
|
||||
code = 'MetaLeft';
|
||||
else if (code === 'OSRight')
|
||||
code = 'MetaRight';
|
||||
const frame = this._frameTree.mainFrame();
|
||||
const tip = frame.textInputProcessor();
|
||||
if (key === 'Meta' && Services.appinfo.OS !== 'Darwin')
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ class Runtime {
|
|||
if (isWorker) {
|
||||
this._registerWorkerConsoleHandler();
|
||||
} else {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
this._registerConsoleServiceListener(Services);
|
||||
this._registerConsoleAPIListener(Services);
|
||||
}
|
||||
|
|
@ -240,8 +239,8 @@ class Runtime {
|
|||
return {success: true, obj: obj.promiseValue};
|
||||
if (obj.promiseState === 'rejected') {
|
||||
const debuggee = executionContext._debuggee;
|
||||
exceptionDetails.text = debuggee.executeInGlobalWithBindings('e.message', {e: obj.promiseReason}).return;
|
||||
exceptionDetails.stack = debuggee.executeInGlobalWithBindings('e.stack', {e: obj.promiseReason}).return;
|
||||
exceptionDetails.text = debuggee.executeInGlobalWithBindings('e.message', {e: obj.promiseReason}, {useInnerBindings: true}).return;
|
||||
exceptionDetails.stack = debuggee.executeInGlobalWithBindings('e.stack', {e: obj.promiseReason}, {useInnerBindings: true}).return;
|
||||
return {success: false, obj: null};
|
||||
}
|
||||
let resolve, reject;
|
||||
|
|
@ -268,8 +267,8 @@ class Runtime {
|
|||
return;
|
||||
};
|
||||
const debuggee = pendingPromise.executionContext._debuggee;
|
||||
pendingPromise.exceptionDetails.text = debuggee.executeInGlobalWithBindings('e.message', {e: obj.promiseReason}).return;
|
||||
pendingPromise.exceptionDetails.stack = debuggee.executeInGlobalWithBindings('e.stack', {e: obj.promiseReason}).return;
|
||||
pendingPromise.exceptionDetails.text = debuggee.executeInGlobalWithBindings('e.message', {e: obj.promiseReason}, {useInnerBindings: true}).return;
|
||||
pendingPromise.exceptionDetails.stack = debuggee.executeInGlobalWithBindings('e.stack', {e: obj.promiseReason}, {useInnerBindings: true}).return;
|
||||
pendingPromise.resolve({success: false, obj: null});
|
||||
}
|
||||
|
||||
|
|
@ -442,7 +441,7 @@ class ExecutionContext {
|
|||
_instanceOf(debuggerObj, rawObj, className) {
|
||||
if (this._domWindow)
|
||||
return rawObj instanceof this._domWindow[className];
|
||||
return this._debuggee.executeInGlobalWithBindings('o instanceof this[className]', {o: debuggerObj, className: this._debuggee.makeDebuggeeValue(className)}).return;
|
||||
return this._debuggee.executeInGlobalWithBindings('o instanceof this[className]', {o: debuggerObj, className: this._debuggee.makeDebuggeeValue(className)}, {useInnerBindings: true}).return;
|
||||
}
|
||||
|
||||
_createRemoteObject(debuggerObj) {
|
||||
|
|
@ -532,7 +531,7 @@ class ExecutionContext {
|
|||
}
|
||||
|
||||
_serialize(obj) {
|
||||
const result = this._debuggee.executeInGlobalWithBindings('stringify(e)', {e: obj, stringify: this._jsonStringifyObject});
|
||||
const result = this._debuggee.executeInGlobalWithBindings('stringify(e)', {e: obj, stringify: this._jsonStringifyObject}, {useInnerBindings: true});
|
||||
if (result.throw)
|
||||
throw new Error('Object is not serializable');
|
||||
return result.return === undefined ? undefined : JSON.parse(result.return);
|
||||
|
|
@ -564,9 +563,9 @@ class ExecutionContext {
|
|||
if (!completionValue)
|
||||
throw new Error('evaluation terminated');
|
||||
if (completionValue.throw) {
|
||||
if (this._debuggee.executeInGlobalWithBindings('e instanceof Error', {e: completionValue.throw}).return) {
|
||||
exceptionDetails.text = this._debuggee.executeInGlobalWithBindings('e.message', {e: completionValue.throw}).return;
|
||||
exceptionDetails.stack = this._debuggee.executeInGlobalWithBindings('e.stack', {e: completionValue.throw}).return;
|
||||
if (this._debuggee.executeInGlobalWithBindings('e instanceof Error', {e: completionValue.throw}, {useInnerBindings: true}).return) {
|
||||
exceptionDetails.text = this._debuggee.executeInGlobalWithBindings('e.message', {e: completionValue.throw}, {useInnerBindings: true}).return;
|
||||
exceptionDetails.stack = this._debuggee.executeInGlobalWithBindings('e.stack', {e: completionValue.throw}, {useInnerBindings: true}).return;
|
||||
} else {
|
||||
exceptionDetails.value = this._serialize(completionValue.throw);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {FrameTree} = ChromeUtils.import('chrome://juggler/content/content/FrameTree.js');
|
||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
"use strict";
|
||||
|
||||
const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {PageHandler} = ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.js");
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
"use strict";
|
||||
|
||||
const {Helper, EventWatcher} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||
const {NetworkObserver, PageNetwork} = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js');
|
||||
const {PageTarget} = ChromeUtils.import('chrome://juggler/content/TargetRegistry.js');
|
||||
|
|
@ -395,7 +394,7 @@ class PageHandler {
|
|||
'nsIReferrerInfo',
|
||||
'init'
|
||||
);
|
||||
referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, referrerURI);
|
||||
referrerInfo = new ReferrerInfo(Ci.nsIReferrerInfo.UNSAFE_URL, true, referrerURI);
|
||||
} catch (e) {
|
||||
throw new Error(`Invalid referer: "${referer}"`);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,8 @@
|
|||
// =================================================================
|
||||
// THESE ARE THE PROPERTIES THAT MUST BE ENABLED FOR JUGGLER TO WORK
|
||||
// =================================================================
|
||||
pref("dom.input_events.security.minNumTicks", 0);
|
||||
pref("dom.input_events.security.minTimeElapsedInMS", 0);
|
||||
|
||||
pref("datareporting.policy.dataSubmissionEnabled", false);
|
||||
pref("datareporting.policy.dataSubmissionPolicyAccepted", false);
|
||||
|
|
@ -305,3 +307,5 @@ pref("extensions.blocklist.enabled", false);
|
|||
// Force Firefox Devtools to open in a separate window.
|
||||
pref("devtools.toolbox.host", "window");
|
||||
|
||||
// Disable auto translations
|
||||
pref("browser.translations.enable", false);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
REMOTE_URL="https://github.com/WebKit/WebKit.git"
|
||||
BASE_BRANCH="main"
|
||||
BASE_REVISION="30884546903f1ba774adb0cbef1adc91c6c53c64"
|
||||
BASE_REVISION="bc0bc692bc9e368bbd9d530322db73b374cd6268"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
function getWebkitCheckoutPath() {
|
||||
echo ${WK_CHECKOUT_PATH:-"$HOME/webkit"}
|
||||
}
|
||||
|
||||
function runOSX() {
|
||||
# if script is run as-is
|
||||
if [[ -f "${SCRIPT_PATH}/EXPECTED_BUILDS" && -n "$WK_CHECKOUT_PATH" && -d "$WK_CHECKOUT_PATH/WebKitBuild/Release/Playwright.app" ]]; then
|
||||
WK_CHECKOUT_PATH=$(getWebkitCheckoutPath)
|
||||
if [[ -f "${SCRIPT_PATH}/EXPECTED_BUILDS" && -d "$WK_CHECKOUT_PATH/WebKitBuild/Release/Playwright.app" ]]; then
|
||||
DYLIB_PATH="$WK_CHECKOUT_PATH/WebKitBuild/Release"
|
||||
elif [[ -f "${SCRIPT_PATH}/EXPECTED_BUILDS" && -d "$HOME/webkit/WebKitBuild/Release/Playwright.app" ]]; then
|
||||
DYLIB_PATH="$HOME/webkit/WebKitBuild/Release"
|
||||
elif [[ -d $SCRIPT_PATH/Playwright.app ]]; then
|
||||
DYLIB_PATH="$SCRIPT_PATH"
|
||||
elif [[ -d $SCRIPT_PATH/WebKitBuild/Release/Playwright.app ]]; then
|
||||
|
|
@ -23,29 +26,30 @@ function runLinux() {
|
|||
GIO_DIR="";
|
||||
LD_PATH="";
|
||||
BUNDLE_DIR="";
|
||||
DEPENDENCIES_FOLDER="DependenciesGTK";
|
||||
DEPENDENCIES_FOLDER="WebKitBuild/DependenciesGTK";
|
||||
MINIBROWSER_FOLDER="minibrowser-gtk";
|
||||
BUILD_FOLDER="WebKitBuild/GTK";
|
||||
if [[ "$*" == *--headless* ]]; then
|
||||
DEPENDENCIES_FOLDER="DependenciesWPE";
|
||||
DEPENDENCIES_FOLDER="WebKitBuild/DependenciesWPE";
|
||||
MINIBROWSER_FOLDER="minibrowser-wpe";
|
||||
BUILD_FOLDER="WebKitBuild/WPE";
|
||||
fi
|
||||
# Setting extra environment variables like LD_LIBRARY_PATH or WEBKIT_INJECTED_BUNDLE_PATH
|
||||
# is only needed when calling MiniBrowser from the build folder. The MiniBrowser from
|
||||
# the zip bundle wrapper already sets itself the needed env variables.
|
||||
WK_CHECKOUT_PATH=$(getWebkitCheckoutPath)
|
||||
if [[ -d $SCRIPT_PATH/$MINIBROWSER_FOLDER ]]; then
|
||||
MINIBROWSER="$SCRIPT_PATH/$MINIBROWSER_FOLDER/MiniBrowser"
|
||||
elif [[ -d $HOME/webkit/$BUILD_FOLDER ]]; then
|
||||
LD_PATH="$HOME/webkit/$BUILD_FOLDER/$DEPENDENCIES_FOLDER/Root/lib:$SCRIPT_PATH/checkout/$BUILD_FOLDER/Release/bin"
|
||||
GIO_DIR="$HOME/webkit/$BUILD_FOLDER/$DEPENDENCIES_FOLDER/Root/lib/gio/modules"
|
||||
BUNDLE_DIR="$HOME/webkit/$BUILD_FOLDER/Release/lib"
|
||||
MINIBROWSER="$HOME/webkit/$BUILD_FOLDER/Release/bin/MiniBrowser"
|
||||
elif [[ -d $WK_CHECKOUT_PATH/$BUILD_FOLDER ]]; then
|
||||
LD_PATH="$WK_CHECKOUT_PATH/$DEPENDENCIES_FOLDER/Root/lib:$SCRIPT_PATH/checkout/$BUILD_FOLDER/Release/bin"
|
||||
GIO_DIR="$WK_CHECKOUT_PATH/$DEPENDENCIES_FOLDER/Root/lib/gio/modules"
|
||||
BUNDLE_DIR="$WK_CHECKOUT_PATH/$BUILD_FOLDER/Release/lib"
|
||||
MINIBROWSER="$WK_CHECKOUT_PATH/$BUILD_FOLDER/Release/bin/MiniBrowser"
|
||||
elif [[ -f $SCRIPT_PATH/MiniBrowser ]]; then
|
||||
MINIBROWSER="$SCRIPT_PATH/MiniBrowser"
|
||||
elif [[ -d $SCRIPT_PATH/$BUILD_FOLDER ]]; then
|
||||
LD_PATH="$SCRIPT_PATH/$BUILD_FOLDER/$DEPENDENCIES_FOLDER/Root/lib:$SCRIPT_PATH/$BUILD_FOLDER/Release/bin"
|
||||
GIO_DIR="$SCRIPT_PATH/$BUILD_FOLDER/$DEPENDENCIES_FOLDER/Root/lib/gio/modules"
|
||||
LD_PATH="$SCRIPT_PATH/$DEPENDENCIES_FOLDER/Root/lib:$SCRIPT_PATH/$BUILD_FOLDER/Release/bin"
|
||||
GIO_DIR="$SCRIPT_PATH/$DEPENDENCIES_FOLDER/Root/lib/gio/modules"
|
||||
BUNDLE_DIR="$SCRIPT_PATH/$BUILD_FOLDER/Release/lib"
|
||||
MINIBROWSER="$SCRIPT_PATH/$BUILD_FOLDER/Release/bin/MiniBrowser"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ title: "Auto-waiting"
|
|||
Playwright performs a range of actionability checks on the elements before making actions to ensure these actions
|
||||
behave as expected. It auto-waits for all the relevant checks to pass and only then performs the requested action. If the required checks do not pass within the given `timeout`, action fails with the `TimeoutError`.
|
||||
|
||||
For example, for [`method: Page.click`], Playwright will ensure that:
|
||||
- element is [Attached] to the DOM
|
||||
For example, for [`method: Locator.click`], Playwright will ensure that:
|
||||
- locator resolves to an exactly one element
|
||||
- element is [Visible]
|
||||
- element is [Stable], as in not animating or completed animation
|
||||
- element [Receives Events], as in not obscured by other elements
|
||||
|
|
@ -17,72 +17,75 @@ For example, for [`method: Page.click`], Playwright will ensure that:
|
|||
|
||||
Here is the complete list of actionability checks performed for each action:
|
||||
|
||||
| Action | [Attached] | [Visible] | [Stable] | [Receives Events] | [Enabled] | [Editable] |
|
||||
| :- | :-: | :-: | :-: | :-: | :-: | :-: |
|
||||
| check | Yes | Yes | Yes | Yes | Yes | - |
|
||||
| click | Yes | Yes | Yes | Yes | Yes | - |
|
||||
| dblclick | Yes | Yes | Yes | Yes | Yes | - |
|
||||
| setChecked | Yes | Yes | Yes | Yes | Yes | - |
|
||||
| tap | Yes | Yes | Yes | Yes | Yes | - |
|
||||
| uncheck | Yes | Yes | Yes | Yes | Yes | - |
|
||||
| hover | Yes | Yes | Yes | Yes | - | - |
|
||||
| scrollIntoViewIfNeeded | Yes | - | Yes | - | - | - |
|
||||
| screenshot | Yes | Yes | Yes | - | - | - |
|
||||
| fill | Yes | Yes | - | - | Yes | Yes |
|
||||
| selectText | Yes | Yes | - | - | - | - |
|
||||
| dispatchEvent | Yes | - | - | - | - | - |
|
||||
| focus | Yes | - | - | - | - | - |
|
||||
| getAttribute | Yes | - | - | - | - | - |
|
||||
| innerText | Yes | - | - | - | - | - |
|
||||
| innerHTML | Yes | - | - | - | - | - |
|
||||
| press | Yes | - | - | - | - | - |
|
||||
| setInputFiles | Yes | - | - | - | - | - |
|
||||
| selectOption | Yes | Yes | - | - | Yes | - |
|
||||
| textContent | Yes | - | - | - | - | - |
|
||||
| type | Yes | - | - | - | - | - |
|
||||
|
||||
<br/>
|
||||
| Action | [Visible] | [Stable] | [Receives Events] | [Enabled] | [Editable] |
|
||||
| :- | :-: | :-: | :-: | :-: | :-: |
|
||||
| [`method: Locator.check`] | Yes | Yes | Yes | Yes | - |
|
||||
| [`method: Locator.click`] | Yes | Yes | Yes | Yes | - |
|
||||
| [`method: Locator.dblclick`] | Yes | Yes | Yes | Yes | - |
|
||||
| [`method: Locator.setChecked`] | Yes | Yes | Yes | Yes | - |
|
||||
| [`method: Locator.tap`] | Yes | Yes | Yes | Yes | - |
|
||||
| [`method: Locator.uncheck`] | Yes | Yes | Yes | Yes | - |
|
||||
| [`method: Locator.hover`] | Yes | Yes | Yes | - | - |
|
||||
| [`method: Locator.dragTo`] | Yes | Yes | Yes | - | - |
|
||||
| [`method: Locator.screenshot`] | Yes | Yes | - | - | - |
|
||||
| [`method: Locator.fill`] | Yes | - | - | Yes | Yes |
|
||||
| [`method: Locator.clear`] | Yes | - | - | Yes | Yes |
|
||||
| [`method: Locator.selectOption`] | Yes | - | - | Yes | - |
|
||||
| [`method: Locator.selectText`] | Yes | - | - | - | - |
|
||||
| [`method: Locator.scrollIntoViewIfNeeded`] | - | Yes | - | - | - |
|
||||
| [`method: Locator.blur`] | - | - | - | - | - |
|
||||
| [`method: Locator.dispatchEvent`] | - | - | - | - | - |
|
||||
| [`method: Locator.focus`] | - | - | - | - | - |
|
||||
| [`method: Locator.press`] | - | - | - | - | - |
|
||||
| [`method: Locator.pressSequentially`] | - | - | - | - | - |
|
||||
| [`method: Locator.setInputFiles`] | - | - | - | - | - |
|
||||
|
||||
## Forcing actions
|
||||
|
||||
Some actions like [`method: Page.click`] support `force` option that disables non-essential actionability checks,
|
||||
for example passing truthy `force` to [`method: Page.click`] method will not check that the target element actually
|
||||
Some actions like [`method: Locator.click`] support `force` option that disables non-essential actionability checks,
|
||||
for example passing truthy `force` to [`method: Locator.click`] method will not check that the target element actually
|
||||
receives click events.
|
||||
|
||||
## Assertions
|
||||
|
||||
You can check the actionability state of the element using one of the following methods as well. This is typically
|
||||
not necessary, but it helps writing assertive tests that ensure that after certain actions, elements reach
|
||||
actionable state:
|
||||
Playwright includes auto-retrying assertions that remove flakiness by waiting until the condition is met, similarly to auto-waiting before actions.
|
||||
|
||||
- [`method: ElementHandle.isChecked`]
|
||||
- [`method: ElementHandle.isDisabled`]
|
||||
- [`method: ElementHandle.isEditable`]
|
||||
- [`method: ElementHandle.isEnabled`]
|
||||
- [`method: ElementHandle.isHidden`]
|
||||
- [`method: ElementHandle.isVisible`]
|
||||
- [`method: Page.isChecked`]
|
||||
- [`method: Page.isDisabled`]
|
||||
- [`method: Page.isEditable`]
|
||||
- [`method: Page.isEnabled`]
|
||||
- [`method: Page.isHidden`]
|
||||
- [`method: Page.isVisible`]
|
||||
- [`method: Locator.isChecked`]
|
||||
- [`method: Locator.isDisabled`]
|
||||
- [`method: Locator.isEditable`]
|
||||
- [`method: Locator.isEnabled`]
|
||||
- [`method: Locator.isHidden`]
|
||||
- [`method: Locator.isVisible`]
|
||||
| Assertion | Description |
|
||||
| :- | :- |
|
||||
| [`method: LocatorAssertions.toBeAttached`] | Element is attached |
|
||||
| [`method: LocatorAssertions.toBeChecked`] | Checkbox is checked |
|
||||
| [`method: LocatorAssertions.toBeDisabled`] | Element is disabled |
|
||||
| [`method: LocatorAssertions.toBeEditable`] | Element is editable |
|
||||
| [`method: LocatorAssertions.toBeEmpty`] | Container is empty |
|
||||
| [`method: LocatorAssertions.toBeEnabled`] | Element is enabled |
|
||||
| [`method: LocatorAssertions.toBeFocused`] | Element is focused |
|
||||
| [`method: LocatorAssertions.toBeHidden`] | Element is not visible |
|
||||
| [`method: LocatorAssertions.toBeInViewport`] | Element intersects viewport |
|
||||
| [`method: LocatorAssertions.toBeVisible`] | Element is visible |
|
||||
| [`method: LocatorAssertions.toContainText`] | Element contains text |
|
||||
| [`method: LocatorAssertions.toHaveAttribute`] | Element has a DOM attribute |
|
||||
| [`method: LocatorAssertions.toHaveClass`] | Element has a class property |
|
||||
| [`method: LocatorAssertions.toHaveCount`] | List has exact number of children |
|
||||
| [`method: LocatorAssertions.toHaveCSS`] | Element has CSS property |
|
||||
| [`method: LocatorAssertions.toHaveId`] | Element has an ID |
|
||||
| [`method: LocatorAssertions.toHaveJSProperty`] | Element has a JavaScript property |
|
||||
| [`method: LocatorAssertions.toHaveText`] | Element matches text |
|
||||
| [`method: LocatorAssertions.toHaveValue`] | Input has a value |
|
||||
| [`method: LocatorAssertions.toHaveValues`] | Select has options selected |
|
||||
| [`method: PageAssertions.toHaveTitle`] | Page has a title |
|
||||
| [`method: PageAssertions.toHaveURL`] | Page has a URL |
|
||||
| [`method: APIResponseAssertions.toBeOK`] | Response has an OK status |
|
||||
|
||||
<br/>
|
||||
|
||||
## Attached
|
||||
|
||||
Element is considered attached when it is [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.
|
||||
Learn more in the [assertions guide](./test-assertions.md).
|
||||
|
||||
## Visible
|
||||
|
||||
Element is considered visible when it has non-empty bounding box and does not have `visibility:hidden` computed style. Note that elements of zero size or with `display:none` are not considered visible.
|
||||
Element is considered visible when it has non-empty bounding box and does not have `visibility:hidden` computed style.
|
||||
|
||||
Note that according to this definition:
|
||||
* Elements of zero size **are not** considered visible.
|
||||
* Elements with `display:none` **are not** considered visible.
|
||||
* Elements with `opacity:0` **are** considered visible.
|
||||
|
||||
## Stable
|
||||
|
||||
|
|
@ -101,7 +104,7 @@ Element is considered editable when it is [enabled] and does not have `readonly`
|
|||
Element is considered receiving pointer events when it is the hit target of the pointer event at the action point. For example, when clicking at the point `(10;10)`, Playwright checks whether some other element (usually an overlay) will instead capture the click at `(10;10)`.
|
||||
|
||||
|
||||
For example, consider a scenario where Playwright will click `Sign Up` button regardless of when the [`method: Page.click`] call was made:
|
||||
For example, consider a scenario where Playwright will click `Sign Up` button regardless of when the [`method: Locator.click`] call was made:
|
||||
- page is checking that user name is unique and `Sign Up` button is disabled;
|
||||
- after checking with the server, the disabled `Sign Up` button is replaced with another one that is now enabled.
|
||||
|
||||
|
|
@ -110,4 +113,3 @@ For example, consider a scenario where Playwright will click `Sign Up` button re
|
|||
[Enabled]: #enabled "Enabled"
|
||||
[Editable]: #editable "Editable"
|
||||
[Receives Events]: #receives-events "Receives Events"
|
||||
[Attached]: #attached "Attached"
|
||||
|
|
|
|||
|
|
@ -48,6 +48,24 @@ export default defineConfig({
|
|||
});
|
||||
```
|
||||
|
||||
**Proxy configuration**
|
||||
|
||||
If your tests need to run behind a proxy, you can specify this in the config and the `request` fixture
|
||||
will pick it up automatically:
|
||||
|
||||
```js title="playwright.config.ts"
|
||||
import { defineConfig } from '@playwright/test';
|
||||
export default defineConfig({
|
||||
use: {
|
||||
proxy: {
|
||||
server: 'http://my-proxy:8080',
|
||||
username: 'user',
|
||||
password: 'secret'
|
||||
},
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Writing tests
|
||||
|
||||
Playwright Test comes with the built-in `request` fixture that respects configuration options like `baseURL` or `extraHTTPHeaders` we specified and is ready to send some requests.
|
||||
|
|
@ -313,7 +331,7 @@ test('context request will share cookie storage with its browser context', async
|
|||
[name, value])
|
||||
)).toEqual(responseCookies);
|
||||
|
||||
route.fulfill({
|
||||
await route.fulfill({
|
||||
response,
|
||||
headers: { ...responseHeaders, foo: 'bar' },
|
||||
});
|
||||
|
|
@ -357,7 +375,7 @@ test('global context request has isolated cookie storage', async ({
|
|||
new Map(contextCookies2.map(({ name, value }) => [name, value]))
|
||||
).toEqual(responseCookies);
|
||||
|
||||
route.fulfill({
|
||||
await route.fulfill({
|
||||
response,
|
||||
headers: { ...responseHeaders, foo: 'bar' },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -74,12 +74,6 @@ const { _android: android } = require('playwright');
|
|||
})();
|
||||
```
|
||||
|
||||
Note that since you don't need Playwright to install web browsers when testing Android, you can omit browser download via setting the following environment variable when installing Playwright:
|
||||
|
||||
```bash js
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright
|
||||
```
|
||||
|
||||
## async method: Android.connect
|
||||
* since: v1.28
|
||||
- returns: <[AndroidDevice]>
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ The reason to be reported to the operations interrupted by the context closure.
|
|||
- alias-java: consoleMessage
|
||||
- argument: <[ConsoleMessage]>
|
||||
|
||||
Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also emitted if the page throws an error or a warning.
|
||||
Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.
|
||||
|
||||
The arguments passed into `console.log` and the page are available on the [ConsoleMessage] event handler argument.
|
||||
|
||||
|
|
@ -1120,11 +1120,11 @@ await browser.CloseAsync();
|
|||
It is possible to examine the request to decide the route action. For example, mocking all requests that contain some post data, and leaving all other requests as is:
|
||||
|
||||
```js
|
||||
await context.route('/api/**', route => {
|
||||
await context.route('/api/**', async route => {
|
||||
if (route.request().postData().includes('my-string'))
|
||||
route.fulfill({ body: 'mocked-data' });
|
||||
await route.fulfill({ body: 'mocked-data' });
|
||||
else
|
||||
route.continue();
|
||||
await route.continue();
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -1138,16 +1138,16 @@ context.route("/api/**", route -> {
|
|||
```
|
||||
|
||||
```python async
|
||||
def handle_route(route):
|
||||
async def handle_route(route: Route):
|
||||
if ("my-string" in route.request.post_data):
|
||||
route.fulfill(body="mocked-data")
|
||||
await route.fulfill(body="mocked-data")
|
||||
else:
|
||||
route.continue_()
|
||||
await route.continue_()
|
||||
await context.route("/api/**", handle_route)
|
||||
```
|
||||
|
||||
```python sync
|
||||
def handle_route(route):
|
||||
def handle_route(route: Route):
|
||||
if ("my-string" in route.request.post_data):
|
||||
route.fulfill(body="mocked-data")
|
||||
else:
|
||||
|
|
@ -1408,6 +1408,14 @@ Returns storage state for this browser context, contains current cookies and loc
|
|||
* since: v1.12
|
||||
- type: <[Tracing]>
|
||||
|
||||
## async method: BrowserContext.unrouteAll
|
||||
* since: v1.41
|
||||
|
||||
Removes all routes created with [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`].
|
||||
|
||||
### option: BrowserContext.unrouteAll.behavior = %%-unroute-all-options-behavior-%%
|
||||
* since: v1.41
|
||||
|
||||
## async method: BrowserContext.unroute
|
||||
* since: v1.8
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ Upon successful cancellations, `download.failure()` would resolve to `'canceled'
|
|||
* langs: java, js, csharp
|
||||
- returns: <[Readable]>
|
||||
|
||||
Returns readable stream for current download or `null` if download failed.
|
||||
Returns a readable stream for a successful download, or throws for a failed/canceled download.
|
||||
|
||||
## async method: Download.delete
|
||||
* since: v1.8
|
||||
|
|
@ -95,8 +95,7 @@ Get the page that the download belongs to.
|
|||
* since: v1.8
|
||||
- returns: <[path]>
|
||||
|
||||
Returns path to the downloaded file in case of successful download. The method will
|
||||
wait for the download to finish if necessary. The method throws when connected remotely.
|
||||
Returns path to the downloaded file for a successful download, or throws for a failed/canceled download. The method will wait for the download to finish if necessary. The method throws when connected remotely.
|
||||
|
||||
Note that the download's file name is a random GUID, use [`method: Download.suggestedFilename`]
|
||||
to get suggested file name.
|
||||
|
|
|
|||
|
|
@ -40,12 +40,6 @@ const { _electron: electron } = require('playwright');
|
|||
})();
|
||||
```
|
||||
|
||||
Note that since you don't need Playwright to install web browsers when testing Electron, you can omit browser download via setting the following environment variable when installing Playwright:
|
||||
|
||||
```bash js
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright
|
||||
```
|
||||
|
||||
**Supported Electron versions are:**
|
||||
* v12.2.0+
|
||||
* v13.4.0+
|
||||
|
|
|
|||
|
|
@ -39,7 +39,27 @@ const { _electron: electron } = require('playwright');
|
|||
## event: ElectronApplication.close
|
||||
* since: v1.9
|
||||
|
||||
This event is issued when the application closes.
|
||||
This event is issued when the application process has been terminated.
|
||||
|
||||
## event: ElectronApplication.console
|
||||
* since: v1.42
|
||||
- argument: <[ConsoleMessage]>
|
||||
|
||||
Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or `console.dir`.
|
||||
|
||||
The arguments passed into `console.log` are available on the [ConsoleMessage] event handler argument.
|
||||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
electronApp.on('console', async msg => {
|
||||
const values = [];
|
||||
for (const arg of msg.args())
|
||||
values.push(await arg.jsonValue());
|
||||
console.log(...values);
|
||||
});
|
||||
await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' }));
|
||||
```
|
||||
|
||||
## event: ElectronApplication.window
|
||||
* since: v1.9
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ await page.Mouse.ClickAsync(box.X + box.Width / 2, box.Y + box.Height / 2);
|
|||
|
||||
## async method: ElementHandle.check
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.check`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
This method checks the element by performing the following steps:
|
||||
1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already
|
||||
|
|
@ -191,6 +192,7 @@ When all steps combined have not finished during the specified [`option: timeout
|
|||
|
||||
## async method: ElementHandle.click
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.click`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
This method clicks the element by performing the following steps:
|
||||
1. Wait for [actionability](../actionability.md) checks on the element, unless [`option: force`] option is set.
|
||||
|
|
@ -241,6 +243,7 @@ Returns the content frame for element handles referencing iframe nodes, or `null
|
|||
|
||||
## async method: ElementHandle.dblclick
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.dblclick`] instead. Read more about [locators](../locators.md).
|
||||
* langs:
|
||||
- alias-csharp: DblClickAsync
|
||||
|
||||
|
|
@ -289,6 +292,7 @@ When all steps combined have not finished during the specified [`option: timeout
|
|||
|
||||
## async method: ElementHandle.dispatchEvent
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.dispatchEvent`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element, `click`
|
||||
is dispatched. This is equivalent to calling
|
||||
|
|
@ -322,13 +326,16 @@ default.
|
|||
|
||||
Since [`param: eventInit`] is event-specific, please refer to the events documentation for the lists of initial
|
||||
properties:
|
||||
* [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)
|
||||
* [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)
|
||||
* [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)
|
||||
* [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
|
||||
* [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)
|
||||
* [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)
|
||||
* [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)
|
||||
* [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)
|
||||
* [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)
|
||||
* [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
|
||||
* [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)
|
||||
|
||||
You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:
|
||||
|
||||
|
|
@ -380,6 +387,9 @@ Optional event-specific initialization properties.
|
|||
|
||||
## async method: ElementHandle.evalOnSelector
|
||||
* since: v1.9
|
||||
* discouraged: This method does not wait for the element to pass actionability
|
||||
checks and therefore can lead to the flaky tests. Use [`method: Locator.evaluate`],
|
||||
other [Locator] helper methods or web-first assertions instead.
|
||||
* langs:
|
||||
- alias-python: eval_on_selector
|
||||
- alias-js: $eval
|
||||
|
|
@ -442,6 +452,8 @@ Optional argument to pass to [`param: expression`].
|
|||
|
||||
## async method: ElementHandle.evalOnSelectorAll
|
||||
* since: v1.9
|
||||
* discouraged: In most cases, [`method: Locator.evaluateAll`],
|
||||
other [Locator] helper methods and web-first assertions do a better job.
|
||||
* langs:
|
||||
- alias-python: eval_on_selector_all
|
||||
- alias-js: $$eval
|
||||
|
|
@ -508,6 +520,7 @@ Optional argument to pass to [`param: expression`].
|
|||
|
||||
## async method: ElementHandle.fill
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.fill`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
This method waits for [actionability](../actionability.md) checks, focuses the element, fills it and triggers an `input` event after filling. Note that you can pass an empty string to clear the input field.
|
||||
|
||||
|
|
@ -535,11 +548,13 @@ Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element.
|
|||
|
||||
## async method: ElementHandle.focus
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.focus`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
|
||||
|
||||
## async method: ElementHandle.getAttribute
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.getAttribute`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[null]|[string]>
|
||||
|
||||
Returns element attribute value.
|
||||
|
|
@ -552,6 +567,7 @@ Attribute name to get the value for.
|
|||
|
||||
## async method: ElementHandle.hover
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.hover`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
This method hovers over the element by performing the following steps:
|
||||
1. Wait for [actionability](../actionability.md) checks on the element, unless [`option: force`] option is set.
|
||||
|
|
@ -587,18 +603,21 @@ When all steps combined have not finished during the specified [`option: timeout
|
|||
|
||||
## async method: ElementHandle.innerHTML
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.innerHTML`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[string]>
|
||||
|
||||
Returns the `element.innerHTML`.
|
||||
|
||||
## async method: ElementHandle.innerText
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.innerText`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[string]>
|
||||
|
||||
Returns the `element.innerText`.
|
||||
|
||||
## async method: ElementHandle.inputValue
|
||||
* since: v1.13
|
||||
* discouraged: Use locator-based [`method: Locator.inputValue`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[string]>
|
||||
|
||||
Returns `input.value` for the selected `<input>` or `<textarea>` or `<select>` element.
|
||||
|
|
@ -613,36 +632,42 @@ Throws for non-input elements. However, if the element is inside the `<label>` e
|
|||
|
||||
## async method: ElementHandle.isChecked
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.isChecked`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
|
||||
## async method: ElementHandle.isDisabled
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.isDisabled`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is disabled, the opposite of [enabled](../actionability.md#enabled).
|
||||
|
||||
## async method: ElementHandle.isEditable
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.isEditable`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is [editable](../actionability.md#editable).
|
||||
|
||||
## async method: ElementHandle.isEnabled
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.isEnabled`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is [enabled](../actionability.md#enabled).
|
||||
|
||||
## async method: ElementHandle.isHidden
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.isHidden`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is hidden, the opposite of [visible](../actionability.md#visible).
|
||||
|
||||
## async method: ElementHandle.isVisible
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.isVisible`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is [visible](../actionability.md#visible).
|
||||
|
|
@ -655,6 +680,7 @@ Returns the frame containing the given element.
|
|||
|
||||
## async method: ElementHandle.press
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.press`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
Focuses the element, and then uses [`method: Keyboard.down`] and [`method: Keyboard.up`].
|
||||
|
||||
|
|
@ -673,7 +699,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i
|
|||
If [`param: key`] is a single character, it is case-sensitive, so the values `a` and `A` will generate different
|
||||
respective texts.
|
||||
|
||||
Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
modifier, modifier is pressed and being held while the subsequent key is being pressed.
|
||||
|
||||
### param: ElementHandle.press.key
|
||||
|
|
@ -699,6 +725,7 @@ Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
|
|||
|
||||
## async method: ElementHandle.querySelector
|
||||
* since: v1.9
|
||||
* discouraged: Use locator-based [`method: Page.locator`] instead. Read more about [locators](../locators.md).
|
||||
* langs:
|
||||
- alias-python: query_selector
|
||||
- alias-js: $
|
||||
|
|
@ -712,6 +739,7 @@ returns `null`.
|
|||
|
||||
## async method: ElementHandle.querySelectorAll
|
||||
* since: v1.9
|
||||
* discouraged: Use locator-based [`method: Page.locator`] instead. Read more about [locators](../locators.md).
|
||||
* langs:
|
||||
- alias-python: query_selector_all
|
||||
- alias-js: $$
|
||||
|
|
@ -725,6 +753,7 @@ returns empty array.
|
|||
|
||||
## async method: ElementHandle.screenshot
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.screenshot`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[Buffer]>
|
||||
|
||||
This method captures a screenshot of the page, clipped to the size and position of this particular element. If the element is covered by other elements, it will not be actually visible on the screenshot. If the element is a scrollable container, only the currently scrolled content will be visible on the screenshot.
|
||||
|
|
@ -746,7 +775,11 @@ Returns the buffer with the captured screenshot.
|
|||
### option: ElementHandle.screenshot.maskColor = %%-screenshot-option-mask-color-%%
|
||||
* since: v1.34
|
||||
|
||||
### option: ElementHandle.screenshot.style = %%-screenshot-option-style-%%
|
||||
* since: v1.41
|
||||
|
||||
## async method: ElementHandle.scrollIntoViewIfNeeded
|
||||
* discouraged: Use locator-based [`method: Locator.scrollIntoViewIfNeeded`] instead. Read more about [locators](../locators.md).
|
||||
* since: v1.8
|
||||
|
||||
This method waits for [actionability](../actionability.md) checks, then tries to scroll element into view, unless it is
|
||||
|
|
@ -764,6 +797,7 @@ Throws when `elementHandle` does not point to an element
|
|||
|
||||
## async method: ElementHandle.selectOption
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.selectOption`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[Array]<[string]>>
|
||||
|
||||
This method waits for [actionability](../actionability.md) checks, waits until all specified options are present in the `<select>` element and selects these options.
|
||||
|
|
@ -857,6 +891,7 @@ await handle.SelectOptionAsync(new[] {
|
|||
|
||||
## async method: ElementHandle.selectText
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.selectText`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
This method waits for [actionability](../actionability.md) checks, then focuses the element and selects all its text
|
||||
content.
|
||||
|
|
@ -873,6 +908,7 @@ If the element is inside the `<label>` element that has an associated [control](
|
|||
* since: v1.8
|
||||
|
||||
## async method: ElementHandle.setChecked
|
||||
* discouraged: Use locator-based [`method: Locator.setChecked`] instead. Read more about [locators](../locators.md).
|
||||
* since: v1.15
|
||||
|
||||
This method checks or unchecks an element by performing the following steps:
|
||||
|
|
@ -911,6 +947,7 @@ When all steps combined have not finished during the specified [`option: timeout
|
|||
|
||||
## async method: ElementHandle.setInputFiles
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.setInputFiles`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they
|
||||
are resolved relative to the current working directory. For empty array, clears the selected files.
|
||||
|
|
@ -932,6 +969,7 @@ This method expects [ElementHandle] to point to an
|
|||
|
||||
## async method: ElementHandle.tap
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.tap`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
This method taps the element by performing the following steps:
|
||||
1. Wait for [actionability](../actionability.md) checks on the element, unless [`option: force`] option is set.
|
||||
|
|
@ -971,6 +1009,7 @@ When all steps combined have not finished during the specified [`option: timeout
|
|||
|
||||
## async method: ElementHandle.textContent
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.textContent`] instead. Read more about [locators](../locators.md).
|
||||
- returns: <[null]|[string]>
|
||||
|
||||
Returns the `node.textContent`.
|
||||
|
|
@ -1008,6 +1047,7 @@ Time to wait between key presses in milliseconds. Defaults to 0.
|
|||
|
||||
## async method: ElementHandle.uncheck
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.uncheck`] instead. Read more about [locators](../locators.md).
|
||||
|
||||
This method checks the element by performing the following steps:
|
||||
1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already
|
||||
|
|
@ -1050,7 +1090,7 @@ Depending on the [`param: state`] parameter, this method waits for one of the [a
|
|||
to pass. This method throws when the element is detached while waiting, unless waiting for the `"hidden"` state.
|
||||
* `"visible"` Wait until the element is [visible](../actionability.md#visible).
|
||||
* `"hidden"` Wait until the element is [not visible](../actionability.md#visible) or
|
||||
[not attached](../actionability.md#attached). Note that waiting for hidden does not throw when the element detaches.
|
||||
not attached. Note that waiting for hidden does not throw when the element detaches.
|
||||
* `"stable"` Wait until the element is both [visible](../actionability.md#visible) and
|
||||
[stable](../actionability.md#stable).
|
||||
* `"enabled"` Wait until the element is [enabled](../actionability.md#enabled).
|
||||
|
|
@ -1073,6 +1113,7 @@ A state to wait for, see below for more details.
|
|||
|
||||
## async method: ElementHandle.waitForSelector
|
||||
* since: v1.8
|
||||
* discouraged: Use web assertions that assert visibility or a locator-based [`method: Locator.waitFor`] instead.
|
||||
- returns: <[null]|[ElementHandle]>
|
||||
|
||||
Returns element specified by selector when it satisfies [`option: state`] option. Returns `null` if waiting for `hidden`
|
||||
|
|
|
|||
|
|
@ -27,6 +27,34 @@ Creates new instance of [FormData].
|
|||
|
||||
Sets a field on the form. File values can be passed either as `Path` or as `FilePayload`.
|
||||
|
||||
```java
|
||||
import com.microsoft.playwright.options.FormData;
|
||||
...
|
||||
FormData form = FormData.create()
|
||||
// Only name and value are set.
|
||||
.set("firstName", "John")
|
||||
// Name and value are set, filename and Content-Type are inferred from the file path.
|
||||
.set("profilePicture1", Paths.get("john.jpg"))
|
||||
// Name, value, filename and Content-Type are set.
|
||||
.set("profilePicture2", new FilePayload("john.jpg", "image/jpeg", Files.readAllBytes(Paths.get("john.jpg"))));
|
||||
.set("age", 30);
|
||||
page.request().post("http://localhost/submit", RequestOptions.create().setForm(form));
|
||||
```
|
||||
|
||||
```csharp
|
||||
var multipart = Context.APIRequest.CreateFormData();
|
||||
// Only name and value are set.
|
||||
multipart.Set("firstName", "John");
|
||||
// Name, value, filename and Content-Type are set.
|
||||
multipart.Set("profilePicture", new FilePayload()
|
||||
{
|
||||
Name = "john.jpg",
|
||||
MimeType = "image/jpeg",
|
||||
Buffer = File.ReadAllBytes("john.jpg")
|
||||
});
|
||||
await Page.APIRequest.PostAsync("https://localhost/submit", new() { Multipart = multipart });
|
||||
```
|
||||
|
||||
### param: FormData.set.name
|
||||
* since: v1.18
|
||||
- `name` <[string]>
|
||||
|
|
|
|||
|
|
@ -382,13 +382,16 @@ default.
|
|||
|
||||
Since [`param: eventInit`] is event-specific, please refer to the events documentation for the lists of initial
|
||||
properties:
|
||||
* [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)
|
||||
* [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)
|
||||
* [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)
|
||||
* [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
|
||||
* [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)
|
||||
* [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)
|
||||
* [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)
|
||||
* [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)
|
||||
* [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)
|
||||
* [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
|
||||
* [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)
|
||||
|
||||
You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:
|
||||
|
||||
|
|
@ -1397,7 +1400,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i
|
|||
If [`param: key`] is a single character, it is case-sensitive, so the values `a` and `A` will generate different
|
||||
respective texts.
|
||||
|
||||
Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
modifier, modifier is pressed and being held while the subsequent key is being pressed.
|
||||
|
||||
### param: Frame.press.selector = %%-input-selector-%%
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i
|
|||
If [`param: key`] is a single character, it is case-sensitive, so the values `a` and `A` will generate different
|
||||
respective texts.
|
||||
|
||||
Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
modifier, modifier is pressed and being held while the subsequent key is being pressed.
|
||||
|
||||
**Usage**
|
||||
|
|
|
|||
|
|
@ -561,13 +561,16 @@ default.
|
|||
|
||||
Since [`param: eventInit`] is event-specific, please refer to the events documentation for the lists of initial
|
||||
properties:
|
||||
* [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)
|
||||
* [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)
|
||||
* [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)
|
||||
* [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
|
||||
* [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)
|
||||
* [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)
|
||||
* [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)
|
||||
* [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)
|
||||
* [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)
|
||||
* [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
|
||||
* [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)
|
||||
|
||||
You can also specify [JSHandle] as the property value if you want live objects to be passed into the event:
|
||||
|
||||
|
|
@ -1717,7 +1720,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i
|
|||
If [`param: key`] is a single character, it is case-sensitive, so the values `a` and `A` will generate different
|
||||
respective texts.
|
||||
|
||||
Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
modifier, modifier is pressed and being held while the subsequent key is being pressed.
|
||||
|
||||
### param: Locator.press.key
|
||||
|
|
@ -1910,6 +1913,9 @@ Returns the buffer with the captured screenshot.
|
|||
### option: Locator.screenshot.maskColor = %%-screenshot-option-mask-color-%%
|
||||
* since: v1.34
|
||||
|
||||
### option: Locator.screenshot.style = %%-screenshot-option-style-%%
|
||||
* since: v1.41
|
||||
|
||||
## async method: Locator.scrollIntoViewIfNeeded
|
||||
* since: v1.14
|
||||
|
||||
|
|
|
|||
|
|
@ -244,6 +244,12 @@ Attribute name.
|
|||
|
||||
Expected attribute value.
|
||||
|
||||
### option: LocatorAssertions.NotToHaveAttribute.ignoreCase
|
||||
* since: v1.40
|
||||
- `ignoreCase` <[boolean]>
|
||||
|
||||
Whether to perform case-insensitive match. [`option: ignoreCase`] option takes precedence over the corresponding regular expression flag if specified.
|
||||
|
||||
### option: LocatorAssertions.NotToHaveAttribute.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.18
|
||||
|
||||
|
|
@ -397,7 +403,7 @@ Expected options currently selected.
|
|||
* langs:
|
||||
- alias-java: isAttached
|
||||
|
||||
Ensures that [Locator] points to an [attached](../actionability.md#attached) DOM node.
|
||||
Ensures that [Locator] points to an element that is [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
@ -831,7 +837,7 @@ element should intersect viewport at any positive ratio. Defaults to `0`.
|
|||
* langs:
|
||||
- alias-java: isVisible
|
||||
|
||||
Ensures that [Locator] points to an [attached](../actionability.md#attached) and [visible](../actionability.md#visible) DOM node.
|
||||
Ensures that [Locator] points to an attached and [visible](../actionability.md#visible) DOM node.
|
||||
|
||||
To check that at least one element from the list is visible, use [`method: Locator.first`].
|
||||
|
||||
|
|
@ -927,7 +933,12 @@ await Expect(
|
|||
* langs:
|
||||
- alias-java: containsText
|
||||
|
||||
Ensures the [Locator] points to an element that contains the given text. You can use regular expressions for the value as well.
|
||||
Ensures the [Locator] points to an element that contains the given text. All nested elements will be considered when computing the text content of the element. You can use regular expressions for the value as well.
|
||||
|
||||
**Details**
|
||||
|
||||
When `expected` parameter is a string, Playwright will normalize whitespaces and line breaks both in the actual text and
|
||||
in the expected string before matching. When regular expression is used, the actual text is matched as is.
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
@ -1151,7 +1162,7 @@ Expected attribute value.
|
|||
### option: LocatorAssertions.toHaveAttribute.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.18
|
||||
|
||||
### option: LocatorAssertions.toHaveAttribute.ignoreCase = %%-js-assertions-timeout-%%
|
||||
### option: LocatorAssertions.toHaveAttribute.ignoreCase
|
||||
* since: v1.40
|
||||
- `ignoreCase` <[boolean]>
|
||||
|
||||
|
|
@ -1530,6 +1541,9 @@ Snapshot name.
|
|||
### option: LocatorAssertions.toHaveScreenshot#1.maskColor = %%-screenshot-option-mask-color-%%
|
||||
* since: v1.35
|
||||
|
||||
### option: LocatorAssertions.toHaveScreenshot#1.stylePath = %%-screenshot-option-style-path-%%
|
||||
* since: v1.41
|
||||
|
||||
### option: LocatorAssertions.toHaveScreenshot#1.omitBackground = %%-screenshot-option-omit-background-%%
|
||||
* since: v1.23
|
||||
|
||||
|
|
@ -1576,6 +1590,9 @@ Note that screenshot assertions only work with Playwright test runner.
|
|||
### option: LocatorAssertions.toHaveScreenshot#2.maskColor = %%-screenshot-option-mask-color-%%
|
||||
* since: v1.35
|
||||
|
||||
### option: LocatorAssertions.toHaveScreenshot#2.stylePath = %%-screenshot-option-style-path-%%
|
||||
* since: v1.41
|
||||
|
||||
### option: LocatorAssertions.toHaveScreenshot#2.omitBackground = %%-screenshot-option-omit-background-%%
|
||||
* since: v1.23
|
||||
|
||||
|
|
@ -1596,7 +1613,12 @@ Note that screenshot assertions only work with Playwright test runner.
|
|||
* langs:
|
||||
- alias-java: hasText
|
||||
|
||||
Ensures the [Locator] points to an element with the given text. You can use regular expressions for the value as well.
|
||||
Ensures the [Locator] points to an element with the given text. All nested elements will be considered when computing the text content of the element. You can use regular expressions for the value as well.
|
||||
|
||||
**Details**
|
||||
|
||||
When `expected` parameter is a string, Playwright will normalize whitespaces and line breaks both in the actual text and
|
||||
in the expected string before matching. When regular expression is used, the actual text is matched as is.
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ Emitted when the page closes.
|
|||
- alias-java: consoleMessage
|
||||
- argument: <[ConsoleMessage]>
|
||||
|
||||
Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also emitted if the page throws an error or a warning.
|
||||
Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.
|
||||
|
||||
The arguments passed into `console.log` are available on the [ConsoleMessage] event handler argument.
|
||||
|
||||
|
|
@ -954,13 +954,16 @@ default.
|
|||
|
||||
Since [`param: eventInit`] is event-specific, please refer to the events documentation for the lists of initial
|
||||
properties:
|
||||
* [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)
|
||||
* [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)
|
||||
* [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)
|
||||
* [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
|
||||
* [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)
|
||||
* [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)
|
||||
* [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)
|
||||
* [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)
|
||||
* [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)
|
||||
* [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
|
||||
* [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)
|
||||
|
||||
You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:
|
||||
|
||||
|
|
@ -2985,6 +2988,18 @@ Give any CSS `@page` size declared in the page priority over what is declared in
|
|||
[`option: height`] or [`option: format`] options. Defaults to `false`, which will scale the content to fit the paper
|
||||
size.
|
||||
|
||||
### option: Page.pdf.tagged
|
||||
* since: v1.42
|
||||
- `tagged` <[boolean]>
|
||||
|
||||
Whether or not to generate tagged (accessible) PDF. Defaults to `false`.
|
||||
|
||||
### option: Page.pdf.outline
|
||||
* since: v1.42
|
||||
- `outline` <[boolean]>
|
||||
|
||||
Whether or not to embed the document outline into the PDF. Defaults to `false`.
|
||||
|
||||
## async method: Page.press
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.press`] instead. Read more about [locators](../locators.md).
|
||||
|
|
@ -3006,7 +3021,7 @@ Holding down `Shift` will type the text that corresponds to the [`param: key`] i
|
|||
If [`param: key`] is a single character, it is case-sensitive, so the values `a` and `A` will generate different
|
||||
respective texts.
|
||||
|
||||
Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
Shortcuts such as `key: "Control+o"`, `key: "Control++` or `key: "Control+Shift+T"` are supported as well. When specified with the
|
||||
modifier, modifier is pressed and being held while the subsequent key is being pressed.
|
||||
|
||||
**Usage**
|
||||
|
|
@ -3127,6 +3142,221 @@ return value resolves to `[]`.
|
|||
### param: Page.querySelectorAll.selector = %%-query-selector-%%
|
||||
* since: v1.9
|
||||
|
||||
|
||||
## async method: Page.addLocatorHandler
|
||||
* since: v1.42
|
||||
|
||||
Sometimes, the web page can show an overlay that obstructs elements behind it and prevents certain actions, like click, from completing. When such an overlay is shown predictably, we recommend dismissing it as a part of your test flow. However, sometimes such an overlay may appear non-deterministically, for example certain cookies consent dialogs behave this way. In this case, [`method: Page.addLocatorHandler`] allows handling an overlay during an action that it would block.
|
||||
|
||||
This method registers a handler for an overlay that is executed once the locator is visible on the page. The handler should get rid of the overlay so that actions blocked by it can proceed. This is useful for nondeterministic interstitial pages or dialogs, like a cookie consent dialog.
|
||||
|
||||
Note that execution time of the handler counts towards the timeout of the action/assertion that executed the handler.
|
||||
|
||||
You can register multiple handlers. However, only a single handler will be running at a time. Any actions inside a handler must not require another handler to run.
|
||||
|
||||
:::warning
|
||||
Running the interceptor will alter your page state mid-test. For example it will change the currently focused element and move the mouse. Make sure that the actions that run after the interceptor are self-contained and do not rely on the focus and mouse state.
|
||||
<br />
|
||||
<br />
|
||||
For example, consider a test that calls [`method: Locator.focus`] followed by [`method: Keyboard.press`]. If your handler clicks a button between these two actions, the focused element most likely will be wrong, and key press will happen on the unexpected element. Use [`method: Locator.press`] instead to avoid this problem.
|
||||
<br />
|
||||
<br />
|
||||
Another example is a series of mouse actions, where [`method: Mouse.move`] is followed by [`method: Mouse.down`]. Again, when the handler runs between these two actions, the mouse position will be wrong during the mouse down. Prefer methods like [`method: Locator.click`] that are self-contained.
|
||||
:::
|
||||
|
||||
**Usage**
|
||||
|
||||
An example that closes a cookie dialog when it appears:
|
||||
|
||||
```js
|
||||
// Setup the handler.
|
||||
await page.addLocatorHandler(page.getByRole('button', { name: 'Accept all cookies' }), async () => {
|
||||
await page.getByRole('button', { name: 'Reject all cookies' }).click();
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
await page.goto('https://example.com');
|
||||
await page.getByRole('button', { name: 'Start here' }).click();
|
||||
```
|
||||
|
||||
```java
|
||||
// Setup the handler.
|
||||
page.addLocatorHandler(page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Accept all cookies")), () => {
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Reject all cookies")).click();
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
page.goto("https://example.com");
|
||||
page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Setup the handler.
|
||||
def handler():
|
||||
page.get_by_role("button", name="Reject all cookies").click()
|
||||
page.add_locator_handler(page.get_by_role("button", name="Accept all cookies"), handler)
|
||||
|
||||
# Write the test as usual.
|
||||
page.goto("https://example.com")
|
||||
page.get_by_role("button", name="Start here").click()
|
||||
```
|
||||
|
||||
```python async
|
||||
# Setup the handler.
|
||||
def handler():
|
||||
await page.get_by_role("button", name="Reject all cookies").click()
|
||||
await page.add_locator_handler(page.get_by_role("button", name="Accept all cookies"), handler)
|
||||
|
||||
# Write the test as usual.
|
||||
await page.goto("https://example.com")
|
||||
await page.get_by_role("button", name="Start here").click()
|
||||
```
|
||||
|
||||
```csharp
|
||||
// Setup the handler.
|
||||
await page.AddLocatorHandlerAsync(page.GetByRole(AriaRole.Button, new() { Name = "Accept all cookies" }), async () => {
|
||||
await page.GetByRole(AriaRole.Button, new() { Name = "Reject all cookies" }).ClickAsync();
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
await page.GotoAsync("https://example.com");
|
||||
await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync();
|
||||
```
|
||||
|
||||
An example that skips the "Confirm your security details" page when it is shown:
|
||||
|
||||
```js
|
||||
// Setup the handler.
|
||||
await page.addLocatorHandler(page.getByText('Confirm your security details'), async () => {
|
||||
await page.getByRole('button', 'Remind me later').click();
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
await page.goto('https://example.com');
|
||||
await page.getByRole('button', { name: 'Start here' }).click();
|
||||
```
|
||||
|
||||
```java
|
||||
// Setup the handler.
|
||||
page.addLocatorHandler(page.getByText("Confirm your security details")), () => {
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click();
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
page.goto("https://example.com");
|
||||
page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Setup the handler.
|
||||
def handler():
|
||||
page.get_by_role("button", name="Remind me later").click()
|
||||
page.add_locator_handler(page.get_by_text("Confirm your security details"), handler)
|
||||
|
||||
# Write the test as usual.
|
||||
page.goto("https://example.com")
|
||||
page.get_by_role("button", name="Start here").click()
|
||||
```
|
||||
|
||||
```python async
|
||||
# Setup the handler.
|
||||
def handler():
|
||||
await page.get_by_role("button", name="Remind me later").click()
|
||||
await page.add_locator_handler(page.get_by_text("Confirm your security details"), handler)
|
||||
|
||||
# Write the test as usual.
|
||||
await page.goto("https://example.com")
|
||||
await page.get_by_role("button", name="Start here").click()
|
||||
```
|
||||
|
||||
```csharp
|
||||
// Setup the handler.
|
||||
await page.AddLocatorHandlerAsync(page.GetByText("Confirm your security details"), async () => {
|
||||
await page.GetByRole(AriaRole.Button, new() { Name = "Remind me later" }).ClickAsync();
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
await page.GotoAsync("https://example.com");
|
||||
await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync();
|
||||
```
|
||||
|
||||
An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible, so the handler is called before every actionability check:
|
||||
|
||||
```js
|
||||
// Setup the handler.
|
||||
await page.addLocatorHandler(page.locator('body'), async () => {
|
||||
await page.evaluate(() => window.removeObstructionsForTestIfNeeded());
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
await page.goto('https://example.com');
|
||||
await page.getByRole('button', { name: 'Start here' }).click();
|
||||
```
|
||||
|
||||
```java
|
||||
// Setup the handler.
|
||||
page.addLocatorHandler(page.locator("body")), () => {
|
||||
page.evaluate("window.removeObstructionsForTestIfNeeded()");
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
page.goto("https://example.com");
|
||||
page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Setup the handler.
|
||||
def handler():
|
||||
page.evaluate("window.removeObstructionsForTestIfNeeded()")
|
||||
page.add_locator_handler(page.locator("body"), handler)
|
||||
|
||||
# Write the test as usual.
|
||||
page.goto("https://example.com")
|
||||
page.get_by_role("button", name="Start here").click()
|
||||
```
|
||||
|
||||
```python async
|
||||
# Setup the handler.
|
||||
def handler():
|
||||
await page.evaluate("window.removeObstructionsForTestIfNeeded()")
|
||||
await page.add_locator_handler(page.locator("body"), handler)
|
||||
|
||||
# Write the test as usual.
|
||||
await page.goto("https://example.com")
|
||||
await page.get_by_role("button", name="Start here").click()
|
||||
```
|
||||
|
||||
```csharp
|
||||
// Setup the handler.
|
||||
await page.AddLocatorHandlerAsync(page.Locator("body"), async () => {
|
||||
await page.EvaluateAsync("window.removeObstructionsForTestIfNeeded()");
|
||||
});
|
||||
|
||||
// Write the test as usual.
|
||||
await page.GotoAsync("https://example.com");
|
||||
await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync();
|
||||
```
|
||||
|
||||
### param: Page.addLocatorHandler.locator
|
||||
* since: v1.42
|
||||
- `locator` <[Locator]>
|
||||
|
||||
Locator that triggers the handler.
|
||||
|
||||
### param: Page.addLocatorHandler.handler
|
||||
* langs: js, python, csharp
|
||||
* since: v1.42
|
||||
- `handler` <[function]>
|
||||
|
||||
Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click.
|
||||
|
||||
### param: Page.addLocatorHandler.handler
|
||||
* langs: java
|
||||
* since: v1.42
|
||||
- `handler` <[Runnable]>
|
||||
|
||||
Function that should be run once [`param: locator`] appears. This function should get rid of the element that blocks actions like click.
|
||||
|
||||
## async method: Page.reload
|
||||
* since: v1.8
|
||||
- returns: <[null]|[Response]>
|
||||
|
|
@ -3245,11 +3475,11 @@ await page.GotoAsync("https://www.microsoft.com");
|
|||
It is possible to examine the request to decide the route action. For example, mocking all requests that contain some post data, and leaving all other requests as is:
|
||||
|
||||
```js
|
||||
await page.route('/api/**', route => {
|
||||
await page.route('/api/**', async route => {
|
||||
if (route.request().postData().includes('my-string'))
|
||||
route.fulfill({ body: 'mocked-data' });
|
||||
await route.fulfill({ body: 'mocked-data' });
|
||||
else
|
||||
route.continue();
|
||||
await route.continue();
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -3263,16 +3493,16 @@ page.route("/api/**", route -> {
|
|||
```
|
||||
|
||||
```python async
|
||||
def handle_route(route):
|
||||
async def handle_route(route: Route):
|
||||
if ("my-string" in route.request.post_data):
|
||||
route.fulfill(body="mocked-data")
|
||||
await route.fulfill(body="mocked-data")
|
||||
else:
|
||||
route.continue_()
|
||||
await route.continue_()
|
||||
await page.route("/api/**", handle_route)
|
||||
```
|
||||
|
||||
```python sync
|
||||
def handle_route(route):
|
||||
def handle_route(route: Route):
|
||||
if ("my-string" in route.request.post_data):
|
||||
route.fulfill(body="mocked-data")
|
||||
else:
|
||||
|
|
@ -3396,6 +3626,9 @@ Returns the buffer with the captured screenshot.
|
|||
### option: Page.screenshot.maskColor = %%-screenshot-option-mask-color-%%
|
||||
* since: v1.34
|
||||
|
||||
### option: Page.screenshot.style = %%-screenshot-option-style-%%
|
||||
* since: v1.41
|
||||
|
||||
## async method: Page.selectOption
|
||||
* since: v1.8
|
||||
* discouraged: Use locator-based [`method: Locator.selectOption`] instead. Read more about [locators](../locators.md).
|
||||
|
|
@ -3857,6 +4090,14 @@ When all steps combined have not finished during the specified [`option: timeout
|
|||
### option: Page.uncheck.trial = %%-input-trial-%%
|
||||
* since: v1.11
|
||||
|
||||
## async method: Page.unrouteAll
|
||||
* since: v1.41
|
||||
|
||||
Removes all routes created with [`method: Page.route`] and [`method: Page.routeFromHAR`].
|
||||
|
||||
### option: Page.unrouteAll.behavior = %%-unroute-all-options-behavior-%%
|
||||
* since: v1.41
|
||||
|
||||
## async method: Page.unroute
|
||||
* since: v1.8
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ assertThat(page).not().hasURL("error");
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page).Not.ToHaveURL("error");
|
||||
await Expect(Page).Not.ToHaveURL("error");
|
||||
```
|
||||
|
||||
## async method: PageAssertions.NotToHaveTitle
|
||||
|
|
@ -161,6 +161,9 @@ Snapshot name.
|
|||
### option: PageAssertions.toHaveScreenshot#1.maskColor = %%-screenshot-option-mask-color-%%
|
||||
* since: v1.35
|
||||
|
||||
### option: PageAssertions.toHaveScreenshot#1.stylePath = %%-screenshot-option-style-path-%%
|
||||
* since: v1.41
|
||||
|
||||
### option: PageAssertions.toHaveScreenshot#1.omitBackground = %%-screenshot-option-omit-background-%%
|
||||
* since: v1.23
|
||||
|
||||
|
|
@ -212,6 +215,9 @@ Note that screenshot assertions only work with Playwright test runner.
|
|||
### option: PageAssertions.toHaveScreenshot#2.maskColor = %%-screenshot-option-mask-color-%%
|
||||
* since: v1.35
|
||||
|
||||
### option: PageAssertions.toHaveScreenshot#2.stylePath = %%-screenshot-option-style-path-%%
|
||||
* since: v1.41
|
||||
|
||||
### option: PageAssertions.toHaveScreenshot#2.omitBackground = %%-screenshot-option-omit-background-%%
|
||||
* since: v1.23
|
||||
|
||||
|
|
@ -261,7 +267,7 @@ expect(page).to_have_title(re.compile(r".*checkout"))
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page).ToHaveTitle("Playwright");
|
||||
await Expect(Page).ToHaveTitle("Playwright");
|
||||
```
|
||||
|
||||
### param: PageAssertions.toHaveTitle.titleOrRegExp
|
||||
|
|
@ -310,7 +316,7 @@ expect(page).to_have_url(re.compile(".*checkout"))
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page).ToHaveURL(new Regex(".*checkout"));
|
||||
await Expect(Page).ToHaveURL(new Regex(".*checkout"));
|
||||
```
|
||||
|
||||
### param: PageAssertions.toHaveURL.urlOrRegExp
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ PlaywrightAssertions.assertThat(page).hasTitle("News");
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page).ToHaveTitleAsync("News");
|
||||
await Expect(Page).ToHaveTitleAsync("News");
|
||||
```
|
||||
|
||||
### param: PlaywrightAssertions.expectPage.page
|
||||
|
|
|
|||
|
|
@ -44,14 +44,14 @@ Continues route's request with optional overrides.
|
|||
**Usage**
|
||||
|
||||
```js
|
||||
await page.route('**/*', (route, request) => {
|
||||
await page.route('**/*', async (route, request) => {
|
||||
// Override headers
|
||||
const headers = {
|
||||
...request.headers(),
|
||||
foo: 'foo-value', // set "foo" header
|
||||
bar: undefined, // remove "bar" header
|
||||
};
|
||||
route.continue({ headers });
|
||||
await route.continue({ headers });
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -92,11 +92,11 @@ page.route("**/*", handle)
|
|||
```
|
||||
|
||||
```csharp
|
||||
await page.RouteAsync("**/*", route =>
|
||||
await page.RouteAsync("**/*", async route =>
|
||||
{
|
||||
var headers = new Dictionary<string, string>(route.Request.Headers) { { "foo", "bar" } };
|
||||
headers.Remove("origin");
|
||||
route.ContinueAsync(headers);
|
||||
await route.ContinueAsync(new() { Headers = headers });
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -154,17 +154,17 @@ in the end will be aborted by the first registered route.
|
|||
**Usage**
|
||||
|
||||
```js
|
||||
await page.route('**/*', route => {
|
||||
await page.route('**/*', async route => {
|
||||
// Runs last.
|
||||
route.abort();
|
||||
await route.abort();
|
||||
});
|
||||
await page.route('**/*', route => {
|
||||
await page.route('**/*', async route => {
|
||||
// Runs second.
|
||||
route.fallback();
|
||||
await route.fallback();
|
||||
});
|
||||
await page.route('**/*', route => {
|
||||
await page.route('**/*', async route => {
|
||||
// Runs first.
|
||||
route.fallback();
|
||||
await route.fallback();
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -220,9 +220,9 @@ GET requests vs POST requests as in the example below.
|
|||
|
||||
```js
|
||||
// Handle GET requests.
|
||||
await page.route('**/*', route => {
|
||||
await page.route('**/*', async route => {
|
||||
if (route.request().method() !== 'GET') {
|
||||
route.fallback();
|
||||
await route.fallback();
|
||||
return;
|
||||
}
|
||||
// Handling GET only.
|
||||
|
|
@ -230,9 +230,9 @@ await page.route('**/*', route => {
|
|||
});
|
||||
|
||||
// Handle POST requests.
|
||||
await page.route('**/*', route => {
|
||||
await page.route('**/*', async route => {
|
||||
if (route.request().method() !== 'POST') {
|
||||
route.fallback();
|
||||
await route.fallback();
|
||||
return;
|
||||
}
|
||||
// Handling POST only.
|
||||
|
|
@ -264,17 +264,17 @@ page.route("**/*", route -> {
|
|||
|
||||
```python async
|
||||
# Handle GET requests.
|
||||
def handle_get(route):
|
||||
async def handle_get(route):
|
||||
if route.request.method != "GET":
|
||||
route.fallback()
|
||||
await route.fallback()
|
||||
return
|
||||
# Handling GET only.
|
||||
# ...
|
||||
|
||||
# Handle POST requests.
|
||||
def handle_post(route):
|
||||
async def handle_post(route):
|
||||
if route.request.method != "POST":
|
||||
route.fallback()
|
||||
await route.fallback()
|
||||
return
|
||||
# Handling POST only.
|
||||
# ...
|
||||
|
|
@ -330,14 +330,14 @@ One can also modify request while falling back to the subsequent handler, that w
|
|||
route handler can modify url, method, headers and postData of the request.
|
||||
|
||||
```js
|
||||
await page.route('**/*', (route, request) => {
|
||||
await page.route('**/*', async (route, request) => {
|
||||
// Override headers
|
||||
const headers = {
|
||||
...request.headers(),
|
||||
foo: 'foo-value', // set "foo" header
|
||||
bar: undefined, // remove "bar" header
|
||||
};
|
||||
route.fallback({ headers });
|
||||
await route.fallback({ headers });
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -378,11 +378,11 @@ page.route("**/*", handle)
|
|||
```
|
||||
|
||||
```csharp
|
||||
await page.RouteAsync("**/*", route =>
|
||||
await page.RouteAsync("**/*", async route =>
|
||||
{
|
||||
var headers = new Dictionary<string, string>(route.Request.Headers) { { "foo", "foo-value" } };
|
||||
headers.Remove("bar");
|
||||
route.FallbackAsync(headers);
|
||||
await route.FallbackAsync(new() { Headers = headers });
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -554,8 +554,8 @@ Fulfills route's request with given response.
|
|||
An example of fulfilling all requests with 404 responses:
|
||||
|
||||
```js
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({
|
||||
await page.route('**/*', async route => {
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'Not Found!'
|
||||
|
|
|
|||
|
|
@ -45,14 +45,15 @@ context.tracing.stop(path = "trace.zip")
|
|||
```
|
||||
|
||||
```csharp
|
||||
await using var browser = playwright.Chromium.LaunchAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
var browser = await playwright.Chromium.LaunchAsync();
|
||||
await using var context = await browser.NewContextAsync();
|
||||
await context.Tracing.StartAsync(new()
|
||||
{
|
||||
Screenshots = true,
|
||||
Snapshots = true
|
||||
});
|
||||
var page = context.NewPageAsync();
|
||||
var page = await context.NewPageAsync();
|
||||
await page.GotoAsync("https://playwright.dev");
|
||||
await context.Tracing.StopAsync(new()
|
||||
{
|
||||
|
|
@ -85,28 +86,29 @@ context.tracing().stop(new Tracing.StopOptions()
|
|||
```
|
||||
|
||||
```python async
|
||||
await context.tracing.start(name="trace", screenshots=True, snapshots=True)
|
||||
await context.tracing.start(screenshots=True, snapshots=True)
|
||||
page = await context.new_page()
|
||||
await page.goto("https://playwright.dev")
|
||||
await context.tracing.stop(path = "trace.zip")
|
||||
```
|
||||
|
||||
```python sync
|
||||
context.tracing.start(name="trace", screenshots=True, snapshots=True)
|
||||
context.tracing.start(screenshots=True, snapshots=True)
|
||||
page = context.new_page()
|
||||
page.goto("https://playwright.dev")
|
||||
context.tracing.stop(path = "trace.zip")
|
||||
```
|
||||
|
||||
```csharp
|
||||
await using var browser = playwright.Chromium.LaunchAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
var browser = await playwright.Chromium.LaunchAsync();
|
||||
await using var context = await browser.NewContextAsync();
|
||||
await context.Tracing.StartAsync(new()
|
||||
{
|
||||
Screenshots = true,
|
||||
Snapshots = true
|
||||
});
|
||||
var page = context.NewPageAsync();
|
||||
var page = await context.NewPageAsync();
|
||||
await page.GotoAsync("https://playwright.dev");
|
||||
await context.Tracing.StopAsync(new()
|
||||
{
|
||||
|
|
@ -118,8 +120,10 @@ await context.Tracing.StopAsync(new()
|
|||
* since: v1.12
|
||||
- `name` <[string]>
|
||||
|
||||
If specified, the trace is going to be saved into the file with the
|
||||
given name inside the [`option: tracesDir`] folder specified in [`method: BrowserType.launch`].
|
||||
If specified, intermediate trace files are going to be saved into the files with the
|
||||
given name prefix inside the [`option: tracesDir`] folder specified in [`method: BrowserType.launch`].
|
||||
To specify the final trace zip file name, you need to pass `path` option to
|
||||
[`method: Tracing.stop`] instead.
|
||||
|
||||
### option: Tracing.start.screenshots
|
||||
* since: v1.12
|
||||
|
|
@ -202,7 +206,7 @@ context.tracing().stopChunk(new Tracing.StopChunkOptions()
|
|||
```
|
||||
|
||||
```python async
|
||||
await context.tracing.start(name="trace", screenshots=True, snapshots=True)
|
||||
await context.tracing.start(screenshots=True, snapshots=True)
|
||||
page = await context.new_page()
|
||||
await page.goto("https://playwright.dev")
|
||||
|
||||
|
|
@ -218,7 +222,7 @@ await context.tracing.stop_chunk(path = "trace2.zip")
|
|||
```
|
||||
|
||||
```python sync
|
||||
context.tracing.start(name="trace", screenshots=True, snapshots=True)
|
||||
context.tracing.start(screenshots=True, snapshots=True)
|
||||
page = context.new_page()
|
||||
page.goto("https://playwright.dev")
|
||||
|
||||
|
|
@ -234,14 +238,15 @@ context.tracing.stop_chunk(path = "trace2.zip")
|
|||
```
|
||||
|
||||
```csharp
|
||||
await using var browser = playwright.Chromium.LaunchAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
var browser = await playwright.Chromium.LaunchAsync();
|
||||
await using var context = await browser.NewContextAsync();
|
||||
await context.Tracing.StartAsync(new()
|
||||
{
|
||||
Screenshots = true,
|
||||
Snapshots = true
|
||||
});
|
||||
var page = context.NewPageAsync();
|
||||
var page = await context.NewPageAsync();
|
||||
await page.GotoAsync("https://playwright.dev");
|
||||
|
||||
await context.Tracing.StartChunkAsync();
|
||||
|
|
@ -271,8 +276,10 @@ Trace name to be shown in the Trace Viewer.
|
|||
* since: v1.32
|
||||
- `name` <[string]>
|
||||
|
||||
If specified, the trace is going to be saved into the file with the
|
||||
given name inside the [`option: tracesDir`] folder specified in [`method: BrowserType.launch`].
|
||||
If specified, intermediate trace files are going to be saved into the files with the
|
||||
given name prefix inside the [`option: tracesDir`] folder specified in [`method: BrowserType.launch`].
|
||||
To specify the final trace zip file name, you need to pass `path` option to
|
||||
[`method: Tracing.stopChunk`] instead.
|
||||
|
||||
## async method: Tracing.stop
|
||||
* since: v1.12
|
||||
|
|
|
|||
|
|
@ -734,6 +734,15 @@ Whether to allow sites to register Service workers. Defaults to `'allow'`.
|
|||
* `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered.
|
||||
* `'block'`: Playwright will block all registration of Service Workers.
|
||||
|
||||
## unroute-all-options-behavior
|
||||
* langs: js, csharp, python
|
||||
* since: v1.41
|
||||
- `behavior` <[UnrouteBehavior]<"wait"|"ignoreErrors"|"default">>
|
||||
|
||||
Specifies wether to wait for already running handlers and what to do if they throw errors:
|
||||
* `'default'` - do not wait for current handler calls (if any) to finish, if unrouted handler throws, it may result in unhandled error
|
||||
* `'wait'` - wait for current handler calls (if any) to finish
|
||||
* `'ignoreErrors'` - do not wait for current handler calls (if any) to finish, all errors thrown by the handlers after unrouting are silently caught
|
||||
|
||||
## select-options-values
|
||||
* langs: java, js, csharp
|
||||
|
|
@ -903,8 +912,12 @@ between the same pixel in compared images, between zero (strict) and one (lax),
|
|||
## browser-option-args
|
||||
- `args` <[Array]<[string]>>
|
||||
|
||||
:::warning
|
||||
Use custom browser args at your own risk, as some of them may break Playwright functionality.
|
||||
:::
|
||||
|
||||
Additional arguments to pass to the browser instance. The list of Chromium flags can be found
|
||||
[here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||
[here](https://peter.sh/experiments/chromium-command-line-switches/).
|
||||
|
||||
## browser-option-channel
|
||||
- `channel` <[string]>
|
||||
|
|
@ -1023,9 +1036,11 @@ For example, `"Playwright"` matches `<article><div>Playwright</div></article>`.
|
|||
## locator-option-has
|
||||
- `has` <[Locator]>
|
||||
|
||||
Matches elements containing an element that matches an inner locator. Inner locator is queried against the outer one.
|
||||
Narrows down the results of the method to those which contain elements matching this relative locator.
|
||||
For example, `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.
|
||||
|
||||
Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not the document root. For example, you can find `content` that has `div` in `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.
|
||||
|
||||
Note that outer and inner locators must belong to the same frame. Inner locator must not contain [FrameLocator]s.
|
||||
|
||||
## locator-option-has-not
|
||||
|
|
@ -1132,6 +1147,18 @@ Defaults to `"css"`.
|
|||
|
||||
When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`, text caret behavior will not be changed. Defaults to `"hide"`.
|
||||
|
||||
## screenshot-option-style
|
||||
- `style` <string>
|
||||
|
||||
Text of the stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements invisible
|
||||
or change their properties to help you creating repeatable screenshots. This stylesheet pierces the Shadow DOM and applies
|
||||
to the inner frames.
|
||||
|
||||
## screenshot-option-style-path
|
||||
- `stylePath` <[string]|[Array]<[string]>>
|
||||
|
||||
File name containing the stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make elements invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces the Shadow DOM and applies to the inner frames.
|
||||
|
||||
## screenshot-options-common-list-v1.8
|
||||
- %%-screenshot-option-animations-%%
|
||||
- %%-screenshot-option-omit-background-%%
|
||||
|
|
@ -1589,7 +1616,7 @@ page.getByRole(AriaRole.BUTTON,
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page
|
||||
await Expect(Page
|
||||
.GetByRole(AriaRole.Heading, new() { Name = "Sign up" }))
|
||||
.ToBeVisibleAsync();
|
||||
|
||||
|
|
@ -1641,7 +1668,7 @@ expect(page.get_by_title("Issues count")).to_have_text("25 issues")
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page.GetByTitle("Issues count")).toHaveText("25 issues");
|
||||
await Expect(Page.GetByTitle("Issues count")).toHaveText("25 issues");
|
||||
```
|
||||
|
||||
## test-config-snapshot-path-template
|
||||
|
|
|
|||
|
|
@ -575,7 +575,7 @@ Reusing authenticated state covers [cookies](https://developer.mozilla.org/en-US
|
|||
```js
|
||||
// Get session storage and store as env variable
|
||||
const sessionStorage = await page.evaluate(() => JSON.stringify(sessionStorage));
|
||||
fs.writeFileSync('playwright/.auth/session.json', JSON.stringify(sessionStorage), 'utf-8');
|
||||
fs.writeFileSync('playwright/.auth/session.json', sessionStorage, 'utf-8');
|
||||
|
||||
// Set session storage in a new context
|
||||
const sessionStorage = JSON.parse(fs.readFileSync('playwright/.auth/session.json', 'utf-8'));
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ page = context.new_page()
|
|||
```
|
||||
|
||||
```csharp
|
||||
await using var browser = playwright.Chromium.LaunchAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
var browser = await playwright.Chromium.LaunchAsync();
|
||||
var context = await browser.NewContextAsync();
|
||||
var page = await context.NewPageAsync();
|
||||
```
|
||||
|
|
|
|||
|
|
@ -457,7 +457,8 @@ Google Chrome and Microsoft Edge respect enterprise policies, which include limi
|
|||
|
||||
### Firefox
|
||||
|
||||
Playwright's Firefox version matches the recent [Firefox Stable](https://www.mozilla.org/en-US/firefox/new/) build. Playwright doesn't work with the branded version of Firefox since it relies on patches. Instead you can test against the recent Firefox Stable build.
|
||||
Playwright's Firefox version matches the recent [Firefox Stable](https://www.mozilla.org/en-US/firefox/new/) build. Playwright doesn't work with the branded version of Firefox since it relies on patches.
|
||||
|
||||
### WebKit
|
||||
|
||||
Playwright's WebKit version matches the recent WebKit trunk build, before it is used in Apple Safari and other WebKit-based browsers. This gives a lot of lead time to react on the potential browser update issues. Playwright doesn't work with the branded version of Safari since it relies on patches. Instead you can test against the recent WebKit build.
|
||||
|
|
@ -612,61 +613,61 @@ binaries. In this case, Playwright can be configured to download from a custom
|
|||
location using the `PLAYWRIGHT_DOWNLOAD_HOST` env variable.
|
||||
|
||||
```bash tab=bash-bash lang=js
|
||||
PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1 npx playwright install
|
||||
PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1 npx playwright install
|
||||
```
|
||||
|
||||
```batch tab=bash-batch lang=js
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
```powershell tab=bash-powershell lang=js
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="192.0.2.1"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="http://192.0.2.1"
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
```bash tab=bash-bash lang=python
|
||||
pip install playwright
|
||||
PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1 playwright install
|
||||
PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1 playwright install
|
||||
```
|
||||
|
||||
```batch tab=bash-batch lang=python
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1
|
||||
pip install playwright
|
||||
playwright install
|
||||
```
|
||||
|
||||
```powershell tab=bash-powershell lang=python
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="192.0.2.1"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="http://192.0.2.1"
|
||||
pip install playwright
|
||||
playwright install
|
||||
```
|
||||
|
||||
```bash tab=bash-bash lang=java
|
||||
PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1 mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
|
||||
PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1 mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
|
||||
```
|
||||
|
||||
```batch tab=bash-batch lang=java
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
|
||||
```
|
||||
|
||||
```powershell tab=bash-powershell lang=java
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="192.0.2.1"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="http://192.0.2.1"
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
|
||||
```
|
||||
|
||||
```bash tab=bash-bash lang=csharp
|
||||
PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1 pwsh bin/Debug/netX/playwright.ps1 install
|
||||
PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1 pwsh bin/Debug/netX/playwright.ps1 install
|
||||
```
|
||||
|
||||
```batch tab=bash-batch lang=csharp
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1
|
||||
pwsh bin/Debug/netX/playwright.ps1 install
|
||||
```
|
||||
|
||||
```powershell tab=bash-powershell lang=csharp
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="192.0.2.1"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="http://192.0.2.1"
|
||||
pwsh bin/Debug/netX/playwright.ps1 install
|
||||
```
|
||||
|
||||
|
|
@ -674,69 +675,69 @@ It is also possible to use a per-browser download hosts using `PLAYWRIGHT_CHROMI
|
|||
take precedence over `PLAYWRIGHT_DOWNLOAD_HOST`.
|
||||
|
||||
```bash tab=bash-bash lang=js
|
||||
PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=203.0.113.3 PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1 npx playwright install
|
||||
PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=http://203.0.113.3 PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1 npx playwright install
|
||||
```
|
||||
|
||||
```batch tab=bash-batch lang=js
|
||||
set PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=203.0.113.3
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1
|
||||
set PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=http://203.0.113.3
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
```powershell tab=bash-powershell lang=js
|
||||
$Env:PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST="203.0.113.3"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="192.0.2.1"
|
||||
$Env:PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST="http://203.0.113.3"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="http://192.0.2.1"
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
```bash tab=bash-bash lang=python
|
||||
pip install playwright
|
||||
PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=203.0.113.3 PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1 playwright install
|
||||
PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=http://203.0.113.3 PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1 playwright install
|
||||
```
|
||||
|
||||
```batch tab=bash-batch lang=python
|
||||
set PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=203.0.113.3
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1
|
||||
set PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=http://203.0.113.3
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1
|
||||
pip install playwright
|
||||
playwright install
|
||||
```
|
||||
|
||||
```powershell tab=bash-powershell lang=python
|
||||
$Env:PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST="203.0.113.3"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="192.0.2.1"
|
||||
$Env:PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST="http://203.0.113.3"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="http://192.0.2.1"
|
||||
pip install playwright
|
||||
playwright install
|
||||
```
|
||||
|
||||
```bash tab=bash-bash lang=java
|
||||
PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=203.0.113.3 PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1 mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
|
||||
PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=http://203.0.113.3 PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1 mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
|
||||
```
|
||||
|
||||
```batch tab=bash-batch lang=java
|
||||
set PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=203.0.113.3
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1
|
||||
set PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=http://203.0.113.3
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
|
||||
```
|
||||
|
||||
```powershell tab=bash-powershell lang=java
|
||||
$Env:PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST="203.0.113.3"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="192.0.2.1"
|
||||
$Env:PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST="http://203.0.113.3"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="http://192.0.2.1"
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"
|
||||
```
|
||||
|
||||
```bash tab=bash-bash lang=csharp
|
||||
PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=203.0.113.3 PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1 pwsh bin/Debug/netX/playwright.ps1 install
|
||||
PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=http://203.0.113.3 PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1 pwsh bin/Debug/netX/playwright.ps1 install
|
||||
```
|
||||
|
||||
```batch tab=bash-batch lang=csharp
|
||||
set PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=203.0.113.3
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=192.0.2.1
|
||||
set PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=http://203.0.113.3
|
||||
set PLAYWRIGHT_DOWNLOAD_HOST=http://192.0.2.1
|
||||
pwsh bin/Debug/netX/playwright.ps1 install
|
||||
```
|
||||
|
||||
```powershell tab=bash-powershell lang=csharp
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="192.0.2.1"
|
||||
$Env:PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST="203.0.113.3"
|
||||
$Env:PLAYWRIGHT_DOWNLOAD_HOST="http://192.0.2.1"
|
||||
$Env:PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST="http://203.0.113.3"
|
||||
pwsh bin/Debug/netX/playwright.ps1 install
|
||||
```
|
||||
## Managing browser binaries
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ title: "Chrome extensions"
|
|||
## Introduction
|
||||
|
||||
:::note
|
||||
Extensions only work in Chrome / Chromium launched with a persistent context.
|
||||
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 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`:
|
||||
|
|
@ -214,6 +214,10 @@ def test_popup_page(page: Page, extension_id: str) -> None:
|
|||
|
||||
## Headless mode
|
||||
|
||||
:::danger
|
||||
`headless=new` mode is not officially supported by Playwright and might result in unexpected behavior.
|
||||
:::
|
||||
|
||||
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 the following code:
|
||||
|
||||
```js title="fixtures.ts"
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ jobs:
|
|||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
|
|
@ -64,7 +64,7 @@ jobs:
|
|||
run: npx playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
run: npx playwright test
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
|
|
@ -89,7 +89,7 @@ jobs:
|
|||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
|
|
@ -102,7 +102,7 @@ jobs:
|
|||
run: python -m playwright install --with-deps
|
||||
- name: Run your tests
|
||||
run: pytest --tracing=retain-on-failure
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-traces
|
||||
|
|
@ -121,7 +121,7 @@ jobs:
|
|||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
|
|
@ -146,14 +146,14 @@ jobs:
|
|||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- run: dotnet build
|
||||
- name: Ensure browsers are installed
|
||||
run: pwsh bin/Debug/net6.0/playwright.ps1 install --with-deps
|
||||
run: pwsh bin/Debug/net8.0/playwright.ps1 install --with-deps
|
||||
- name: Run your tests
|
||||
run: dotnet test
|
||||
```
|
||||
|
|
@ -181,14 +181,16 @@ jobs:
|
|||
container:
|
||||
image: mcr.microsoft.com/playwright:v%%VERSION%%-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Run your tests
|
||||
run: npx playwright test
|
||||
env:
|
||||
HOME: /root
|
||||
```
|
||||
|
||||
```yml python title=".github/workflows/playwright.yml"
|
||||
|
|
@ -205,7 +207,7 @@ jobs:
|
|||
container:
|
||||
image: mcr.microsoft.com/playwright/python:v%%VERSION%%-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
|
|
@ -217,6 +219,8 @@ jobs:
|
|||
pip install -e .
|
||||
- name: Run your tests
|
||||
run: pytest
|
||||
env:
|
||||
HOME: /root
|
||||
```
|
||||
|
||||
```yml java title=".github/workflows/playwright.yml"
|
||||
|
|
@ -233,7 +237,7 @@ jobs:
|
|||
container:
|
||||
image: mcr.microsoft.com/playwright/java:v%%VERSION%%-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
|
|
@ -242,6 +246,8 @@ jobs:
|
|||
run: mvn -B install -D skipTests --no-transfer-progress
|
||||
- name: Run tests
|
||||
run: mvn test
|
||||
env:
|
||||
HOME: /root
|
||||
```
|
||||
|
||||
```yml csharp title=".github/workflows/playwright.yml"
|
||||
|
|
@ -258,14 +264,16 @@ jobs:
|
|||
container:
|
||||
image: mcr.microsoft.com/playwright/dotnet:v%%VERSION%%-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- run: dotnet build
|
||||
- name: Run your tests
|
||||
run: dotnet test
|
||||
env:
|
||||
HOME: /root
|
||||
```
|
||||
|
||||
### On deployment
|
||||
|
|
@ -283,8 +291,8 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: github.event.deployment_status.state == 'success'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
|
|
@ -307,7 +315,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: github.event.deployment_status.state == 'success'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
|
@ -334,7 +342,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: github.event.deployment_status.state == 'success'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
|
|
@ -360,14 +368,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: github.event.deployment_status.state == 'success'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- run: dotnet build
|
||||
- name: Ensure browsers are installed
|
||||
run: pwsh bin/Debug/net6.0/playwright.ps1 install --with-deps
|
||||
run: pwsh bin/Debug/net8.0/playwright.ps1 install --with-deps
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
env:
|
||||
|
|
|
|||
|
|
@ -167,11 +167,11 @@ steps:
|
|||
- task: UseDotNet@2
|
||||
inputs:
|
||||
packageType: sdk
|
||||
version: '6.0.x'
|
||||
version: '8.0.x'
|
||||
displayName: 'Use .NET SDK'
|
||||
- script: dotnet build --configuration Release
|
||||
displayName: 'Build'
|
||||
- script: pwsh bin/Release/net6.0/playwright.ps1 install --with-deps
|
||||
- script: pwsh bin/Release/net8.0/playwright.ps1 install --with-deps
|
||||
displayName: 'Install Playwright browsers'
|
||||
- script: dotnet test --configuration Release
|
||||
displayName: 'Run tests'
|
||||
|
|
@ -227,7 +227,7 @@ Note: The JUnit reporter needs to be configured accordingly via
|
|||
import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
reporter: ['junit', { outputFile: 'test-results/e2e-junit-results.xml' }],
|
||||
reporter: [['junit', { outputFile: 'test-results/e2e-junit-results.xml' }]],
|
||||
});
|
||||
```
|
||||
in `playwright.config.ts`.
|
||||
|
|
@ -367,7 +367,7 @@ steps:
|
|||
- task: UseDotNet@2
|
||||
inputs:
|
||||
packageType: sdk
|
||||
version: '6.0.x'
|
||||
version: '8.0.x'
|
||||
displayName: 'Use .NET SDK'
|
||||
|
||||
- script: dotnet build --configuration Release
|
||||
|
|
@ -587,6 +587,19 @@ tests:
|
|||
- npm ci
|
||||
- npx playwright test --project=$PROJECT --shard=$SHARD
|
||||
```
|
||||
### Google Cloud Build
|
||||
* langs: js
|
||||
|
||||
To run Playwright tests on Google Cloud Build, use our public Docker image ([see Dockerfile](./docker.md)).
|
||||
|
||||
```yml
|
||||
steps:
|
||||
- name: mcr.microsoft.com/playwright:v%%VERSION%%-jammy
|
||||
script:
|
||||
...
|
||||
env:
|
||||
- 'CI=true'
|
||||
```
|
||||
|
||||
## Caching browsers
|
||||
|
||||
|
|
@ -667,7 +680,7 @@ await playwright.Chromium.LaunchAsync(new()
|
|||
});
|
||||
```
|
||||
|
||||
On Linux agents, headed execution requires [Xvfb](https://en.wikipedia.org/wiki/Xvfb) to be installed. Our [Docker image](./docker.md) and GitHub Action have Xvfb pre-installed. To run browsers in headed mode with Xvfb, add `xvfb-run` before the Node.js command.
|
||||
On Linux agents, headed execution requires [Xvfb](https://en.wikipedia.org/wiki/Xvfb) to be installed. Our [Docker image](./docker.md) and GitHub Action have Xvfb pre-installed. To run browsers in headed mode with Xvfb, add `xvfb-run` before the actual command.
|
||||
|
||||
```bash js
|
||||
xvfb-run node index.js
|
||||
|
|
|
|||
|
|
@ -36,25 +36,33 @@ pwsh bin/Debug/netX/playwright.ps1 codegen demo.playwright.dev/todomvc
|
|||
|
||||
Run `codegen` and perform actions in the browser. Playwright will generate the code for the user interactions. `Codegen` will look at the rendered page and figure out the recommended locator, prioritizing role, text and test id locators. If the generator identifies multiple elements matching the locator, it will improve the locator to make it resilient and uniquely identify the target element, therefore eliminating and reducing test(s) failing and flaking due to locators.
|
||||
|
||||
With the test generator you can record:
|
||||
* Actions like click or fill by simply interacting with the page
|
||||
* Assertions by clicking on one of the icons in the toolbar and then clicking on an element on the page to assert against. You can choose:
|
||||
* `'assert visibility'` to assert that an element is visible
|
||||
* `'assert text'` to assert that an element contains specific text
|
||||
* `'assert value'` to assert that an element has a specific value
|
||||
|
||||
######
|
||||
* langs: js
|
||||
|
||||

|
||||

|
||||
|
||||
######
|
||||
* langs: java
|
||||
|
||||

|
||||

|
||||
|
||||
######
|
||||
* langs: python
|
||||
|
||||

|
||||

|
||||
|
||||
######
|
||||
* langs: csharp
|
||||
|
||||

|
||||

|
||||
|
||||
######
|
||||
* langs: js, java, python, csharp
|
||||
|
||||
|
|
@ -77,22 +85,22 @@ You can generate [locators](/locators.md) with the test generator.
|
|||
######
|
||||
* langs: js
|
||||
|
||||

|
||||

|
||||
|
||||
######
|
||||
* langs: java
|
||||
|
||||

|
||||

|
||||
|
||||
######
|
||||
* langs: python
|
||||
|
||||

|
||||

|
||||
|
||||
######
|
||||
* langs: csharp
|
||||
|
||||

|
||||

|
||||
|
||||
### Emulation
|
||||
|
||||
|
|
|
|||
|
|
@ -27,17 +27,25 @@ To record a test click on the **Record new** button from the Testing sidebar. Th
|
|||
|
||||
In the browser go to the URL you wish to test and start clicking around to record your user actions.
|
||||
|
||||
<img width="1394" alt="clicking delete button on todo app with locator highlighted" src="https://user-images.githubusercontent.com/13063165/220957132-31b54f82-6235-4c52-a966-6863553b5b23.png" />
|
||||

|
||||
|
||||
Playwright will record your actions and generate the test code directly in VS Code. Once you are done recording click the **cancel** button or close the browser window. You can then inspect your `test-1.spec.ts` file and see your generated test and then manually improve the test by adding [ assertions](test-assertions).
|
||||
Playwright will record your actions and generate the test code directly in VS Code. You can also generate assertions by choosing one of the icons in the toolbar and then clicking on an element on the page to assert against. The following assertions can be generated:
|
||||
* `'assert visibility'` to assert that an element is visible
|
||||
* `'assert text'` to assert that an element contains specific text
|
||||
* `'assert value'` to assert that an element has a specific value
|
||||
|
||||
<img width="1667" alt="vs code showing recorded actions of test" src="https://user-images.githubusercontent.com/13063165/220938674-6e1ff1d3-e75a-4238-a7fc-4c40dbc8b3bc.png" />
|
||||

|
||||
|
||||
Once you are done recording click the **cancel** button or close the browser window. You can then inspect your `test-1.spec.ts` file and manually improve it if needed.
|
||||
|
||||

|
||||
|
||||
### Record at Cursor
|
||||
|
||||
To record from a specific point in your test move your cursor to where you want to record more actions and then click the **Record at cursor** button from the Testing sidebar. If your browser window is not already open then first run the test with 'Show browser' checked and then click the **Record at cursor** button.
|
||||
|
||||
<img width="1529" alt="record at cursor in vs code" src="https://user-images.githubusercontent.com/13063165/220959996-2bb3af59-85d9-4d58-aba7-d57375e7ca7e.png" />
|
||||
|
||||

|
||||
|
||||
In the browser window start performing the actions you want to record.
|
||||
|
||||
|
|
@ -46,7 +54,7 @@ In the browser window start performing the actions you want to record.
|
|||
|
||||
In the test file in VS Code you will see your new generated actions added to your test at the cursor position.
|
||||
|
||||
<img width="1641" alt="vs code showing test code generated" src="https://user-images.githubusercontent.com/13063165/220940902-d1dbc321-0ef5-4388-9e11-6311aff59ff4.png" />
|
||||

|
||||
|
||||
### Generating locators
|
||||
|
||||
|
|
@ -85,25 +93,35 @@ pwsh bin/Debug/netX/playwright.ps1 codegen demo.playwright.dev/todomvc
|
|||
|
||||
Run the `codegen` command and perform actions in the browser window. Playwright will generate the code for the user interactions which you can see in the Playwright Inspector window. Once you have finished recording your test stop the recording and press the **copy** button to copy your generated test into your editor.
|
||||
|
||||
With the test generator you can record:
|
||||
* Actions like click or fill by simply interacting with the page
|
||||
* Assertions by clicking on one of the icons in the toolbar and then clicking on an element on the page to assert against. You can choose:
|
||||
* `'assert visibility'` to assert that an element is visible
|
||||
* `'assert text'` to assert that an element contains specific text
|
||||
* `'assert value'` to assert that an element has a specific value
|
||||
|
||||
######
|
||||
* langs: js
|
||||
|
||||
<img width="1365" alt="Recording a test" src="https://user-images.githubusercontent.com/13063165/212754505-b98e80fd-6dda-48f7-860b-b32b4fabee33.png" />
|
||||

|
||||
|
||||
######
|
||||
* langs: java
|
||||
|
||||
<img width="1365" alt="Recording a test" src="https://user-images.githubusercontent.com/13063165/212754804-0d9f9d52-0a48-45c8-970d-e672d4a91221.png" />
|
||||

|
||||
|
||||
######
|
||||
* langs: python
|
||||
|
||||
<img width="1365" alt="Recording a test" src="https://user-images.githubusercontent.com/13063165/212751993-b7da2c40-a7cc-4b13-9a91-40ee837042a1.png" />
|
||||

|
||||
|
||||
######
|
||||
* langs: csharp
|
||||
|
||||
<img width="1365" alt="Recording a test" src="https://user-images.githubusercontent.com/13063165/212754994-fa637d81-b81d-44b8-bcd7-5dc218034f0a.png" />
|
||||

|
||||
|
||||
######
|
||||
* langs: js, java, python, csharp
|
||||
|
||||
When you have finished interacting with the page, press the **record** button to stop the recording and use the **copy** button to copy the generated code to your editor.
|
||||
|
||||
|
|
@ -122,22 +140,22 @@ You can generate [locators](/locators.md) with the test generator.
|
|||
######
|
||||
* langs: js
|
||||
|
||||
<img width="1321" alt="Picking a locator" src="https://user-images.githubusercontent.com/13063165/212753129-55fbcf69-0be3-422e-888a-f52060c7aa6b.png" />
|
||||

|
||||
|
||||
######
|
||||
* langs: java
|
||||
|
||||
<img width="1321" alt="Picking a locator" src="https://user-images.githubusercontent.com/13063165/212753446-456484a8-8c37-4104-8db5-4525b74c8cf1.png" />
|
||||

|
||||
|
||||
######
|
||||
* langs: python
|
||||
|
||||
<img width="1321" alt="Picking a locator" src="https://user-images.githubusercontent.com/13063165/212753605-861d66a4-fc1c-4559-b821-cb1f39059337.png" />
|
||||

|
||||
|
||||
######
|
||||
* langs: csharp
|
||||
|
||||
<img width="1321" alt="Picking a locator" src="https://user-images.githubusercontent.com/13063165/212753728-49d35a7c-c05a-4298-bf66-89930d2cb578.png" />
|
||||

|
||||
|
||||
## Emulation
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ title: "Docker"
|
|||
|
||||
## Introduction
|
||||
|
||||
[Dockerfile.jammy] can be used to run Playwright scripts in Docker environment. These image includes all the dependencies needed to run browsers in a Docker container, and also include the browsers themselves.
|
||||
[Dockerfile.jammy] can be used to run Playwright scripts in Docker environment. These image includes the [Playwright browsers](./browsers.md#install-browsers) and [browser system dependencies](./browsers.md#install-system-dependencies). The Playwright package/dependency is not included in the image and should be installed separately.
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -107,15 +107,12 @@ See our [Continuous Integration guides](./ci.md) for sample configs.
|
|||
|
||||
See [all available image tags].
|
||||
|
||||
Docker images are published automatically by GitHub Actions. We currently publish images with the
|
||||
following tags (`v1.33.0` in this case is an example:):
|
||||
We currently publish images with the following tags:
|
||||
- `:next` - tip-of-tree image version based on Ubuntu 22.04 LTS (Jammy Jellyfish).
|
||||
- `:next-jammy` - tip-of-tree image version based on Ubuntu 22.04 LTS (Jammy Jellyfish).
|
||||
- `:v1.33.0` - Playwright v1.33.0 release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish).
|
||||
- `:v1.33.0-jammy` - Playwright v1.33.0 release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish).
|
||||
- `:v1.33.0-focal` - Playwright v1.33.0 release docker image based on Ubuntu 20.04 LTS (Focal Fossa).
|
||||
- `:sha-XXXXXXX` - docker image for every commit that changed
|
||||
docker files or browsers, marked with a [short sha](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#Short-SHA-1) (first 7 digits of the SHA commit).
|
||||
- `:v%%VERSION%%` - Playwright v%%VERSION%% release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish).
|
||||
- `:v%%VERSION%%-jammy` - Playwright v%%VERSION%% release docker image based on Ubuntu 22.04 LTS (Jammy Jellyfish).
|
||||
- `:v%%VERSION%%-focal` - Playwright v%%VERSION%% release docker image based on Ubuntu 20.04 LTS (Focal Fossa).
|
||||
|
||||
:::note
|
||||
It is recommended to always pin your Docker image to a specific version if possible. If the Playwright version in your Docker image does not match the version in your project/tests, Playwright will be unable to locate browser executables.
|
||||
|
|
@ -131,20 +128,34 @@ We currently publish images based on the following [Ubuntu](https://hub.docker.c
|
|||
|
||||
Browser builds for Firefox and WebKit are built for the [glibc](https://en.wikipedia.org/wiki/Glibc) library. Alpine Linux and other distributions that are based on the [musl](https://en.wikipedia.org/wiki/Musl) standard library are not supported.
|
||||
|
||||
## Development
|
||||
## Using a different .NET version
|
||||
* langs: csharp
|
||||
|
||||
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 6.0
|
||||
```
|
||||
|
||||
## Build your own image
|
||||
* langs: js
|
||||
|
||||
### Build the image
|
||||
To run Playwright inside Docker, you need to have Node.js, [Playwright browsers](./browsers.md#install-browsers) and [browser system dependencies](./browsers.md#install-system-dependencies) installed. See the following Dockerfile:
|
||||
|
||||
Use [`//utils/docker/build.sh`](https://github.com/microsoft/playwright/blob/main/utils/docker/build.sh) to build the image.
|
||||
```Dockerfile
|
||||
FROM node:20-bookworm
|
||||
|
||||
```txt
|
||||
./utils/docker/build.sh jammy playwright:localbuild-jammy
|
||||
RUN npx -y playwright@%%VERSION%% install --with-deps
|
||||
```
|
||||
|
||||
The image will be tagged as `playwright:localbuild-jammy` and could be run as:
|
||||
## Build your own image
|
||||
* langs: python
|
||||
|
||||
```txt
|
||||
docker run --rm -it playwright:localbuild /bin/bash
|
||||
To run Playwright inside Docker, you need to have Python, [Playwright browsers](./browsers.md#install-browsers) and [browser system dependencies](./browsers.md#install-system-dependencies) installed. See the following Dockerfile:
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.12-bookworm
|
||||
|
||||
RUN pip install playwright==@%%VERSION%% && \
|
||||
playwright install --with-deps
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,8 @@ Playwright can emulate various devices by specifying `setDeviceScaleFactor`, `se
|
|||
The viewport is included in the device but you can override it for some tests with [`method: Page.setViewportSize`].
|
||||
|
||||
```js tab=js-test title="playwright.config.ts"
|
||||
import { defineConfig } from '@playwright/test';
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
projects: [
|
||||
{
|
||||
|
|
@ -245,7 +246,7 @@ await using var context = await browser.NewContextAsync(new()
|
|||
Whether the meta viewport tag is taken into account and touch events are enabled.
|
||||
|
||||
```js title="playwright.config.ts"
|
||||
import { defineConfig } from '@playwright/test';
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
projects: [
|
||||
|
|
@ -780,16 +781,16 @@ BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
|||
|
||||
```python async
|
||||
context = await browser.new_context(
|
||||
javaScript_enabled=False
|
||||
java_script_enabled=False
|
||||
)
|
||||
```
|
||||
|
||||
```python sync
|
||||
context = browser.new_context(
|
||||
javaScript_enabled=False
|
||||
java_script_enabled=False
|
||||
)
|
||||
```
|
||||
|
||||
```csharp
|
||||
var context = await browser.NewContextAsync(new() { JavaScriptEnabled = true });
|
||||
var context = await browser.NewContextAsync(new() { JavaScriptEnabled = false });
|
||||
```
|
||||
|
|
|
|||
|
|
@ -83,15 +83,15 @@ await page.evaluate(array => array.length, [1, 2, 3]);
|
|||
await page.evaluate(object => object.foo, { foo: 'bar' });
|
||||
|
||||
// A single handle.
|
||||
const button = await page.evaluate('window.button');
|
||||
const button = await page.evaluateHandle('window.button');
|
||||
await page.evaluate(button => button.textContent, button);
|
||||
|
||||
// Alternative notation using elementHandle.evaluate.
|
||||
await button.evaluate((button, from) => button.textContent.substring(from), 5);
|
||||
|
||||
// Object with multiple handles.
|
||||
const button1 = await page.evaluate('window.button1');
|
||||
const button2 = await page.evaluate('window.button2');
|
||||
const button1 = await page.evaluateHandle('window.button1');
|
||||
const button2 = await page.evaluateHandle('window.button2');
|
||||
await page.evaluate(
|
||||
o => o.button1.textContent + o.button2.textContent,
|
||||
{ button1, button2 });
|
||||
|
|
@ -128,15 +128,15 @@ obj.put("foo", "bar");
|
|||
page.evaluate("object => object.foo", obj);
|
||||
|
||||
// A single handle.
|
||||
ElementHandle button = page.evaluate("window.button");
|
||||
ElementHandle button = page.evaluateHandle("window.button");
|
||||
page.evaluate("button => button.textContent", button);
|
||||
|
||||
// Alternative notation using elementHandle.evaluate.
|
||||
button.evaluate("(button, from) => button.textContent.substring(from)", 5);
|
||||
|
||||
// Object with multiple handles.
|
||||
ElementHandle button1 = page.evaluate("window.button1");
|
||||
ElementHandle button2 = page.evaluate("window.button2");
|
||||
ElementHandle button1 = page.evaluateHandle("window.button1");
|
||||
ElementHandle button2 = page.evaluateHandle("window.button2");
|
||||
Map<String, ElementHandle> arg = new HashMap<>();
|
||||
arg.put("button1", button1);
|
||||
arg.put("button2", button2);
|
||||
|
|
@ -177,15 +177,15 @@ await page.evaluate('array => array.length', [1, 2, 3])
|
|||
await page.evaluate('object => object.foo', { 'foo': 'bar' })
|
||||
|
||||
# A single handle.
|
||||
button = await page.evaluate('button')
|
||||
button = await page.evaluate_handle('button')
|
||||
await page.evaluate('button => button.textContent', button)
|
||||
|
||||
# Alternative notation using elementHandle.evaluate.
|
||||
await button.evaluate('(button, from) => button.textContent.substring(from)', 5)
|
||||
|
||||
# Object with multiple handles.
|
||||
button1 = await page.query_selector('window.button1')
|
||||
button2 = await page.query_selector('window.button2')
|
||||
button1 = await page.evaluate_handle('window.button1')
|
||||
button2 = await page.evaluate_handle('window.button2')
|
||||
await page.evaluate("""
|
||||
o => o.button1.textContent + o.button2.textContent""",
|
||||
{ 'button1': button1, 'button2': button2 })
|
||||
|
|
@ -220,15 +220,15 @@ page.evaluate('array => array.length', [1, 2, 3])
|
|||
page.evaluate('object => object.foo', { 'foo': 'bar' })
|
||||
|
||||
# A single handle.
|
||||
button = page.evaluate('window.button')
|
||||
button = page.evaluate_handle('window.button')
|
||||
page.evaluate('button => button.textContent', button)
|
||||
|
||||
# Alternative notation using elementHandle.evaluate.
|
||||
button.evaluate('(button, from) => button.textContent.substring(from)', 5)
|
||||
|
||||
# Object with multiple handles.
|
||||
button1 = page.evaluate('window.button1')
|
||||
button2 = page.evaluate('.button2')
|
||||
button1 = page.evaluate_handle('window.button1')
|
||||
button2 = page.evaluate_handle('.button2')
|
||||
page.evaluate("""o => o.button1.textContent + o.button2.textContent""",
|
||||
{ 'button1': button1, 'button2': button2 })
|
||||
|
||||
|
|
@ -262,15 +262,15 @@ await page.EvaluateAsync<int[]>("array => array.length", new[] { 1, 2, 3 });
|
|||
await page.EvaluateAsync<object>("object => object.foo", new { foo = "bar" });
|
||||
|
||||
// A single handle.
|
||||
var button = await page.EvaluateAsync("window.button");
|
||||
var button = await page.EvaluateHandleAsync("window.button");
|
||||
await page.EvaluateAsync<IJSHandle>("button => button.textContent", button);
|
||||
|
||||
// Alternative notation using elementHandle.EvaluateAsync.
|
||||
await button.EvaluateAsync<string>("(button, from) => button.textContent.substring(from)", 5);
|
||||
|
||||
// Object with multiple handles.
|
||||
var button1 = await page.EvaluateAsync("window.button1");
|
||||
var button2 = await page.EvaluateAsync("window.button2");
|
||||
var button1 = await page.EvaluateHandleAsync("window.button1");
|
||||
var button2 = await page.EvaluateHandleAsync("window.button2");
|
||||
await page.EvaluateAsync("o => o.button1.textContent + o.button2.textContent", new { button1, button2 });
|
||||
|
||||
// Object destructuring works. Note that property names must match
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ title: "Extensibility"
|
|||
Playwright supports custom selector engines, registered with [`method: Selectors.register`].
|
||||
|
||||
Selector engine should have the following properties:
|
||||
- `create` function to create a relative selector from `root` (root is either a `Document`, `ShadowRoot` or `Element`)
|
||||
to a `target` element.
|
||||
- `query` function to query first element matching `selector` relative to the `root`.
|
||||
- `queryAll` function to query all elements matching `selector` relative to the `root`.
|
||||
|
||||
|
|
|
|||
|
|
@ -145,23 +145,28 @@ CodeGen will auto generate your tests for you as you perform actions in the brow
|
|||
|
||||
### Record a New Test
|
||||
|
||||
To record a test click on the **Record new** button from the Testing sidebar. This will create a `test-1.spec.ts` file as well as open up a browser window. In the browser go to the URL you wish to test and start clicking around. Playwright will record your actions and generate a test for you. Once you are done recording click the **cancel** button or close the browser window. You can then inspect your `test-1.spec.ts` file and see your generated test.
|
||||
To record a test click on the **Record new** button from the Testing sidebar. This will create a `test-1.spec.ts` file as well as open up a browser window. In the browser go to the URL you wish to test and start clicking around. Playwright will record your actions and generate the test code directly in VS Code. You can also generate assertions by choosing one of the icons in the toolbar and then clicking on an element on the page to assert against. The following assertions can be generated:
|
||||
* `'assert visibility'` to assert that an element is visible
|
||||
* `'assert text'` to assert that an element contains specific text
|
||||
* `'assert value'` to assert that an element has a specific value
|
||||
|
||||
Once you are done recording click the **cancel** button or close the browser window. You can then inspect your `test-1.spec.ts` file and see your generated test.
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
### Record at Cursor
|
||||
|
||||
To record from a specific point in your test file click the **Record at cursor** button from the Testing sidebar. This generates actions into the existing test at the current cursor position. You can run the test, position the cursor at the end of the test and continue generating the test.
|
||||
|
||||

|
||||

|
||||
|
||||
### Picking a Locator
|
||||
|
||||
Pick a [locator](./locators.md) and copy it into your test file by clicking the **Pick locator** button form the testing sidebar. Then in the browser click the element you require and it will now show up in the **Pick locator** box in VS Code. Press 'enter' on your keyboard to copy the locator into the clipboard and then paste anywhere in your code. Or press 'escape' if you want to cancel.
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
Playwright will look at your page and figure out the best locator, prioritizing [role, text and test id locators](./locators.md). If the generator finds multiple elements matching the locator, it will improve the locator to make it resilient and uniquely identify the target element, so you don't have to worry about failing tests due to locators.
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ Using [`method: Locator.setChecked`] is the easiest way to check and uncheck a c
|
|||
await page.getByLabel('I agree to the terms above').check();
|
||||
|
||||
// Assert the checked state
|
||||
expect(await page.getByLabel('Subscribe to newsletter').isChecked()).toBeTruthy();
|
||||
expect(page.getByLabel('Subscribe to newsletter')).toBeChecked();
|
||||
|
||||
// Select the radio button
|
||||
await page.getByLabel('XL').check();
|
||||
|
|
@ -101,7 +101,7 @@ await page.getByLabel('XL').check();
|
|||
page.getByLabel("I agree to the terms above").check();
|
||||
|
||||
// Assert the checked state
|
||||
assertTrue(page.getByLabel("Subscribe to newsletter").isChecked());
|
||||
assertTrue(page.getByLabel("Subscribe to newsletter")).isChecked();
|
||||
|
||||
// Select the radio button
|
||||
page.getByLabel("XL").check();
|
||||
|
|
@ -112,7 +112,7 @@ page.getByLabel("XL").check();
|
|||
await page.get_by_label('I agree to the terms above').check()
|
||||
|
||||
# Assert the checked state
|
||||
assert await page.get_by_label('Subscribe to newsletter').is_checked() is True
|
||||
await expect(page.get_by_label('Subscribe to newsletter')).to_be_checked()
|
||||
|
||||
# Select the radio button
|
||||
await page.get_by_label('XL').check()
|
||||
|
|
@ -123,7 +123,7 @@ await page.get_by_label('XL').check()
|
|||
page.get_by_label('I agree to the terms above').check()
|
||||
|
||||
# Assert the checked state
|
||||
assert page.get_by_label('Subscribe to newsletter').is_checked() is True
|
||||
expect(page.get_by_label('Subscribe to newsletter')).to_be_checked()
|
||||
|
||||
# Select the radio button
|
||||
page.get_by_label('XL').check()
|
||||
|
|
@ -134,7 +134,7 @@ page.get_by_label('XL').check()
|
|||
await page.GetByLabel("I agree to the terms above").CheckAsync();
|
||||
|
||||
// Assert the checked state
|
||||
Assert.True(await page.GetByLabel("Subscribe to newsletter").IsCheckedAsync());
|
||||
await Expect(page.GetByLabel("Subscribe to newsletter")).ToBeCheckedAsync();
|
||||
|
||||
// Select the radio button
|
||||
await page.GetByLabel("XL").CheckAsync();
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ dotnet add package Microsoft.Playwright.MSTest
|
|||
dotnet build
|
||||
```
|
||||
|
||||
4. Install required browsers by replacing `netX` with the actual output folder name, e.g. `net6.0`:
|
||||
4. Install required browsers by replacing `netX` with the actual output folder name, e.g. `net8.0`:
|
||||
|
||||
```bash
|
||||
pwsh bin/Debug/netX/playwright.ps1 install
|
||||
|
|
@ -91,7 +91,7 @@ Edit the `UnitTest1.cs` file with the code below to create an example end-to-end
|
|||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
```csharp
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Playwright;
|
||||
|
|
@ -130,7 +130,7 @@ public class Tests : PageTest
|
|||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
```csharp
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Playwright;
|
||||
|
|
@ -200,9 +200,9 @@ See our doc on [Test Runners](./test-runners.md) to learn more about running tes
|
|||
|
||||
## System requirements
|
||||
|
||||
- Playwright is distributed as a .NET Standard 2.0 library. We recommend .NET 6.
|
||||
- Playwright is distributed as a .NET Standard 2.0 library. We recommend .NET 8.
|
||||
- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL).
|
||||
- MacOS 12 Monterey or MacOS 13 Ventura.
|
||||
- MacOS 12 Monterey, MacOS 13 Ventura, or MacOS 14 Sonoma.
|
||||
- Debian 11, Debian 12, Ubuntu 20.04 or Ubuntu 22.04.
|
||||
|
||||
## What's next
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ By default browsers launched with Playwright run headless, meaning no browser UI
|
|||
|
||||
- Java 8 or higher.
|
||||
- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL).
|
||||
- MacOS 12 Monterey or MacOS 13 Ventura.
|
||||
- MacOS 12 Monterey, MacOS 13 Ventura, or MacOS 14 Sonoma.
|
||||
- Debian 11, Debian 12, Ubuntu 20.04 or Ubuntu 22.04.
|
||||
|
||||
## What's next
|
||||
|
|
|
|||
|
|
@ -115,6 +115,8 @@ To update Playwright to the latest version run the following command:
|
|||
|
||||
```bash
|
||||
npm install -D @playwright/test@latest
|
||||
# Also download new browser binaries and their dependencies:
|
||||
npx playwright install --with-deps
|
||||
```
|
||||
You can always check which version of Playwright you have by running the following command:
|
||||
|
||||
|
|
@ -124,9 +126,9 @@ npx playwright --version
|
|||
|
||||
## System requirements
|
||||
|
||||
- Node.js 16+
|
||||
- Node.js 18+
|
||||
- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL).
|
||||
- MacOS 12 Monterey or MacOS 13 Ventura.
|
||||
- MacOS 12 Monterey, MacOS 13 Ventura, or MacOS 14 Sonoma.
|
||||
- Debian 11, Debian 12, Ubuntu 20.04 or Ubuntu 22.04, with x86-64 or arm64 architecture.
|
||||
|
||||
## What's next
|
||||
|
|
|
|||
|
|
@ -6,14 +6,18 @@ title: "Installation"
|
|||
|
||||
Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation.
|
||||
|
||||
The [Playwright library](./library.md) can be used as a general purpose browser automation tool, providing a powerful set of APIs to automate web applications, for both sync and async Python.
|
||||
|
||||
This introduction describes the Playwright Pytest plugin, which is the recommended way to write end-to-end tests.
|
||||
|
||||
**You will learn**
|
||||
|
||||
- [How to install Playwright](/intro.md#installing-playwright)
|
||||
- [How to install Playwright Pytest](/intro.md#installing-playwright-pytest)
|
||||
- [How to run the example test](/intro.md#running-the-example-test)
|
||||
|
||||
## Installing Playwright
|
||||
## Installing Playwright Pytest
|
||||
|
||||
Playwright recommends using the official [Playwright Pytest plugin](./test-runners.md) to write end-to-end tests. It provides context isolation, running it on multiple browser configurations out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure with your preferred test-runner. The Pytest plugin utilizes the sync version of Playwright, there is also an async version accessible via the library.
|
||||
Playwright recommends using the official [Playwright Pytest plugin](./test-runners.md) to write end-to-end tests. It provides context isolation, running it on multiple browser configurations out of the box.
|
||||
|
||||
Get started by installing Playwright and running the example test to see it in action.
|
||||
|
||||
|
|
@ -97,7 +101,7 @@ pip install pytest-playwright playwright -U
|
|||
|
||||
- Python 3.8 or higher.
|
||||
- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL).
|
||||
- MacOS 12 Monterey or MacOS 13 Ventura.
|
||||
- MacOS 12 Monterey, MacOS 13 Ventura, or MacOS 14 Sonoma.
|
||||
- Debian 11, Debian 12, Ubuntu 20.04 or Ubuntu 22.04.
|
||||
|
||||
## What's next
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ cd PlaywrightDemo
|
|||
dotnet add package Microsoft.Playwright
|
||||
# Build the project
|
||||
dotnet build
|
||||
# Install required browsers - replace netX with actual output folder name, e.g. net6.0.
|
||||
# Install required browsers - replace netX with actual output folder name, e.g. net8.0.
|
||||
pwsh bin/Debug/netX/playwright.ps1 install
|
||||
|
||||
# If the pwsh command does not work (throws TypeNotFound), make sure to use an up-to-date version of PowerShell.
|
||||
|
|
@ -66,6 +66,9 @@ You can do the following to leverage Playwright's web-first assertions when you
|
|||
using Microsoft.Playwright;
|
||||
using static Microsoft.Playwright.Assertions;
|
||||
|
||||
// Change the default 5 seconds timeout if you'd like.
|
||||
SetDefaultExpectTimeout(10_000);
|
||||
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.Chromium.LaunchAsync();
|
||||
var page = await browser.NewPageAsync();
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ await page.GetByLabel("Password").FillAsync("secret-password");
|
|||
|
||||
await page.GetByRole(AriaRole.Button, new() { Name = "Sign in" }).ClickAsync();
|
||||
|
||||
await Expect(page.GetByText("Welcome, John!")).ToBeVisibleAsync();
|
||||
await Expect(Page.GetByText("Welcome, John!")).ToBeVisibleAsync();
|
||||
```
|
||||
|
||||
## Locating elements
|
||||
|
|
@ -245,7 +245,7 @@ page.getByRole(AriaRole.BUTTON,
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page
|
||||
await Expect(Page
|
||||
.GetByRole(AriaRole.Heading, new() { Name = "Sign up" }))
|
||||
.ToBeVisibleAsync();
|
||||
|
||||
|
|
@ -373,7 +373,7 @@ expect(page.get_by_text("Welcome, John")).to_be_visible()
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page.GetByText("Welcome, John")).ToBeVisibleAsync();
|
||||
await Expect(Page.GetByText("Welcome, John")).ToBeVisibleAsync();
|
||||
```
|
||||
|
||||
Set an exact match:
|
||||
|
|
@ -396,7 +396,7 @@ expect(page.get_by_text("Welcome, John", exact=True)).to_be_visible()
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page
|
||||
await Expect(Page
|
||||
.GetByText("Welcome, John", new() { Exact = true }))
|
||||
.ToBeVisibleAsync();
|
||||
```
|
||||
|
|
@ -424,7 +424,7 @@ expect(page.get_by_text(re.compile("welcome, john", re.IGNORECASE))).to_be_visib
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page
|
||||
await Expect(Page
|
||||
.GetByText(new Regex("welcome, john", RegexOptions.IgnoreCase)))
|
||||
.ToBeVisibleAsync();
|
||||
```
|
||||
|
|
@ -504,7 +504,7 @@ expect(page.get_by_title("Issues count")).to_have_text("25 issues")
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page.GetByTitle("Issues count")).toHaveText("25 issues");
|
||||
await Expect(Page.GetByTitle("Issues count")).toHaveText("25 issues");
|
||||
```
|
||||
|
||||
:::note When to use title locators
|
||||
|
|
@ -784,7 +784,7 @@ await expect(page.locator("x-details")).to_contain_text("Details")
|
|||
expect(page.locator("x-details")).to_contain_text("Details")
|
||||
```
|
||||
```csharp
|
||||
await Expect(page.Locator("x-details")).ToContainTextAsync("Details");
|
||||
await Expect(Page.Locator("x-details")).ToContainTextAsync("Details");
|
||||
```
|
||||
## Filtering Locators
|
||||
|
||||
|
|
@ -910,7 +910,7 @@ expect(page.get_by_role("listitem").filter(has_not_text="Out of stock")).to_have
|
|||
|
||||
```csharp
|
||||
// 5 in-stock items
|
||||
await Expect(page.getByRole(AriaRole.Listitem).Filter(new() { HasNotText = "Out of stock" }))
|
||||
await Expect(Page.getByRole(AriaRole.Listitem).Filter(new() { HasNotText = "Out of stock" }))
|
||||
.ToHaveCountAsync(5);
|
||||
```
|
||||
|
||||
|
|
@ -973,19 +973,21 @@ await page
|
|||
.ClickAsync();
|
||||
```
|
||||
|
||||
We can also assert the product card to make sure there is only one
|
||||
We can also assert the product card to make sure there is only one:
|
||||
|
||||
```js
|
||||
await expect(page
|
||||
.getByRole('listitem')
|
||||
.filter({ has: page.getByText('Product 2') }))
|
||||
.filter({ has: page.getByRole('heading', { name: 'Product 2' }) }))
|
||||
.toHaveCount(1);
|
||||
```
|
||||
|
||||
```java
|
||||
assertThat(page
|
||||
.getByRole(AriaRole.LISTITEM)
|
||||
.filter(new Locator.FilterOptions().setHas(page.getByText("Product 2")))
|
||||
.filter(new Locator.FilterOptions()
|
||||
.setHas(page.GetByRole(AriaRole.HEADING,
|
||||
new Page.GetByRoleOptions().setName("Product 2"))))
|
||||
.hasCount(1);
|
||||
```
|
||||
|
||||
|
|
@ -1006,7 +1008,7 @@ expect(
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page
|
||||
await Expect(Page
|
||||
.GetByRole(AriaRole.Listitem)
|
||||
.Filter(new() {
|
||||
Has = page.GetByRole(AriaRole.Heading, new() { Name = "Product 2" })
|
||||
|
|
@ -1014,6 +1016,55 @@ await Expect(page
|
|||
.ToHaveCountAsync(1);
|
||||
```
|
||||
|
||||
The filtering locator **must be relative** to the original locator and is queried starting with the original locator match, not the document root. Therefore, the following will not work, because the filtering locator starts matching from the `<ul>` list element that is outside of the `<li>` list item matched by the original locator:
|
||||
|
||||
```js
|
||||
// ✖ WRONG
|
||||
await expect(page
|
||||
.getByRole('listitem')
|
||||
.filter({ has: page.getByRole('list').getByText('Product 2') }))
|
||||
.toHaveCount(1);
|
||||
```
|
||||
|
||||
```java
|
||||
// ✖ WRONG
|
||||
assertThat(page
|
||||
.getByRole(AriaRole.LISTITEM)
|
||||
.filter(new Locator.FilterOptions()
|
||||
.setHas(page.GetByRole(AriaRole.LIST)
|
||||
.GetByRole(AriaRole.HEADING,
|
||||
new Page.GetByRoleOptions().setName("Product 2"))))
|
||||
.hasCount(1);
|
||||
```
|
||||
|
||||
```python async
|
||||
# ✖ WRONG
|
||||
await expect(
|
||||
page.get_by_role("listitem").filter(
|
||||
has=page.get_by_role("list").get_by_role("heading", name="Product 2")
|
||||
)
|
||||
).to_have_count(1)
|
||||
```
|
||||
|
||||
```python sync
|
||||
# ✖ WRONG
|
||||
expect(
|
||||
page.get_by_role("listitem").filter(
|
||||
has=page.get_by_role("list").get_by_role("heading", name="Product 2")
|
||||
)
|
||||
).to_have_count(1)
|
||||
```
|
||||
|
||||
```csharp
|
||||
// ✖ WRONG
|
||||
await Expect(Page
|
||||
.GetByRole(AriaRole.Listitem)
|
||||
.Filter(new() {
|
||||
Has = page.GetByRole(AriaRole.List).GetByRole(AriaRole.Heading, new() { Name = "Product 2" })
|
||||
}))
|
||||
.ToHaveCountAsync(1);
|
||||
```
|
||||
|
||||
### Filter by not having child/descendant
|
||||
|
||||
We can also filter by **not having** a matching element inside.
|
||||
|
|
@ -1049,7 +1100,7 @@ expect(
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page
|
||||
await Expect(Page
|
||||
.GetByRole(AriaRole.Listitem)
|
||||
.Filter(new() {
|
||||
HasNot = page.GetByRole(AriaRole.Heading, new() { Name = "Product 2" })
|
||||
|
|
@ -1309,7 +1360,7 @@ assertThat(page.getByRole(AriaRole.LISTITEM).hasCount(3);
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page.GetByRole(AriaRole.Listitem)).ToHaveCountAsync(3);
|
||||
await Expect(Page.GetByRole(AriaRole.Listitem)).ToHaveCountAsync(3);
|
||||
```
|
||||
|
||||
### Assert all text in a list
|
||||
|
|
@ -1349,7 +1400,7 @@ assertThat(page
|
|||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page
|
||||
await Expect(Page
|
||||
.GetByRole(AriaRole.Listitem))
|
||||
.ToHaveTextAsync(new string[] {"apple", "banana", "orange"});
|
||||
```
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ async def test_mock_the_fruit_api(page: Page):
|
|||
await page.goto("https://demo.playwright.dev/api-mocking")
|
||||
|
||||
# Assert that the Strawberry fruit is visible
|
||||
await page.get_by_text("Strawberry").to_be_visible()
|
||||
await expect(page.get_by_text("Strawberry")).to_be_visible()
|
||||
```
|
||||
|
||||
```python sync
|
||||
|
|
@ -58,7 +58,7 @@ def test_mock_the_fruit_api(page: Page):
|
|||
page.goto("https://demo.playwright.dev/api-mocking")
|
||||
|
||||
# Assert that the Strawberry fruit is visible
|
||||
page.get_by_text("Strawberry").to_be_visible()
|
||||
expect(page.get_by_text("Strawberry")).to_be_visible()
|
||||
```
|
||||
|
||||
```csharp
|
||||
|
|
@ -118,7 +118,7 @@ test('gets the json from api and adds a new fruit', async ({ page }) => {
|
|||
await page.route('*/**/api/v1/fruits', async route => {
|
||||
const response = await route.fetch();
|
||||
const json = await response.json();
|
||||
json.push({ name: 'Playwright', id: 100 });
|
||||
json.push({ name: 'Loquat', id: 100 });
|
||||
// Fulfill using the original response, while patching the response body
|
||||
// with the given JSON object.
|
||||
await route.fulfill({ response, json });
|
||||
|
|
@ -128,67 +128,67 @@ test('gets the json from api and adds a new fruit', async ({ page }) => {
|
|||
await page.goto('https://demo.playwright.dev/api-mocking');
|
||||
|
||||
// Assert that the new fruit is visible
|
||||
await expect(page.getByText('Playwright', { exact: true })).toBeVisible();
|
||||
await expect(page.getByText('Loquat', { exact: true })).toBeVisible();
|
||||
});
|
||||
```
|
||||
|
||||
```python async
|
||||
async def test_gets_the_json_from_api_and_adds_a_new_fruit(page: Page):
|
||||
async def handle(route: Route):
|
||||
response = await route.fulfill()
|
||||
response = await route.fetch()
|
||||
json = await response.json()
|
||||
json.append({ "name": "Playwright", "id": 100})
|
||||
json.append({ "name": "Loquat", "id": 100})
|
||||
# Fulfill using the original response, while patching the response body
|
||||
# with the given JSON object.
|
||||
await route.fulfill(response=response, json=json)
|
||||
|
||||
await page.route("https://dog.ceo/api/breeds/list/all", handle)
|
||||
await page.route("https://demo.playwright.dev/api-mocking/api/v1/fruits", handle)
|
||||
|
||||
# Go to the page
|
||||
await page.goto("https://demo.playwright.dev/api-mocking")
|
||||
|
||||
# Assert that the new fruit is visible
|
||||
await page.get_by_text("Playwright", exact=True).to_be_visible()
|
||||
await expect(page.get_by_text("Loquat", exact=True)).to_be_visible()
|
||||
```
|
||||
|
||||
```python sync
|
||||
def test_gets_the_json_from_api_and_adds_a_new_fruit(page: Page):
|
||||
def handle(route: Route):
|
||||
response = route.fulfill()
|
||||
response = route.fetch()
|
||||
json = response.json()
|
||||
json.append({ "name": "Playwright", "id": 100})
|
||||
json.append({ "name": "Loquat", "id": 100})
|
||||
# Fulfill using the original response, while patching the response body
|
||||
# with the given JSON object.
|
||||
route.fulfill(response=response, json=json)
|
||||
|
||||
page.route("https://dog.ceo/api/breeds/list/all", handle)
|
||||
page.route("https://demo.playwright.dev/api-mocking/api/v1/fruits", handle)
|
||||
|
||||
# Go to the page
|
||||
page.goto("https://demo.playwright.dev/api-mocking")
|
||||
|
||||
# Assert that the new fruit is visible
|
||||
page.get_by_text("Playwright", exact=True).to_be_visible()
|
||||
expect(page.get_by_text("Loquat", exact=True)).to_be_visible()
|
||||
```
|
||||
|
||||
```csharp
|
||||
await page.RouteAsync("*/**/api/v1/fruits", async (route) => {
|
||||
var response = await route.FetchAsync();
|
||||
var fruits = await response.JsonAsync<Fruit[]>();
|
||||
fruits.Add(new Fruit() { Name = "Playwright", Id = 100 });
|
||||
fruits.Add(new Fruit() { Name = "Loquat", Id = 100 });
|
||||
// Fulfill using the original response, while patching the response body
|
||||
// with the given JSON object.
|
||||
await route.FulfillAsync(new ()
|
||||
{
|
||||
Response = response,
|
||||
Json = json
|
||||
Json = fruits
|
||||
});
|
||||
}
|
||||
);
|
||||
// Go to the page
|
||||
await page.GotoAsync("https://demo.playwright.dev/api-mocking");
|
||||
|
||||
// Assert that the Strawberry fruit is visible
|
||||
await Expect(page.GetByTextAsync("Playwright", new () { Exact = true })).ToBeVisibleAsync();
|
||||
// Assert that the Loquat fruit is visible
|
||||
await Expect(page.GetByTextAsync("Loquat", new () { Exact = true })).ToBeVisibleAsync();
|
||||
```
|
||||
|
||||
```java
|
||||
|
|
@ -196,7 +196,7 @@ page.route("*/**/api/v1/fruits", route -> {
|
|||
Response response = route.fetch();
|
||||
byte[] json = response.body();
|
||||
parsed = new Gson().fromJson(json, JsonObject.class)
|
||||
parsed.add(new JsonObject().add("name", "Playwright").add("id", 100));
|
||||
parsed.add(new JsonObject().add("name", "Loquat").add("id", 100));
|
||||
// Fulfill using the original response, while patching the response body
|
||||
// with the given JSON object.
|
||||
route.fulfill(new Route.FulfillOptions().setResponse(response).setBody(json.toString()));
|
||||
|
|
@ -205,8 +205,8 @@ page.route("*/**/api/v1/fruits", route -> {
|
|||
// Go to the page
|
||||
page.goto("https://demo.playwright.dev/api-mocking");
|
||||
|
||||
// Assert that the Strawberry fruit is visible
|
||||
assertThat(page.getByText("Playwright", new Page.GetByTextOptions().setExact(true))).isVisible();
|
||||
// Assert that the Loquat fruit is visible
|
||||
assertThat(page.getByText("Loquat", new Page.GetByTextOptions().setExact(true))).isVisible();
|
||||
```
|
||||
|
||||
In the trace of our test we can see that the API was called and the response was modified.
|
||||
|
|
@ -249,7 +249,7 @@ test('records or updates the HAR file', async ({ page }) => {
|
|||
```
|
||||
|
||||
```python async
|
||||
async def records_or_updates_the_har_file(page: Page):
|
||||
async def test_records_or_updates_the_har_file(page: Page):
|
||||
# Get the response from the HAR file
|
||||
await page.route_from_har("./hars/fruit.har", url="*/**/api/v1/fruits", update=True)
|
||||
|
||||
|
|
@ -261,7 +261,7 @@ async def records_or_updates_the_har_file(page: Page):
|
|||
```
|
||||
|
||||
```python sync
|
||||
def records_or_updates_the_har_file(page: Page):
|
||||
def test_records_or_updates_the_har_file(page: Page):
|
||||
# Get the response from the HAR file
|
||||
page.route_from_har("./hars/fruit.har", url="*/**/api/v1/fruits", update=True)
|
||||
|
||||
|
|
@ -347,7 +347,7 @@ async def test_gets_the_json_from_har_and_checks_the_new_fruit_has_been_added(pa
|
|||
await page.goto("https://demo.playwright.dev/api-mocking")
|
||||
|
||||
# Assert that the Playwright fruit is visible
|
||||
await page.get_by_text("Playwright", exact=True).to_be_visible()
|
||||
await expect(page.get_by_text("Playwright", exact=True)).to_be_visible()
|
||||
```
|
||||
|
||||
```python sync
|
||||
|
|
@ -361,7 +361,7 @@ def test_gets_the_json_from_har_and_checks_the_new_fruit_has_been_added(page: Pa
|
|||
page.goto("https://demo.playwright.dev/api-mocking")
|
||||
|
||||
# Assert that the Playwright fruit is visible
|
||||
page.get_by_text("Playwright", exact=True).to_be_visible()
|
||||
expect(page.get_by_text("Playwright", exact=True)).to_be_visible()
|
||||
```
|
||||
|
||||
```csharp
|
||||
|
|
@ -425,7 +425,7 @@ mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="op
|
|||
|
||||
```bash python
|
||||
# Save API requests from example.com as "example.har" archive.
|
||||
playwright open --save-har=example.har --save-har-glob="**/api/**" https://example.coms
|
||||
playwright open --save-har=example.har --save-har-glob="**/api/**" https://example.com
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
|
|
|
|||
|
|
@ -495,10 +495,10 @@ await page.GotoAsync("https://example.com");
|
|||
|
||||
```js
|
||||
// Delete header
|
||||
await page.route('**/*', route => {
|
||||
await page.route('**/*', async route => {
|
||||
const headers = route.request().headers();
|
||||
delete headers['X-Secret'];
|
||||
route.continue({ headers });
|
||||
await route.continue({ headers });
|
||||
});
|
||||
|
||||
// Continue requests as POST.
|
||||
|
|
@ -522,7 +522,7 @@ page.route("**/*", route -> route.resume(new Route.ResumeOptions().setMethod("PO
|
|||
async def handle_route(route):
|
||||
headers = route.request.headers
|
||||
del headers["x-secret"]
|
||||
route.continue_(headers=headers)
|
||||
await route.continue_(headers=headers)
|
||||
await page.route("**/*", handle_route)
|
||||
|
||||
# Continue requests as POST.
|
||||
|
|
@ -617,7 +617,7 @@ await page.route('**/title.html', async route => {
|
|||
// Add a prefix to the title.
|
||||
let body = await response.text();
|
||||
body = body.replace('<title>', '<title>My prefix:');
|
||||
route.fulfill({
|
||||
await route.fulfill({
|
||||
// Pass all fields from the response.
|
||||
response,
|
||||
// Override response body.
|
||||
|
|
|
|||
|
|
@ -726,7 +726,7 @@ expect(page.locator("label")).to_have_text("Password")
|
|||
|
||||
```csharp
|
||||
// Fill the input by targeting the label.
|
||||
await Expect(page.Locator("label")).ToHaveTextAsync("Password");
|
||||
await Expect(Page.Locator("label")).ToHaveTextAsync("Password");
|
||||
```
|
||||
|
||||
## Legacy text locator
|
||||
|
|
@ -884,11 +884,6 @@ await page.Locator("data-test-id=submit").ClickAsync();
|
|||
Attribute selectors are not CSS selectors, so anything CSS-specific like `:enabled` is not supported. For more features, use a proper [css] selector, e.g. `css=[data-test="login"]:enabled`.
|
||||
:::
|
||||
|
||||
:::note
|
||||
Attribute selectors pierce shadow DOM. To opt-out from this behavior, use `:light` suffix after attribute, for example `page.locator('data-test-id:light=submit').click()`
|
||||
:::
|
||||
|
||||
|
||||
## Chaining selectors
|
||||
|
||||
:::warning
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ test.describe('Playwright homepage', () => {
|
|||
1. Each Playwright Test file has explicit import of the `test` and `expect` functions
|
||||
1. Test function is marked with `async`
|
||||
1. Playwright Test is given a `page` as one of its parameters. This is one of the many [useful fixtures](./api/class-fixtures) in Playwright Test.
|
||||
Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll#1`] and close it in [`method: Test.afterAll#1`].
|
||||
Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll`] and close it in [`method: Test.afterAll`].
|
||||
1. Locator creation with [`method: Page.locator`] is one of the few methods that is sync.
|
||||
1. Use [assertions](./test-assertions) to verify the state instead of `page.$eval()`.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,69 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.41
|
||||
|
||||
### New APIs
|
||||
|
||||
- New method [`method: Page.unrouteAll`] removes all routes registered by [`method: Page.route`] and [`method: Page.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them.
|
||||
- New method [`method: BrowserContext.unrouteAll`] removes all routes registered by [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them.
|
||||
- New option [`option: style`] in [`method: Page.screenshot`] and [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 121.0.6167.57
|
||||
* Mozilla Firefox 121.0
|
||||
* WebKit 17.4
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 120
|
||||
* Microsoft Edge 120
|
||||
|
||||
## Version 1.40
|
||||
|
||||
### Test Generator Update
|
||||
|
||||

|
||||
|
||||
New tools to generate assertions:
|
||||
- "Assert visibility" tool generates [`method: LocatorAssertions.toBeVisible`].
|
||||
- "Assert value" tool generates [`method: LocatorAssertions.toHaveValue`].
|
||||
- "Assert text" tool generates [`method: LocatorAssertions.toContainText`].
|
||||
|
||||
Here is an example of a generated test with assertions:
|
||||
|
||||
```csharp
|
||||
await Page.GotoAsync("https://playwright.dev/");
|
||||
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
|
||||
await Expect(Page.GetByLabel("Breadcrumbs").GetByRole(AriaRole.List)).ToContainTextAsync("Installation");
|
||||
await Expect(Page.GetByLabel("Search")).ToBeVisibleAsync();
|
||||
await Page.GetByLabel("Search").ClickAsync();
|
||||
await Page.GetByPlaceholder("Search docs").FillAsync("locator");
|
||||
await Expect(Page.GetByPlaceholder("Search docs")).ToHaveValueAsync("locator");
|
||||
```
|
||||
|
||||
### New APIs
|
||||
|
||||
- Option [`option: reason`] in [`method: Page.close`], [`method: BrowserContext.close`] and [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure.
|
||||
- Option [`option: firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`].
|
||||
|
||||
### Other Changes
|
||||
|
||||
- Methods [`method: Download.path`] and [`method: Download.createReadStream`] throw an error for failed and cancelled downloads.
|
||||
- Playwright [docker image](./docker.md) now comes with .NET 8 (new LTS).
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 120.0.6099.28
|
||||
* Mozilla Firefox 119.0
|
||||
* WebKit 17.4
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 119
|
||||
* Microsoft Edge 119
|
||||
|
||||
## Version 1.39
|
||||
|
||||
Evergreen browsers update.
|
||||
|
|
@ -373,7 +436,7 @@ await page.GetByLabel("Password").FillAsync("secret-password");
|
|||
|
||||
await page.GetByRole(AriaRole.Button, new() { NameString = "Sign in" }).ClickAsync();
|
||||
|
||||
await Expect(page.GetByText("Welcome, John!")).ToBeVisibleAsync();
|
||||
await Expect(Page.GetByText("Welcome, John!")).ToBeVisibleAsync();
|
||||
```
|
||||
|
||||
All the same methods are also available on [Locator], [FrameLocator] and [Frame] classes.
|
||||
|
|
@ -387,7 +450,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)).ToHaveAttribute("disabled", "");
|
||||
await Expect(Page.GetByRole(AriaRole.Button)).ToHaveAttribute("disabled", "");
|
||||
```
|
||||
|
||||
### Browser Versions
|
||||
|
|
|
|||
|
|
@ -4,6 +4,68 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.41
|
||||
|
||||
### New APIs
|
||||
|
||||
- New method [`method: Page.unrouteAll`] removes all routes registered by [`method: Page.route`] and [`method: Page.routeFromHAR`].
|
||||
- New method [`method: BrowserContext.unrouteAll`] removes all routes registered by [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`].
|
||||
- New option [`option: style`] in [`method: Page.screenshot`] and [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 121.0.6167.57
|
||||
* Mozilla Firefox 121.0
|
||||
* WebKit 17.4
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 120
|
||||
* Microsoft Edge 120
|
||||
|
||||
## Version 1.40
|
||||
|
||||
### Test Generator Update
|
||||
|
||||

|
||||
|
||||
New tools to generate assertions:
|
||||
- "Assert visibility" tool generates [`method: LocatorAssertions.toBeVisible`].
|
||||
- "Assert value" tool generates [`method: LocatorAssertions.toHaveValue`].
|
||||
- "Assert text" tool generates [`method: LocatorAssertions.toContainText`].
|
||||
|
||||
Here is an example of a generated test with assertions:
|
||||
|
||||
```java
|
||||
page.navigate("https://playwright.dev/");
|
||||
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("Get started")).click();
|
||||
assertThat(page.getByLabel("Breadcrumbs").getByRole(AriaRole.LIST)).containsText("Installation");
|
||||
assertThat(page.getByLabel("Search")).isVisible();
|
||||
page.getByLabel("Search").click();
|
||||
page.getByPlaceholder("Search docs").fill("locator");
|
||||
assertThat(page.getByPlaceholder("Search docs")).hasValue("locator");
|
||||
```
|
||||
|
||||
### New APIs
|
||||
|
||||
- Option [`option: reason`] in [`method: Page.close`], [`method: BrowserContext.close`] and [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure.
|
||||
- Option [`option: firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`].
|
||||
|
||||
### Other Changes
|
||||
|
||||
- Methods [`method: Download.path`] and [`method: Download.createReadStream`] throw an error for failed and cancelled downloads.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 120.0.6099.28
|
||||
* Mozilla Firefox 119.0
|
||||
* WebKit 17.4
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 119
|
||||
* Microsoft Edge 119
|
||||
|
||||
## Version 1.39
|
||||
|
||||
Evergreen browsers update.
|
||||
|
|
|
|||
|
|
@ -6,6 +6,80 @@ toc_max_heading_level: 2
|
|||
|
||||
import LiteYouTube from '@site/src/components/LiteYouTube';
|
||||
|
||||
## Version 1.41
|
||||
|
||||
### New APIs
|
||||
|
||||
- New method [`method: Page.unrouteAll`] removes all routes registered by [`method: Page.route`] and [`method: Page.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them.
|
||||
- New method [`method: BrowserContext.unrouteAll`] removes all routes registered by [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them.
|
||||
- New option [`option: style`] in [`method: Page.screenshot`] and [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot.
|
||||
- New option `stylePath` for methods [`method: PageAssertions.toHaveScreenshot#1`] and [`method: LocatorAssertions.toHaveScreenshot#1`] to apply a custom stylesheet while making the screenshot.
|
||||
- New `fileName` option for [Blob reporter](./test-reporters#blob-reporter), to specify the name of the report to be created.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 121.0.6167.57
|
||||
* Mozilla Firefox 121.0
|
||||
* WebKit 17.4
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 120
|
||||
* Microsoft Edge 120
|
||||
|
||||
## Version 1.40
|
||||
|
||||
<LiteYouTube
|
||||
id="mn892dV81_8"
|
||||
title="Playwright 1.40"
|
||||
/>
|
||||
|
||||
### Test Generator Update
|
||||
|
||||

|
||||
|
||||
New tools to generate assertions:
|
||||
- "Assert visibility" tool generates [`method: LocatorAssertions.toBeVisible`].
|
||||
- "Assert value" tool generates [`method: LocatorAssertions.toHaveValue`].
|
||||
- "Assert text" tool generates [`method: LocatorAssertions.toContainText`].
|
||||
|
||||
Here is an example of a generated test with assertions:
|
||||
|
||||
```js
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('test', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
await page.getByRole('link', { name: 'Get started' }).click();
|
||||
await expect(page.getByLabel('Breadcrumbs').getByRole('list')).toContainText('Installation');
|
||||
await expect(page.getByLabel('Search')).toBeVisible();
|
||||
await page.getByLabel('Search').click();
|
||||
await page.getByPlaceholder('Search docs').fill('locator');
|
||||
await expect(page.getByPlaceholder('Search docs')).toHaveValue('locator');
|
||||
});
|
||||
```
|
||||
|
||||
### New APIs
|
||||
|
||||
- Option [`option: reason`] in [`method: Page.close`], [`method: BrowserContext.close`] and [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure.
|
||||
- Option [`option: firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`].
|
||||
|
||||
### Other Changes
|
||||
|
||||
- Methods [`method: Download.path`] and [`method: Download.createReadStream`] throw an error for failed and cancelled downloads.
|
||||
- Playwright [docker image](./docker.md) now comes with Node.js v20.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 120.0.6099.28
|
||||
* Mozilla Firefox 119.0
|
||||
* WebKit 17.4
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 119
|
||||
* Microsoft Edge 119
|
||||
|
||||
## Version 1.39
|
||||
|
||||
<LiteYouTube
|
||||
|
|
@ -30,7 +104,7 @@ test('pass', async ({ page }) => {
|
|||
});
|
||||
```
|
||||
|
||||
See the documentation [for a full example](./test-configuration.md#add-custom-matchers-using-expectextend).
|
||||
See the documentation [for a full example](./test-assertions.md#add-custom-matchers-using-expectextend).
|
||||
|
||||
### Merge test fixtures
|
||||
|
||||
|
|
@ -1036,7 +1110,7 @@ Linux support looks like this:
|
|||
|
||||
### 🕵️ Anonymous Describe
|
||||
|
||||
It is now possible to call [`method: Test.describe#2`] to create suites without a title. This is useful for giving a group of tests a common option with [`method: Test.use`].
|
||||
It is now possible to call [`method: Test.describe`] to create suites without a title. This is useful for giving a group of tests a common option with [`method: Test.use`].
|
||||
|
||||
```ts
|
||||
test.describe(() => {
|
||||
|
|
@ -1159,16 +1233,16 @@ test.beforeEach(async ({ page }) => {
|
|||
await page.route('**/*', async route => {
|
||||
const headers = await route.request().allHeaders();
|
||||
delete headers['if-none-match'];
|
||||
route.fallback({ headers });
|
||||
await route.fallback({ headers });
|
||||
});
|
||||
});
|
||||
|
||||
test('should work', async ({ page }) => {
|
||||
await page.route('**/*', route => {
|
||||
await page.route('**/*', async route => {
|
||||
if (route.request().resourceType() === 'image')
|
||||
route.abort();
|
||||
await route.abort();
|
||||
else
|
||||
route.fallback();
|
||||
await route.fallback();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
|
@ -1446,7 +1520,7 @@ This version was also tested against the following stable channels:
|
|||
|
||||
Read more in [our documentation](./test-assertions#soft-assertions)
|
||||
|
||||
- You can now specify a **custom error message** as a second argument to the `expect` and `expect.soft` functions, for example:
|
||||
- You can now specify a **custom expect message** as a second argument to the `expect` and `expect.soft` functions, for example:
|
||||
|
||||
```js
|
||||
await expect(page.locator('text=Name'), 'should be logged in').toBeVisible();
|
||||
|
|
@ -1739,7 +1813,7 @@ test('response interception', async ({ page }) => {
|
|||
const response = await page._request.fetch(route.request());
|
||||
const image = await jimp.read(await response.body());
|
||||
await image.blur(5);
|
||||
route.fulfill({
|
||||
await route.fulfill({
|
||||
response,
|
||||
body: await image.getBufferAsync('image/jpeg'),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,71 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.41
|
||||
|
||||
### New APIs
|
||||
|
||||
- New method [`method: Page.unrouteAll`] removes all routes registered by [`method: Page.route`] and [`method: Page.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them.
|
||||
- New method [`method: BrowserContext.unrouteAll`] removes all routes registered by [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`]. Optionally allows to wait for ongoing routes to finish, or ignore any errors from them.
|
||||
- New option [`option: style`] in [`method: Page.screenshot`] and [`method: Locator.screenshot`] to add custom CSS to the page before taking a screenshot.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 121.0.6167.57
|
||||
* Mozilla Firefox 121.0
|
||||
* WebKit 17.4
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 120
|
||||
* Microsoft Edge 120
|
||||
|
||||
## Version 1.40
|
||||
|
||||
### Test Generator Update
|
||||
|
||||

|
||||
|
||||
New tools to generate assertions:
|
||||
- "Assert visibility" tool generates [`method: LocatorAssertions.toBeVisible`].
|
||||
- "Assert value" tool generates [`method: LocatorAssertions.toHaveValue`].
|
||||
- "Assert text" tool generates [`method: LocatorAssertions.toContainText`].
|
||||
|
||||
Here is an example of a generated test with assertions:
|
||||
|
||||
```python
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
def test_example(page: Page) -> None:
|
||||
page.goto("https://playwright.dev/")
|
||||
page.get_by_role("link", name="Get started").click()
|
||||
expect(page.get_by_label("Breadcrumbs").get_by_role("list")).to_contain_text("Installation")
|
||||
expect(page.get_by_label("Search")).to_be_visible()
|
||||
page.get_by_label("Search").click()
|
||||
page.get_by_placeholder("Search docs").fill("locator")
|
||||
expect(page.get_by_placeholder("Search docs")).to_have_value("locator");
|
||||
```
|
||||
|
||||
### New APIs
|
||||
|
||||
- Option [`option: reason`] in [`method: Page.close`], [`method: BrowserContext.close`] and [`method: Browser.close`]. Close reason is reported for all operations interrupted by the closure.
|
||||
- Option [`option: firefoxUserPrefs`] in [`method: BrowserType.launchPersistentContext`].
|
||||
|
||||
### Other Changes
|
||||
|
||||
- Method [`method: Download.path`] throws an error for failed and cancelled downloads.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
* Chromium 120.0.6099.28
|
||||
* Mozilla Firefox 119.0
|
||||
* WebKit 17.4
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
* Google Chrome 119
|
||||
* Microsoft Edge 119
|
||||
|
||||
## Version 1.39
|
||||
|
||||
Evergreen browsers update.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ title: "Running and debugging tests"
|
|||
|
||||
## Introduction
|
||||
|
||||
You can run a single test, a set of tests or all tests. Tests can be run on different browsers. By default tests are run in a headless manner meaning no browser window will be opened while running the tests and results will be seen in the terminal. If you prefer you can run your tests in headed mode by using the `headless` test run parameter.
|
||||
You can run a single test, a set of tests or all tests. Tests can be run on different browsers. By default, tests are run in a headless manner, meaning no browser window will be opened while running the tests and results will be seen in the terminal. If you prefer, you can run your tests in headed mode by using the `headless` test run parameter.
|
||||
|
||||
- Running all tests
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ You can run a single test, a set of tests or all tests. Tests can be run on diff
|
|||
|
||||
- Running Tests on multiple browsers
|
||||
|
||||
To run your test on multiple browsers or configurations you need to invoke the `dotnet test` command multiple times. There you can then either specify the `BROWSER` environment variable or set the `Playwright.BrowserName` via the runsettings file:
|
||||
To run your test on multiple browsers or configurations, you need to invoke the `dotnet test` command multiple times. There you can then either specify the `BROWSER` environment variable or set the `Playwright.BrowserName` via the runsettings file:
|
||||
|
||||
```bash
|
||||
dotnet test --settings:chromium.runsettings
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue