1
0
mirror of synced 2026-05-23 03:03:15 +00:00

Compare commits

...

38 Commits

Author SHA1 Message Date
Yury Semikhatsky 3b56a714f6 cherry-pick(#1534): fix(docs): generate javadocs (#1535)
Reference https://github.com/microsoft/playwright-java/issues/1533
2024-04-03 13:36:24 -07:00
Yury Semikhatsky ccffa01154 chore: set release version to 1.42.0 (#1512) 2024-03-11 17:00:17 -07:00
dependabot[bot] 91c3264c75 chore(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.1.0 to 3.2.0 (#1509) 2024-03-11 16:37:53 -07:00
Yury Semikhatsky 419c378e0f chore: roll 1.42.1 (#1511) 2024-03-11 16:29:30 -07:00
Yury Semikhatsky 270bc99420 docs: junit fixture javadocs (#1510)
Reference https://github.com/microsoft/playwright-java/issues/1369
2024-03-11 16:03:27 -07:00
Yury Semikhatsky 942a281f15 chore: add missing license headers (#1508) 2024-03-11 13:01:45 -07:00
uchagani 3417717c62 Convert TestBrowser to use fixtures (#1506) 2024-03-11 10:12:00 -07:00
Max Schmitt f98ad20dcc devops: mark Docker images as EOL (#1505) 2024-03-07 20:18:20 +01:00
Yury Semikhatsky 8fb21af3ff chore: roll 1.42.0 (#1502) 2024-02-27 12:30:28 -08:00
Yury Semikhatsky 049493c242 chore: roll driver to 1.42.0-beta-1708994059000 (#1501) 2024-02-26 18:21:25 -08:00
uchagani be06d1e7db feat(junit): Added ignoreHttpsErrors option (#1500)
Added ignoreHttpsErrors option
2024-02-26 17:02:21 -08:00
Yury Semikhatsky b9b3552cc4 chore: roll driver, fix canSpecifyPreinstalledNodeJsAsEnv on windows (#1496) 2024-02-20 10:58:32 -08:00
uchagani 1b2ac3f2c3 chore: Fix typo in PlaywrightRegistry (#1497)
Fix typo in PlaywrightRegistry
2024-02-17 08:59:32 -08:00
Yury Semikhatsky e8e076310a chore: track Playwright instances in PlaywrightRegistry (#1495) 2024-02-16 19:19:03 -08:00
uchagani 4a07105d8b feat(junit): Close Playwright after test run (#1492) 2024-02-16 18:11:22 -08:00
Yury Semikhatsky 7f2a5fe9af feat: roll 1.42.0-alpha driver, implement page.addLocatorHandler (#1494) 2024-02-15 08:49:45 -08:00
Jean-François Greffier 99ab89917f fix: remove remaining JUnit Option getter (#1493) 2024-02-14 17:20:30 -08:00
Yury Semikhatsky bd97c96707 feat(junit): testIdAttribute option (#1491) 2024-02-12 16:13:53 -08:00
Yury Semikhatsky d38bae17d3 chore: send testIdAttributeName to server (#1490) 2024-02-12 15:33:51 -08:00
Yury Semikhatsky 23aa10690e chore: custom test id per playwright instance (#1489) 2024-02-12 14:28:14 -08:00
Yury Semikhatsky 72b36b46e2 chore: use latest JDK LTS version in docker (#1488) 2024-02-12 12:47:19 -08:00
Jean-François Greffier 70a8577f6f remove JUnit Options getters (#1487) 2024-02-12 10:24:55 -08:00
Jean-François Greffier de1d6a6b36 JUnit minor fixes (#1486) 2024-02-09 17:16:01 -08:00
Yury Semikhatsky 36705817bf devops: add maven dependabot (#1485) 2024-02-09 12:16:57 -08:00
Max Schmitt 8286f8a9d8 chore: bump dependencies (#1484) 2024-02-09 20:48:01 +01:00
Max Schmitt 906d947c26 devops: bump Docker Maven to v3.9.6 (#1482) 2024-02-09 09:21:35 -08:00
Max Schmitt e26a2710bd devops: simplify Docker publishing (#1481) 2024-02-09 14:54:18 +01:00
Yury Semikhatsky dbe1d30feb chore: move junit tests into junit package (#1480) 2024-02-08 11:56:04 -08:00
Yury Semikhatsky 4121a28299 test(junit): restore browser channel test (#1479)
Fixes https://github.com/microsoft/playwright-java/issues/1478
2024-02-08 11:27:34 -08:00
Max Schmitt 0ad604f058 test: do not rely on channel: chrome (#1477) 2024-02-07 18:47:12 +01:00
Max Schmitt d72000c793 devops: update guardian suppression file (#1476) 2024-02-06 22:51:42 +01:00
Max Schmitt 9ef73b389b devops: migrate to 1ES PT (#1475) 2024-02-06 12:05:52 -08:00
Yury Semikhatsky da2501af49 devops: update issue templates (#1474) 2024-02-05 11:25:55 -08:00
Yury Semikhatsky 7ce6c7f0a0 feat: support device name in playwright fixtures (#1472)
Reference https://github.com/microsoft/playwright-java/issues/1369
Reference https://github.com/microsoft/playwright-java/issues/939
2024-02-04 19:07:24 -08:00
Yury Semikhatsky 197ee801ad fix: put file payloads into "payloads" protocol field (#1469)
Fixes https://github.com/microsoft/playwright-java/issues/1468
2024-02-01 11:54:18 -08:00
Max Schmitt 77e59999ab test: should serialize storageState with lone surrogates (#1464) 2024-01-29 20:21:13 +01:00
uchagani c4e1f898e6 Remove setters from Options. Add tests for custom fixtures (#1436) 2024-01-23 17:36:37 -08:00
Yury Semikhatsky ffe2bd4a96 chore: set dev version to 1.42 (#1454) 2024-01-17 16:27:02 -08:00
123 changed files with 2826 additions and 480 deletions
+115
View File
@@ -0,0 +1,115 @@
{
"hydrated": false,
"properties": {
"helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/suppressions",
"hydrationStatus": "This file does not contain identifying data. It is safe to check into your repo. To hydrate this file with identifying data, run `guardian hydrate --help` and follow the guidance."
},
"version": "1.0.0",
"suppressionSets": {
"default": {
"name": "default",
"createdDate": "2024-02-06 20:37:57Z",
"lastUpdatedDate": "2024-02-06 20:37:57Z"
}
},
"results": {
"de854c6ab9b27b1ec642cec1a51059b92f88815a231c1c8f4b8e71d9e378f174": {
"signature": "de854c6ab9b27b1ec642cec1a51059b92f88815a231c1c8f4b8e71d9e378f174",
"alternativeSignatures": [
"79827cf2e75a7f99eec716562fb9fa0d49014ac96b7afc76d29d79a33e8edd94"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"ebe0026db71ae4bb2d8e76b706198003079a5c095658de8dd49e67f7e572ea46": {
"signature": "ebe0026db71ae4bb2d8e76b706198003079a5c095658de8dd49e67f7e572ea46",
"alternativeSignatures": [],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"bb02ef965dce40b6c950b6d0cf8a9f41c08c5e33f17268fb208f3687b11ffa26": {
"signature": "bb02ef965dce40b6c950b6d0cf8a9f41c08c5e33f17268fb208f3687b11ffa26",
"alternativeSignatures": [
"3a8630eaccc2c5c39ae78836bb115bf55557058bad206fdad7dbe4f0ae466ed1"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"dffa3e24f6dc7542d741b2fab22eec657aca441a51cee515ca9bb4932995a416": {
"signature": "dffa3e24f6dc7542d741b2fab22eec657aca441a51cee515ca9bb4932995a416",
"alternativeSignatures": [
"56a4b454840e2c21d44a76d9ab09fde76fba682ce7508a3f6abf7b411d389e56"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"5dbcaaac8cdccc6df557cbde94e5d7e0f1c8a67546ba82a1b85b629030f3d311": {
"signature": "5dbcaaac8cdccc6df557cbde94e5d7e0f1c8a67546ba82a1b85b629030f3d311",
"alternativeSignatures": [
"fdb8e8af8d5697f767d0e55194f91d4a089d981555e00d058ba866acb8602014"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"b7db026b2a60bdb63af1c0938ac16d9414bb4d38e9efdc51626e12aa55f6b72f": {
"signature": "b7db026b2a60bdb63af1c0938ac16d9414bb4d38e9efdc51626e12aa55f6b72f",
"alternativeSignatures": [
"bf7bb897e644659827d8dc8d55f719296d3ab94dc750b666ce90d8057361d9d5"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"aa702d85554c352e3016e3ec3d790df489833dc97e3733c58b57915ec125f47b": {
"signature": "aa702d85554c352e3016e3ec3d790df489833dc97e3733c58b57915ec125f47b",
"alternativeSignatures": [
"948787329d52a66b4691383c81af407b03e68344a6685bbab3cfa8eb8db34f81"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"38174b5c9b474c3c0278d4c4173c14022e03dec3dc4db5ced51d2ca8459e7f2a": {
"signature": "38174b5c9b474c3c0278d4c4173c14022e03dec3dc4db5ced51d2ca8459e7f2a",
"alternativeSignatures": [
"a9efa99679f3966da96a8b89f1250576cfceda774556d43ea89ea84289967276"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"08a4e1be4662d897e5b2b7a7dc6d6901405462cd2d5f8ad433c13e12a8503fb6": {
"signature": "08a4e1be4662d897e5b2b7a7dc6d6901405462cd2d5f8ad433c13e12a8503fb6",
"alternativeSignatures": [
"009132f89939956a3f4471bb13353394015c9a7e675d118aa427e75f05658e05"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
},
"551d69737553150380e58bc9a8ae1e8f17da931561ea4e195781fb8cad225188": {
"signature": "551d69737553150380e58bc9a8ae1e8f17da931561ea4e195781fb8cad225188",
"alternativeSignatures": [
"617ad56322c5fb82a162b0b583e676d66a8e76b0d444aaab487865e4bf89e83b"
],
"memberOf": [
"default"
],
"createdDate": "2024-02-06 20:37:57Z"
}
}
}
+78 -59
View File
@@ -1,62 +1,81 @@
trigger:
none
# don't trigger for Pull Requests
trigger: none
pr: none
pool:
vmImage: ubuntu-22.04
resources:
repositories:
- repository: 1esPipelines
type: git
name: 1ESPipelineTemplates/1ESPipelineTemplates
ref: refs/tags/release
steps:
- bash: |
if [[ ! "$CURRENT_BRANCH" =~ ^release-.* ]]; then
echo "Can only publish from a release branch."
echo "Unexpected branch name: $CURRENT_BRANCH"
exit 1
fi
env:
CURRENT_BRANCH: ${{ variables['Build.SourceBranchName'] }}
displayName: "Check the branch is a release branch"
- bash: |
echo "importing GPG key:"
# Pipeline variables do not preserve line ends so we use base64 instead of --armored as a workaround.
echo $GPG_PRIVATE_KEY_BASE64 | base64 -d | gpg --batch --import
echo "list keys after import:"
gpg --list-keys
env:
GPG_PRIVATE_KEY_BASE64: $(GPG_PRIVATE_KEY_BASE64) # secret variable has to be mapped to an env variable
displayName: "Import gpg key"
- bash: ./scripts/download_driver_for_all_platforms.sh
displayName: 'Download driver'
- bash: mvn -B deploy -D skipTests --no-transfer-progress --activate-profiles release -D gpg.passphrase=$GPG_PASSPHRASE -DaltDeploymentRepository=snapshot-repo::default::file:$(pwd)/local-build
displayName: 'Build and deploy to a local directory'
env:
GPG_PASSPHRASE: $(GPG_PASSPHRASE) # secret variable has to be mapped to an env variable
- bash: |
for file in $(find snapshots -type f); do
echo "processing: $file"
if [[ $file =~ \.(md5|sha1|sha256)$ ]]; then
continue
fi
sha256sum "$file" | cut -f1 -d \ > "$file.sha256"
done
displayName: 'Create .sha256 files'
- task: EsrpRelease@4
inputs:
ConnectedServiceName: 'Playwright-ESRP'
Intent: 'PackageDistribution'
ContentType: 'Maven'
ContentSource: 'Folder'
FolderLocation: './local-build'
WaitForReleaseCompletion: true
Owners: 'yurys@microsoft.com'
Approvers: 'maxschmitt@microsoft.com'
ServiceEndpointUrl: 'https://api.esrp.microsoft.com'
MainPublisher: 'Playwright'
DomainTenantId: '72f988bf-86f1-41af-91ab-2d7cd011db47'
displayName: 'ESRP Release to Maven'
extends:
template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines
parameters:
pool:
name: DevDivPlaywrightAzurePipelinesUbuntu2204
os: linux
sdl:
sourceAnalysisPool:
name: DevDivPlaywrightAzurePipelinesWindows2022
# The image must be windows-based due to restrictions of the SDL tools. See: https://aka.ms/AAo6v8e
# In the case of a windows build, this can be the same as the above pool image.
os: windows
suppression:
suppressionFile: $(Build.SourcesDirectory)\.azure-pipelines\guardian\SDL\.gdnsuppress
stages:
- stage: Stage
jobs:
- job: HostJob
steps:
- bash: |
if [[ ! "$CURRENT_BRANCH" =~ ^release-.* ]]; then
echo "Can only publish from a release branch."
echo "Unexpected branch name: $CURRENT_BRANCH"
exit 1
fi
env:
CURRENT_BRANCH: ${{ variables['Build.SourceBranchName'] }}
displayName: "Check the branch is a release branch"
- bash: |
echo "importing GPG key:"
# Pipeline variables do not preserve line ends so we use base64 instead of --armored as a workaround.
echo $GPG_PRIVATE_KEY_BASE64 | base64 -d | gpg --batch --import
echo "list keys after import:"
gpg --list-keys
env:
GPG_PRIVATE_KEY_BASE64: $(GPG_PRIVATE_KEY_BASE64) # secret variable has to be mapped to an env variable
displayName: "Import gpg key"
- bash: ./scripts/download_driver_for_all_platforms.sh
displayName: 'Download driver'
- bash: mvn -B deploy -D skipTests --no-transfer-progress --activate-profiles release -D gpg.passphrase=$GPG_PASSPHRASE -DaltDeploymentRepository=snapshot-repo::default::file:$(pwd)/local-build
displayName: 'Build and deploy to a local directory'
env:
GPG_PASSPHRASE: $(GPG_PASSPHRASE) # secret variable has to be mapped to an env variable
- bash: |
for file in $(find snapshots -type f); do
echo "processing: $file"
if [[ $file =~ \.(md5|sha1|sha256)$ ]]; then
continue
fi
sha256sum "$file" | cut -f1 -d \ > "$file.sha256"
done
displayName: 'Create .sha256 files'
- task: EsrpRelease@4
inputs:
ConnectedServiceName: 'Playwright-ESRP'
Intent: 'PackageDistribution'
ContentType: 'Maven'
ContentSource: 'Folder'
FolderLocation: './local-build'
WaitForReleaseCompletion: true
Owners: 'yurys@microsoft.com'
Approvers: 'maxschmitt@microsoft.com'
ServiceEndpointUrl: 'https://api.esrp.microsoft.com'
MainPublisher: 'Playwright'
DomainTenantId: '72f988bf-86f1-41af-91ab-2d7cd011db47'
displayName: 'ESRP Release to Maven'
-64
View File
@@ -1,64 +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
**Test file (self-contained)**
```java
import com.microsoft.playwright.*;
public class ExampleReproducible {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch();
BrowserContext context = browser.newContext();
Page page = context.newPage();
// ...
}
}
}
```
**Steps**
- [Run the test]
- [...]
**Expected**
[Describe expected behavior]
**Actual**
[Describe actual behavior]
+95
View File
@@ -0,0 +1,95 @@
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/java/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/java/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-java/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. mvn test
3. 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 provide information about the environment you are running in.
placeholder: |
- Operating System: [Ubuntu 22.04]
- CPU: [arm64]
- Browser: [All, Chromium, Firefox, WebKit]
- Java Version: [20]
- Maven Version: [3.8.6]
- Other info:
validations:
required: true
+1
View File
@@ -1,3 +1,4 @@
blank_issues_enabled: false
contact_links:
- name: Join our Discord Server
url: https://aka.ms/playwright/discord
+29
View 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/java/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
View 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
View File
@@ -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
View 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/java)
- [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
-38
View File
@@ -1,38 +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:
```java
import com.microsoft.playwright.*;
public class ExampleReproducible {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch();
BrowserContext context = browser.newContext();
Page page = context.newPage();
// ...
}
}
}
```
**Describe the bug**
Add any other details about the problem here.
+90
View File
@@ -0,0 +1,90 @@
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-java/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. mvn test
3. 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 provide information about the environment you are running in.
placeholder: |
- Operating System: [Ubuntu 22.04]
- CPU: [arm64]
- Browser: [All, Chromium, Firefox, WebKit]
- Java Version: [20]
- Maven Version: [3.8.6]
- Other info:
validations:
required: true
+10
View File
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/" # Location of the pom.xml file
schedule:
interval: "weekly"
open-pull-requests-limit: 10
allow:
- dependency-type: "direct" # Optional: Only update direct dependencies
- dependency-type: "indirect" # Optional: Only update indirect (transitive) dependencies
@@ -1,25 +0,0 @@
name: "devrelease:docker"
on:
push:
branches:
- main
jobs:
publish-canary-docker:
name: "publish to DockerHub"
runs-on: ubuntu-20.04
if: github.repository == 'microsoft/playwright-java'
steps:
- uses: actions/checkout@v2
- uses: azure/docker-login@v1
with:
login-server: playwright.azurecr.io
username: playwright
password: ${{ secrets.DOCKER_PASSWORD }}
- uses: actions/checkout@v2
- name: Set up Docker QEMU for arm64 docker builds
uses: docker/setup-qemu-action@v1
with:
platforms: arm64
- name: publish docker canary
run: ./utils/docker/publish_docker.sh canary
@@ -8,25 +8,23 @@ on:
required: true
type: boolean
description: "Is this a release image?"
branches:
- release-*
jobs:
publish-canary-docker:
name: publish to DockerHub
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
if: github.repository == 'microsoft/playwright-java'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- 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@v1
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: ./utils/docker/publish_docker.sh stable
if: (github.event_name != 'workflow_dispatch' && !github.event.release.prerelease) || (github.event_name == 'workflow_dispatch' && github.event.inputs.is_release == 'true')
- run: ./utils/docker/publish_docker.sh canary
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
env:
BROWSER: ${{ matrix.browser }}
- name: Run tracing tests w/ sources
run: mvn test --no-transfer-progress --fail-at-end -D test=*TestTracing* -D org.slf4j.simpleLogger.showDateTime=true -D org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
run: mvn test --no-transfer-progress --fail-at-end --projects=playwright -D test=*TestTracing* -D org.slf4j.simpleLogger.showDateTime=true -D org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
env:
BROWSER: ${{ matrix.browser }}
PLAYWRIGHT_JAVA_SRC: src/test/java
+2 -2
View File
@@ -34,9 +34,9 @@ Names of published driver archives can be found at https://github.com/microsoft/
mvn compile
mvn test
# Executing a single test
BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes#shouldHaveTheCorrectResponseBodySize
BROWSER=chromium mvn test --projects=playwright -Dtest=TestPageNetworkSizes#shouldHaveTheCorrectResponseBodySize
# Executing a single test class
BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes
BROWSER=chromium mvn test --projects=playwright -Dtest=TestPageNetworkSizes
```
### Generating API
+4 -4
View File
@@ -11,9 +11,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->121.0.6167.57<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->123.0.6312.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->121.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->123.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Headless execution is supported for all the browsers on all platforms. Check out [system requirements](https://playwright.dev/java/docs/intro#system-requirements) for details.
@@ -43,7 +43,7 @@ To run Playwright simply add following dependency to your Maven project:
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.28.1</version>
<version>1.41.0</version>
</dependency>
```
@@ -51,7 +51,7 @@ To run Playwright using Gradle add following dependency to your build.gradle fil
```gradle
dependencies {
implementation group: 'com.microsoft.playwright', name: 'playwright', version: '1.28.1'
implementation group: 'com.microsoft.playwright', name: 'playwright', version: '1.41.0'
}
```
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.41.0-SNAPSHOT</version>
<version>1.42.0</version>
</parent>
<artifactId>driver-bundle</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.41.0-SNAPSHOT</version>
<version>1.42.0</version>
</parent>
<artifactId>driver</artifactId>
+3 -3
View File
@@ -6,7 +6,7 @@
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>1.41.0-SNAPSHOT</version>
<version>1.42.0</version>
<name>Playwright Client Examples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -15,7 +15,7 @@
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.30.0</version>
<version>1.41.0</version>
</dependency>
</dependencies>
<build>
@@ -23,7 +23,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<version>3.12.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
+2 -2
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.41.0-SNAPSHOT</version>
<version>1.42.0</version>
</parent>
<artifactId>playwright</artifactId>
@@ -26,7 +26,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<configuration combine.self="append">
<subpackages>com.microsoft.playwright</subpackages>
<excludePackageNames>com.microsoft.playwright.impl</excludePackageNames>
<excludePackageNames>com.microsoft.playwright.impl,com.microsoft.playwright.junit.impl</excludePackageNames>
</configuration>
</plugin>
<plugin>
@@ -60,7 +60,7 @@ public interface BrowserContext extends AutoCloseable {
/**
* Emitted when JavaScript within the page calls one of console API methods, e.g. {@code console.log} or {@code
* console.dir}. Also emitted if the page throws an error or a warning.
* console.dir}.
*
* <p> The arguments passed into {@code console.log} and the page are available on the {@code ConsoleMessage} event handler
* argument.
@@ -1779,8 +1779,8 @@ public interface ElementHandle extends JSHandle {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code key: "Control+Shift+T"} are supported as
* well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
*
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
* @since v1.8
@@ -1809,8 +1809,8 @@ public interface ElementHandle extends JSHandle {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code key: "Control+Shift+T"} are supported as
* well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
*
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
* @since v1.8
@@ -3991,8 +3991,8 @@ public interface Frame {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code key: "Control+Shift+T"} are supported as
* well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
*
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used.
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
@@ -4020,8 +4020,8 @@ public interface Frame {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code key: "Control+Shift+T"} are supported as
* well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
*
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used.
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
@@ -152,8 +152,8 @@ public interface Keyboard {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code 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.
*
* <p> **Usage**
* <pre>{@code
@@ -197,8 +197,8 @@ public interface Keyboard {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code 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.
*
* <p> **Usage**
* <pre>{@code
@@ -4084,8 +4084,8 @@ public interface Locator {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code key: "Control+Shift+T"} are supported as
* well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
*
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
* @since v1.14
@@ -4123,8 +4123,8 @@ public interface Locator {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code key: "Control+Shift+T"} are supported as
* well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
*
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
* @since v1.14
@@ -80,7 +80,7 @@ public interface Page extends AutoCloseable {
/**
* Emitted when JavaScript within the page calls one of console API methods, e.g. {@code console.log} or {@code
* console.dir}. Also emitted if the page throws an error or a warning.
* console.dir}.
*
* <p> The arguments passed into {@code console.log} are available on the {@code ConsoleMessage} event handler argument.
*
@@ -2055,6 +2055,10 @@ public interface Page extends AutoCloseable {
* Paper margins, defaults to none.
*/
public Margin margin;
/**
* Whether or not to embed the document outline into the PDF. Defaults to {@code false}.
*/
public Boolean outline;
/**
* Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages.
*/
@@ -2077,6 +2081,10 @@ public interface Page extends AutoCloseable {
* Scale of the webpage rendering. Defaults to {@code 1}. Scale amount must be between 0.1 and 2.
*/
public Double scale;
/**
* Whether or not to generate tagged (accessible) PDF. Defaults to {@code false}.
*/
public Boolean tagged;
/**
* Paper width, accepts values labeled with units.
*/
@@ -2139,6 +2147,13 @@ public interface Page extends AutoCloseable {
this.margin = margin;
return this;
}
/**
* Whether or not to embed the document outline into the PDF. Defaults to {@code false}.
*/
public PdfOptions setOutline(boolean outline) {
this.outline = outline;
return this;
}
/**
* Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages.
*/
@@ -2176,6 +2191,13 @@ public interface Page extends AutoCloseable {
this.scale = scale;
return this;
}
/**
* Whether or not to generate tagged (accessible) PDF. Defaults to {@code false}.
*/
public PdfOptions setTagged(boolean tagged) {
this.tagged = tagged;
return this;
}
/**
* Paper width, accepts values labeled with units.
*/
@@ -5804,8 +5826,8 @@ public interface Page extends AutoCloseable {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code 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.
*
* <p> **Usage**
* <pre>{@code
@@ -5847,8 +5869,8 @@ public interface Page extends AutoCloseable {
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate
* different respective texts.
*
* <p> Shortcuts such as {@code key: "Control+o"} or {@code 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.
* <p> Shortcuts such as {@code key: "Control+o"}, {@code key: "Control++} or {@code 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.
*
* <p> **Usage**
* <pre>{@code
@@ -5895,6 +5917,83 @@ public interface Page extends AutoCloseable {
* @since v1.9
*/
List<ElementHandle> querySelectorAll(String selector);
/**
* When testing a web page, sometimes unexpected overlays like a coookie consent dialog appear and block actions you want
* to automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making
* them tricky to handle in automated tests.
*
* <p> This method lets you set up a special function, called a handler, that activates when it detects that overlay is
* visible. The handler's job is to remove the overlay, allowing your test to continue as if the overlay wasn't there.
*
* <p> Things to keep in mind:
* <ul>
* <li> When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as a part of
* your normal test flow, instead of using {@link Page#addLocatorHandler Page.addLocatorHandler()}.</li>
* <li> Playwright checks for the overlay every time before executing or retrying an action that requires an <a
* href="https://playwright.dev/java/docs/actionability">actionability check</a>, or before performing an auto-waiting
* assertion check. When overlay is visible, Playwright calls the handler first, and then proceeds with the
* action/assertion.</li>
* <li> The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. If your
* handler takes too long, it might cause timeouts.</li>
* <li> You can register multiple handlers. However, only a single handler will be running at a time. Make sure the actions
* within a handler don't depend on another handler.</li>
* </ul>
*
* <p> <strong>NOTE:</strong> Running the handler will alter your page state mid-test. For example it will change the currently focused element and
* move the mouse. Make sure that actions that run after the handler are self-contained and do not rely on the focus and
* mouse state being unchanged. <br /> <br /> For example, consider a test that calls {@link Locator#focus Locator.focus()}
* followed by {@link Keyboard#press 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 {@link Locator#press
* Locator.press()} instead to avoid this problem. <br /> <br /> Another example is a series of mouse actions, where {@link
* Mouse#move Mouse.move()} is followed by {@link Mouse#down Mouse.down()}. Again, when the handler runs between these two
* actions, the mouse position will be wrong during the mouse down. Prefer self-contained actions like {@link Locator#click
* Locator.click()} that do not rely on the state being unchanged by a handler.
*
* <p> **Usage**
*
* <p> An example that closes a cookie consent dialog when it appears:
* <pre>{@code
* // 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();
* }</pre>
*
* <p> An example that skips the "Confirm your security details" page when it is shown:
* <pre>{@code
* // 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();
* }</pre>
*
* <p> An example with a custom callback on every actionability check. It uses a {@code <body>} locator that is always visible,
* so the handler is called before every actionability check:
* <pre>{@code
* // 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();
* }</pre>
*
* @param locator Locator that triggers the handler.
* @param handler Function that should be run once {@code locator} appears. This function should get rid of the element that blocks
* actions like click.
* @since v1.42
*/
void addLocatorHandler(Locator locator, Runnable handler);
/**
* This method reloads the current page, in the same way as if the user had triggered a browser refresh. Returns the main
* resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect.
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.google.gson.*;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.google.gson.Gson;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.google.gson.JsonElement;
@@ -71,6 +71,7 @@ public class Connection {
isLogging = (debug != null) && debug.contains("pw:channel");
}
LocalUtils localUtils;
PlaywrightImpl playwright;
final Map<String, String> env;
private int tracingCount;
@@ -79,7 +80,7 @@ public class Connection {
super(connection, "Root", "");
}
Playwright initialize() {
PlaywrightImpl initialize() {
JsonObject params = new JsonObject();
params.addProperty("sdkLanguage", "java");
JsonElement result = sendMessage("initialize", params.getAsJsonObject());
@@ -177,7 +178,8 @@ public class Connection {
}
public PlaywrightImpl initializePlaywright() {
return (PlaywrightImpl) this.root.initialize();
playwright = root.initialize();
return playwright;
}
LocalUtils localUtils() {
@@ -490,7 +490,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
options = new SetInputFilesOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.add("files", Serialization.toJsonArray(files));
params.add("payloads", Serialization.toJsonArray(files));
sendMessage("setInputFiles", params);
}
@@ -186,7 +186,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
throw new PlaywrightException("Failed to read from file", e);
}
String content = new String(encoded, StandardCharsets.UTF_8);
content += "//# sourceURL=" + options.path.toString().replace("\n", "");
content = addSourceUrlToScript(content, options.path);
jsonOptions.addProperty("content", content);
}
JsonElement json = sendMessage("addScriptTag", jsonOptions);
@@ -407,12 +407,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
@Override
public Locator getByTestId(String testId) {
return locator(getByTestIdSelector(testId));
return locator(getByTestIdSelector(testId, connection.playwright));
}
@Override
public Locator getByTestId(Pattern testId) {
return locator(getByTestIdSelector(testId));
return locator(getByTestIdSelector(testId, connection.playwright));
}
@Override
@@ -82,12 +82,12 @@ class FrameLocatorImpl implements FrameLocator {
@Override
public Locator getByTestId(String testId) {
return locator(getByTestIdSelector(testId));
return locator(getByTestIdSelector(testId, frame.connection.playwright));
}
@Override
public Locator getByTestId(Pattern testId) {
return locator(getByTestIdSelector(testId));
return locator(getByTestIdSelector(testId, frame.connection.playwright));
}
@Override
@@ -73,6 +73,14 @@ public class HARRouter {
if ("fulfill".equals(action)) {
int status = response.get("status").getAsInt();
// If the response status is -1, the request was canceled or stalled, so we just stall it here.
// See https://github.com/microsoft/playwright/issues/29311.
// TODO: it'd be better to abort such requests, but then we likely need to respect the timing,
// because the request might have been stalled for a long time until the very end of the
// test when HAR was recorded but we'd abort it immediately.
if (status == -1) {
return;
}
Map<String, String> headers = fromNameValues(response.getAsJsonArray("headers"));
byte[] buffer = Base64.getDecoder().decode(response.get("body").getAsString());
route.fulfill(new Route.FulfillOptions()
@@ -29,6 +29,10 @@ class LocalUtils extends ChannelOwner {
super(parent, type, guid, initializer);
}
JsonArray deviceDescriptors() {
return initializer.getAsJsonArray("deviceDescriptors");
}
void zip(Path zipFile, JsonArray entries, String stacksId, boolean appendMode, boolean includeSources) {
JsonObject params = new JsonObject();
params.addProperty("zipFile", zipFile.toString());
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.google.gson.JsonElement;
@@ -278,12 +294,12 @@ class LocatorImpl implements Locator {
@Override
public Locator getByTestId(String testId) {
return locator(getByTestIdSelector(testId));
return locator(getByTestIdSelector(testId, frame.connection.playwright));
}
@Override
public Locator getByTestId(Pattern testId) {
return locator(getByTestIdSelector(testId));
return locator(getByTestIdSelector(testId, frame.connection.playwright));
}
@Override
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.microsoft.playwright.Locator;
@@ -9,12 +25,6 @@ import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
public class LocatorUtils {
private static volatile String testIdAttributeName = "data-testid";;
static void setTestIdAttributeName(String name) {
testIdAttributeName = name;
}
static String getByTextSelector(Object text, Locator.GetByTextOptions options) {
boolean exact = options != null && options.exact != null && options.exact;
return "internal:text=" + escapeForTextSelector(text, exact);
@@ -29,7 +39,8 @@ public class LocatorUtils {
return "internal:attr=[" + attrName + "=" + escapeForAttributeSelector(value, exact) + "]";
}
static String getByTestIdSelector(Object testId) {
static String getByTestIdSelector(Object testId, PlaywrightImpl playwright) {
String testIdAttributeName = ((SharedSelectors) playwright.selectors()).testIdAttributeName;
return getByAttributeTextSelector(testIdAttributeName, testId, true);
}
@@ -49,6 +49,8 @@ public class PageImpl extends ChannelOwner implements Page {
private ViewportSize viewport;
private final Router routes = new Router();
private final Set<FrameImpl> frames = new LinkedHashSet<>();
private final Map<Integer, Runnable> locatorHandlers = new HashMap<>();
private static final Map<EventType, String> eventSubscriptions() {
Map<EventType, String> result = new HashMap<>();
result.put(EventType.CONSOLE, "console");
@@ -170,6 +172,9 @@ public class PageImpl extends ChannelOwner implements Page {
frame.parentFrame.childFrames.remove(frame);
}
listeners.notify(EventType.FRAMEDETACHED, frame);
} else if ("locatorHandlerTriggered".equals(event)) {
int uid = params.get("uid").getAsInt();
onLocatorHandlerTriggered(uid);
} else if ("route".equals(event)) {
RouteImpl route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
route.browserContext = browserContext;
@@ -523,6 +528,34 @@ public class PageImpl extends ChannelOwner implements Page {
return withLogging("Page.querySelectorAll", () -> mainFrame.querySelectorAllImpl(selector));
}
@Override
public void addLocatorHandler(Locator locator, Runnable handler) {
LocatorImpl locatorImpl = (LocatorImpl) locator;
if (locatorImpl.frame != mainFrame) {
throw new PlaywrightException("Locator must belong to the main frame of this page");
}
withLogging("Page.addLocatorHandler", () -> {
JsonObject params = new JsonObject();
params.addProperty("selector", locatorImpl.selector);
JsonObject json = (JsonObject) sendMessage("registerLocatorHandler", params);
int uid = json.get("uid").getAsInt();
locatorHandlers.put(uid, handler);
});
}
private void onLocatorHandlerTriggered(int uid) {
try {
Runnable handler = locatorHandlers.get(uid);
if (handler != null) {
handler.run();
}
} finally {
JsonObject params = new JsonObject();
params.addProperty("uid", uid);
sendMessageAsync("resolveLocatorHandlerNoReply", params);
}
}
@Override
public Object evalOnSelector(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
return withLogging("Page.evalOnSelector", () -> mainFrame.evalOnSelectorImpl(
@@ -544,7 +577,8 @@ public class PageImpl extends ChannelOwner implements Page {
withLogging("Page.addInitScript", () -> {
try {
byte[] bytes = readAllBytes(path);
addInitScriptImpl(new String(bytes, UTF_8));
String script = addSourceUrlToScript(new String(bytes, UTF_8), path);
addInitScriptImpl(script);
} catch (IOException e) {
throw new PlaywrightException("Failed to read script from file", e);
}
@@ -16,6 +16,7 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.microsoft.playwright.APIRequest;
import com.microsoft.playwright.Playwright;
@@ -89,6 +90,10 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
sharedSelectors.removeChannel(selectors);
}
public JsonArray deviceDescriptors() {
return connection.localUtils.deviceDescriptors();
}
@Override
public BrowserTypeImpl chromium() {
return chromium;
@@ -17,22 +17,16 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.Selectors;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static com.microsoft.playwright.impl.Serialization.gson;
import static java.nio.charset.StandardCharsets.UTF_8;
class SelectorsImpl extends ChannelOwner {
SelectorsImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
}
void registerImpl(String name, String script, Selectors.RegisterOptions options) {
void register(String name, String script, Selectors.RegisterOptions options) {
if (options == null) {
options = new Selectors.RegisterOptions();
}
@@ -41,4 +35,10 @@ class SelectorsImpl extends ChannelOwner {
params.addProperty("source", script);
sendMessage("register", params);
}
void setTestIdAttributeName(String name) {
JsonObject params = new JsonObject();
params.addProperty("testIdAttributeName", name);
sendMessageAsync("setTestIdAttributeName", params);
}
}
@@ -25,13 +25,14 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import static com.microsoft.playwright.impl.LocatorUtils.setTestIdAttributeName;
import static java.nio.charset.StandardCharsets.UTF_8;
public class SharedSelectors extends LoggingSupport implements Selectors {
private final List<SelectorsImpl> channels = new ArrayList<>();
private final List<Registration> registrations = new ArrayList<>();
String testIdAttributeName = "data-testid";
private static class Registration {
final String name;
final String script;
@@ -64,12 +65,22 @@ public class SharedSelectors extends LoggingSupport implements Selectors {
@Override
public void setTestIdAttribute(String attributeName) {
// TODO: set it per playwright insttance
setTestIdAttributeName(attributeName);
if (attributeName == null) {
throw new PlaywrightException("Test id attribute cannot be null");
}
testIdAttributeName = attributeName;
channels.forEach(channel -> channel.setTestIdAttributeName(testIdAttributeName));
}
void addChannel(SelectorsImpl channel) {
registrations.forEach(r -> channel.registerImpl(r.name, r.script, r.options));
registrations.forEach(r -> {
try {
channel.register(r.name, r.script, r.options);
} catch (PlaywrightException e) {
// This should not fail except for connection closure, but just in case we catch.
}
channel.setTestIdAttributeName(testIdAttributeName);
});
channels.add(channel);
}
@@ -78,7 +89,7 @@ public class SharedSelectors extends LoggingSupport implements Selectors {
}
private void registerImpl(String name, String script, RegisterOptions options) {
channels.forEach(impl -> impl.registerImpl(name, script, options));
channels.forEach(impl -> impl.register(name, script, options));
registrations.add(new Registration(name, script, options));
}
}
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
@@ -341,4 +341,8 @@ public class Utils {
}
return flags;
}
static String addSourceUrlToScript(String source, Path path) {
return source + "\n//# sourceURL=" + path.toString().replace("\n", "");
}
}
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import java.util.function.BooleanSupplier;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.microsoft.playwright.WebError;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
@@ -1,90 +1,87 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit;
import com.microsoft.playwright.APIRequest;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.options.ViewportSize;
import java.nio.file.Path;
/**
* <strong>NOTE:</strong> this API is experimental and is subject to changes.
*
* <p> Instances of this class are expected to be created by custom {@link OptionsFactory}
* implementations. Implement custom factories to provide custom Playwright configurations.
*
* <p> For more details and usage examples see our
* <a href="https://playwright.dev/java/docs/junit">JUnit guide</a>.
*/
public class Options {
public String baseUrl;
public Path storageStatePath;
public ViewportSize viewportSize;
public String channel;
public Boolean headless;
public String browserName = "chromium";
public String browserName;
public String deviceName;
// Custom attribute to be used in page.getByTestId(). data-testid is used by default.
public String testIdAttribute;
public Boolean ignoreHTTPSErrors;
public BrowserType.LaunchOptions launchOptions;
public Browser.NewContextOptions contextOption;
public Browser.NewContextOptions contextOptions;
public APIRequest.NewContextOptions apiRequestOptions;
public Playwright.CreateOptions playwrightCreateOptions;
public Playwright.CreateOptions getPlaywrightCreateOptions() {
return playwrightCreateOptions;
}
public Options setPlaywrightCreateOptions(Playwright.CreateOptions playwrightCreateOptions) {
this.playwrightCreateOptions = playwrightCreateOptions;
return this;
}
public BrowserType.LaunchOptions getLaunchOptions() {
return launchOptions;
}
public Options setLaunchOptions(BrowserType.LaunchOptions launchOptions) {
this.launchOptions = launchOptions;
return this;
}
public Browser.NewContextOptions getContextOption() {
return contextOption;
}
public Options setContextOption(Browser.NewContextOptions contextOption) {
this.contextOption = contextOption;
public Options setContextOptions(Browser.NewContextOptions contextOptions) {
this.contextOptions = contextOptions;
return this;
}
public APIRequest.NewContextOptions getApiRequestOptions() {
return apiRequestOptions;
}
public Options setApiRequestOptions(APIRequest.NewContextOptions apiRequestOptions) {
this.apiRequestOptions = apiRequestOptions;
return this;
}
public String getBaseUrl() {
return baseUrl;
}
public Options setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
return this;
}
public Path getStorageStatePath() {
return storageStatePath;
}
public Options setStorageStatePath(Path storageStatePath) {
this.storageStatePath = storageStatePath;
public Options setTestIdAttribute(String name) {
this.testIdAttribute = name;
return this;
}
public String getBrowserName() {
return browserName;
}
public Options setBrowserName(String browserName) {
this.browserName = browserName;
return this;
}
public String getChannel() {
return channel;
public Options setDeviceName(String deviceName) {
this.deviceName = deviceName;
return this;
}
public Options setChannel(String channel) {
@@ -92,21 +89,13 @@ public class Options {
return this;
}
public Boolean isHeadless() {
return headless;
}
public Options setHeadless(Boolean headless) {
this.headless = headless;
return this;
}
public ViewportSize getViewportSize() {
return viewportSize;
}
public Options setViewportSize(ViewportSize viewportSize) {
this.viewportSize = viewportSize;
public Options setIgnoreHTTPSErrors(Boolean ignoreHTTPSErrors) {
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
return this;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit;
/**
* <strong>NOTE:</strong> this API is experimental and is subject to changes.
*
* <p> Implement this interface to pass custom options to {@link UsePlaywright}
* annotation.
*
* <p> An example of implementing {@code @OptionsFactory}:
* <pre>{@code
* import com.microsoft.playwright.junit.Options;
* import com.microsoft.playwright.junit.OptionsFactory;
* import com.microsoft.playwright.junit.UsePlaywright;
*
* @UsePlaywright(MyTest.CustomOptions.class)
* public class MyTest {
*
* public static class CustomOptions implements OptionsFactory {
* @Override
* public Options getOptions() {
* return new Options()
* .setHeadless(false)
* .setContextOption(new Browser.NewContextOptions()
* .setBaseURL("https://github.com"))
* .setApiRequestOptions(new APIRequest.NewContextOptions()
* .setBaseURL("https://playwright.dev"));
* }
* }
*
* @Test
* public void testWithCustomOptions(Page page, APIRequestContext request) {
* page.navigate("/");
* assertThat(page).hasURL(Pattern.compile("github"));
*
* APIResponse response = request.get("/");
* assertTrue(response.text().contains("Playwright"));
* }
* }
* }</pre>
*
* <p>For more details and usage examples see our
* <a href="https://playwright.dev/java/docs/junit">JUnit guide</a>.
*/
public interface OptionsFactory {
Options getOptions();
}
@@ -1,5 +1,22 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.junit.impl.*;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -8,10 +25,61 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <strong>NOTE:</strong> this API is experimental and is subject to changes.
*
* Use {@code @UsePlaywright} annotation to automatically manage Playwright objects
* used in your test. Custom configuration can be provided by implementing
* {@link OptionsFactory} and passing the class as a parameter.
*
* <p> When a test class is annotated with {@code @UsePlaywright} each test method can
* use any of the following arguments that will be automatically created at run time:
* <ul>
* <li> {@link com.microsoft.playwright.Page Page page}</li>
* <li> {@link com.microsoft.playwright.BrowserContext BrowserContext context}</li>
* <li> {@link com.microsoft.playwright.Browser Browser browser}</li>
* <li> {@link com.microsoft.playwright.APIRequestContext APIRequestContext request}</li>
* <li> {@link com.microsoft.playwright.Playwright Playwright playwright}</li>
* </ul>
* {@code Page} and {@code BrowserContext} are created before each test and closed
* after the test has finished. {@code Browser} and {@code Playwright} are reused
* between tests for better efficiency.
*
* <p> An example of using {@code @UsePlaywright} annotation:
* <pre>{@code
* import com.microsoft.playwright.Browser;
* import com.microsoft.playwright.BrowserContext;
* import com.microsoft.playwright.Page;
* import org.junit.jupiter.api.Test;
*
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
* import static org.junit.jupiter.api.Assertions.assertEquals;
* import static org.junit.jupiter.api.Assertions.assertNotNull;
*
* @UsePlaywright
* public class TestExample {
* @Test
* void shouldProvidePage(Page page) {
* page.navigate("https://playwright.dev");
* assertThat(page).hasURL("https://playwright.dev/");
* }
*
* @Test
* void shouldResolvePlaywrightObjects(Page page, BrowserContext context, Browser browser) {
* assertEquals(context, page.context());
* assertEquals(browser, context.browser());
* assertNotNull(browser.version());
* }
* }
* }</pre>
*
* <p> For more details and usage examples see our
* <a href="https://playwright.dev/java/docs/junit">JUnit guide</a>.
*/
@ExtendWith({OptionsExtension.class, PlaywrightExtension.class, BrowserExtension.class, BrowserContextExtension.class,
PageExtension.class, APIRequestContextExtension.class})
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UsePlaywright {
Class<? extends Options> options() default Options.class;
Class<? extends OptionsFactory> value() default DefaultOptions.class;
}
@@ -1,7 +1,25 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.APIRequest;
import com.microsoft.playwright.APIRequestContext;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.impl.Utils;
import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;
@@ -38,8 +56,20 @@ public class APIRequestContextExtension implements ParameterResolver, BeforeEach
Options options = OptionsExtension.getOptions(extensionContext);
Playwright playwright = PlaywrightExtension.getOrCreatePlaywright(extensionContext);
apiRequestContext = playwright.request().newContext(options.getApiRequestOptions());
apiRequestContext = playwright.request().newContext(getContextOptions(options));
threadLocalAPIRequestContext.set(apiRequestContext);
return apiRequestContext;
}
private static APIRequest.NewContextOptions getContextOptions(Options options) {
APIRequest.NewContextOptions contextOptions = Utils.clone(options.apiRequestOptions);
if(contextOptions == null) {
contextOptions = new APIRequest.NewContextOptions();
}
if(options.ignoreHTTPSErrors != null) {
contextOptions.ignoreHTTPSErrors = options.ignoreHTTPSErrors;
}
return contextOptions;
}
}
@@ -1,13 +1,30 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.impl.Utils;
import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isClassHook;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.*;
public class BrowserContextExtension implements ParameterResolver, AfterEachCallback {
private static final ThreadLocal<BrowserContext> threadLocalBrowserContext = new ThreadLocal<>();
@@ -38,29 +55,41 @@ public class BrowserContextExtension implements ParameterResolver, AfterEachCall
}
Options options = OptionsExtension.getOptions(extensionContext);
Playwright playwright = PlaywrightExtension.getOrCreatePlaywright(extensionContext);
setTestIdAttribute(playwright, options);
Browser browser = BrowserExtension.getOrCreateBrowser(extensionContext);
Browser.NewContextOptions contextOptions = getContextOptions(options);
Browser.NewContextOptions contextOptions = getContextOptions(playwright, options);
browserContext = browser.newContext(contextOptions);
threadLocalBrowserContext.set(browserContext);
return browserContext;
}
private static Browser.NewContextOptions getContextOptions(Options options) {
Browser.NewContextOptions contextOptions = Utils.clone(options.getContextOption());
private static Browser.NewContextOptions getContextOptions(Playwright playwright, Options options) {
Browser.NewContextOptions contextOptions = Utils.clone(options.contextOptions);
if (contextOptions == null) {
contextOptions = new Browser.NewContextOptions();
}
if (options.getBaseUrl() != null) {
contextOptions.setBaseURL(options.getBaseUrl());
if (options.baseUrl != null) {
contextOptions.setBaseURL(options.baseUrl);
}
if (options.getStorageStatePath() != null) {
contextOptions.setStorageStatePath(options.getStorageStatePath());
if (options.deviceName != null) {
DeviceDescriptor deviceDescriptor = DeviceDescriptor.findByName(playwright, options.deviceName);
if (deviceDescriptor == null) {
throw new PlaywrightException("Unknown device name: " + options.deviceName);
}
contextOptions.userAgent = deviceDescriptor.userAgent;
if (deviceDescriptor.viewport != null) {
contextOptions.setViewportSize(deviceDescriptor.viewport.width, deviceDescriptor.viewport.height);
}
contextOptions.deviceScaleFactor = deviceDescriptor.deviceScaleFactor;
contextOptions.isMobile = deviceDescriptor.isMobile;
contextOptions.hasTouch = deviceDescriptor.hasTouch;
}
if (options.getViewportSize() != null) {
contextOptions.setViewportSize(options.getViewportSize());
if(options.ignoreHTTPSErrors != null) {
contextOptions.setIgnoreHTTPSErrors(options.ignoreHTTPSErrors);
}
return contextOptions;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.Browser;
@@ -9,7 +25,6 @@ import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
public class BrowserExtension implements ParameterResolver, AfterAllCallback {
private static final ThreadLocal<Browser> threadLocalBrowser = new ThreadLocal<>();
@@ -42,36 +57,46 @@ public class BrowserExtension implements ParameterResolver, AfterAllCallback {
Playwright playwright = PlaywrightExtension.getOrCreatePlaywright(extensionContext);
BrowserType.LaunchOptions launchOptions = getLaunchOptions(options);
switch (options.getBrowserName()) {
case "webkit":
browser = playwright.webkit().launch(launchOptions);
break;
case "firefox":
browser = playwright.firefox().launch(launchOptions);
break;
case "chromium":
browser = playwright.chromium().launch(launchOptions);
break;
default:
throw new PlaywrightException("Invalid browser name.");
BrowserType browserType = playwright.chromium();
if (options.browserName != null) {
browserType = getBrowserTypeForName(playwright, options.browserName);
} else if (options.deviceName != null) {
DeviceDescriptor deviceDescriptor = DeviceDescriptor.findByName(playwright, options.deviceName);
if (deviceDescriptor != null && deviceDescriptor.defaultBrowserType != null) {
browserType = getBrowserTypeForName(playwright, deviceDescriptor.defaultBrowserType);
}
}
browser = browserType.launch(launchOptions);
threadLocalBrowser.set(browser);
return browser;
}
private static BrowserType getBrowserTypeForName(Playwright playwright, String name) {
switch (name) {
case "webkit":
return playwright.webkit();
case "firefox":
return playwright.firefox();
case "chromium":
return playwright.chromium();
default:
throw new PlaywrightException("Invalid browser name.");
}
}
private static BrowserType.LaunchOptions getLaunchOptions(Options options) {
BrowserType.LaunchOptions launchOptions = Utils.clone(options.getLaunchOptions());
BrowserType.LaunchOptions launchOptions = Utils.clone(options.launchOptions);
if (launchOptions == null) {
launchOptions = new BrowserType.LaunchOptions();
}
if (options.isHeadless() != null) {
launchOptions.setHeadless(options.isHeadless());
if (options.headless != null) {
launchOptions.setHeadless(options.headless);
}
if (options.getChannel() != null) {
options.setChannel(options.getChannel());
if (options.channel != null) {
launchOptions.setChannel(options.channel);
}
return launchOptions;
@@ -0,0 +1,27 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.junit.Options;
import com.microsoft.playwright.junit.OptionsFactory;
public class DefaultOptions implements OptionsFactory {
@Override
public Options getOptions() {
return new Options();
}
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.impl.PlaywrightImpl;
import com.microsoft.playwright.options.ViewportSize;
class DeviceDescriptor {
public String userAgent;
public ViewportSize viewport;
public Double deviceScaleFactor;
public Boolean isMobile;
public Boolean hasTouch;
public String defaultBrowserType;
static DeviceDescriptor findByName(Playwright playwright, String name) {
JsonArray devices = ((PlaywrightImpl) playwright).deviceDescriptors();
JsonObject descriptor = null;
for (JsonElement item : devices) {
if (name.equals(item.getAsJsonObject().get("name").getAsString())) {
descriptor = item.getAsJsonObject().getAsJsonObject("descriptor");
break;
}
}
if (descriptor == null) {
return null;
}
return new Gson().fromJson(descriptor, DeviceDescriptor.class);
}
}
@@ -1,5 +1,23 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.junit.Options;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
@@ -27,4 +45,10 @@ class ExtensionUtils {
Class<?> clazz = parameterContext.getParameter().getType();
return subject.equals(clazz);
}
static void setTestIdAttribute(Playwright playwright, Options options) {
String testIdAttribute = options.testIdAttribute == null ? "data-testid" : options.testIdAttribute;
playwright.selectors().setTestIdAttribute(testIdAttribute);
}
}
@@ -1,7 +1,24 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.junit.Options;
import com.microsoft.playwright.junit.OptionsFactory;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
@@ -24,7 +41,7 @@ public class OptionsExtension implements AfterAllCallback {
UsePlaywright usePlaywrightAnnotation = getUsePlaywrightAnnotation(extensionContext);
try {
options = usePlaywrightAnnotation.options().newInstance();
options = usePlaywrightAnnotation.value().newInstance().getOptions();
threadLocalOptions.set(options);
} catch (InstantiationException | IllegalAccessException e) {
throw new PlaywrightException("Failed to create options", e);
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.BrowserContext;
@@ -1,20 +1,70 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.junit.impl;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.junit.Options;
import org.junit.jupiter.api.extension.*;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.isParameterSupported;
import static com.microsoft.playwright.junit.impl.ExtensionUtils.setTestIdAttribute;
public class PlaywrightExtension implements ParameterResolver, AfterAllCallback {
public class PlaywrightExtension implements ParameterResolver {
private static final ThreadLocal<Playwright> threadLocalPlaywright = new ThreadLocal<>();
private static final ExtensionContext.Namespace namespace = ExtensionContext.Namespace.create(PlaywrightExtension.class);
@Override
public void afterAll(ExtensionContext extensionContext) {
Playwright playwright = threadLocalPlaywright.get();
threadLocalPlaywright.remove();
if (playwright != null) {
playwright.close();
// There should be at most one instance of PlaywrightRegistry per test run, it keeps
// track of all created Playwright instances and calls `close()` on each of them after
// the tests finished.
static class PlaywrightRegistry implements ExtensionContext.Store.CloseableResource {
private final List<Playwright> playwrightList = Collections.synchronizedList(new ArrayList<>());
static synchronized PlaywrightRegistry getOrCreateFor(ExtensionContext extensionContext) {
ExtensionContext.Store rootStore = extensionContext.getRoot().getStore(namespace);
PlaywrightRegistry instance = (PlaywrightRegistry) rootStore.get(PlaywrightRegistry.class);
if (instance == null) {
instance = new PlaywrightRegistry();
rootStore.put(PlaywrightRegistry.class, instance);
}
return instance;
}
Playwright createPlaywright(Playwright.CreateOptions options) {
Playwright playwright = Playwright.create(options);
playwrightList.add(playwright);
return playwright;
}
// This is a workaround for JUnit's lack of an "AfterTestRun" hook
// This will be called once after all tests have completed.
@Override
public void close() throws Throwable {
for (Playwright playwright : playwrightList) {
playwright.close();
}
playwrightList.clear();
}
}
@@ -36,8 +86,11 @@ public class PlaywrightExtension implements ParameterResolver, AfterAllCallback
}
Options options = OptionsExtension.getOptions(extensionContext);
playwright = Playwright.create(options.getPlaywrightCreateOptions());
PlaywrightRegistry registry = PlaywrightRegistry.getOrCreateFor(extensionContext);
playwright = registry.createPlaywright(options.playwrightCreateOptions);
threadLocalPlaywright.set(playwright);
setTestIdAttribute(playwright, options);
return playwright;
}
}
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.sun.net.httpserver.HttpExchange;
@@ -52,7 +52,7 @@ public class Server implements HttpHandler {
}
}
static Server createHttp(int port) throws IOException {
public static Server createHttp(int port) throws IOException {
return new Server(port, false);
}
@@ -81,7 +81,7 @@ public class Server implements HttpHandler {
server.start();
}
void stop() {
public void stop() {
server.stop(0);
}
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.microsoft.playwright.assertions.LocatorAssertions;
@@ -1,12 +1,12 @@
/*
* Copyright (c) Microsoft Corporation.
* <p>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
*
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,24 +18,21 @@ package com.microsoft.playwright;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.options.BrowserChannel;
import org.junit.jupiter.api.Assumptions;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import static com.microsoft.playwright.Utils.getBrowserTypeFromEnv;
import static org.junit.jupiter.api.Assertions.*;
public class TestBrowser extends TestBase {
@Override
void createContextAndPage() {
// Do not create anything.
}
@UsePlaywright(TestOptionsFactories.BasicOptionsFactory.class)
public class TestBrowser1 {
@Test
void shouldCreateNewPage() {
void shouldCreateNewPage(Browser browser) {
Page page1 = browser.newPage();
assertEquals(1, browser.contexts().size());
@@ -50,7 +47,7 @@ public class TestBrowser extends TestBase {
}
@Test
void shouldThrowUponSecondCreateNewPage() {
void shouldThrowUponSecondCreateNewPage(Browser browser) {
Page page = browser.newPage();
PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.context().newPage());
assertTrue(e.getMessage().contains("Please use browser.newContext()"));
@@ -58,54 +55,32 @@ public class TestBrowser extends TestBase {
}
@Test
void versionShouldWork() {
if (isChromium()) {
assertTrue(Pattern.matches("^\\d+\\.\\d+\\.\\d+\\.\\d+$", browser.version()));
} else if (isWebKit()) {
assertTrue(Pattern.matches("^\\d+\\.\\d+", browser.version()));
} else if (isFirefox()) {
// It can be 85.0b1 in Firefox.
assertTrue(Pattern.matches("^\\d+\\.\\d+.*", browser.version()));
}
}
private static BrowserChannel getBrowserChannelEnumFromEnv() {
String channel = getBrowserChannelFromEnv();
if (channel == null) {
return null;
}
switch (channel) {
case "chrome": return BrowserChannel.CHROME;
case "chrome-beta": return BrowserChannel.CHROME_BETA;
case "chrome-dev": return BrowserChannel.CHROME_DEV;
case "chrome-canary": return BrowserChannel.CHROME_CANARY;
case "msedge": return BrowserChannel.MSEDGE;
case "msedge-beta": return BrowserChannel.MSEDGE_BETA;
case "msedge-dev": return BrowserChannel.MSEDGE_DEV;
case "msedge-canary": return BrowserChannel.MSEDGE_CANARY;
default: throw new IllegalArgumentException("Unknown BROWSER_CHANNEL " + channel);
void versionShouldWork(Browser browser) {
switch (browser.browserType().name()) {
case "chromium":
assertTrue(Pattern.matches("^\\d+\\.\\d+\\.\\d+\\.\\d+$", browser.version()));
break;
case "webkit":
assertTrue(Pattern.matches("^\\d+\\.\\d+", browser.version()));
break;
case "firefox":
// It can be 85.0b1 in Firefox.
assertTrue(Pattern.matches("^\\d+\\.\\d+.*", browser.version()));
break;
default:
fail("Unknown browser");
}
}
@Test
void shouldSupportDeprecatedChannelEnum() {
BrowserChannel channel = getBrowserChannelEnumFromEnv();
Assumptions.assumeTrue(channel != null);
BrowserType.LaunchOptions options = createLaunchOptions();
options.setChannel(channel);
Browser browser = browserType.launch(options);
assertNotNull(browser);
browser.close();
void shouldReturnBrowserType(Playwright playwright, Browser browser) {
assertEquals(getBrowserTypeFromEnv(playwright), browser.browserType());
}
@Test
void shouldReturnBrowserType() {
assertEquals(browserType, browser.browserType());
}
@Test
@EnabledIf(value = "com.microsoft.playwright.TestBase#isChromium", disabledReason = "Chrome Devtools Protocol supported by chromium only")
void shouldWorkWithNewBrowserCDPSession() {
@EnabledIf(value = "com.microsoft.playwright.TestOptionsFactories#isChromium",
disabledReason = "Chrome Devtools Protocol supported by chromium only")
void shouldWorkWithNewBrowserCDPSession(Browser browser) {
CDPSession session = browser.newBrowserCDPSession();
JsonElement response = session.send("Browser.getVersion");
@@ -129,8 +104,7 @@ public class TestBrowser extends TestBase {
}
@Test
void shouldPropagateCloseReasonToPendingActions() {
Browser browser = browserType.launch();
void shouldPropagateCloseReasonToPendingActions(Browser browser) {
BrowserContext context = browser.newContext();
PlaywrightException e = assertThrows(PlaywrightException.class, () -> context.waitForPage(() -> {
browser.close(new Browser.CloseOptions().setReason("The reason."));
@@ -0,0 +1,83 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.microsoft.playwright.junit.Options;
import com.microsoft.playwright.junit.OptionsFactory;
import com.microsoft.playwright.junit.UsePlaywright;
import com.microsoft.playwright.options.BrowserChannel;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import static com.microsoft.playwright.TestBrowser2.ChannelOptionsFactory.getBrowserChannelEnumFromEnv;
import static com.microsoft.playwright.TestOptionsFactories.createLaunchOptions;
import static com.microsoft.playwright.TestOptionsFactories.getBrowserChannelFromEnv;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@UsePlaywright(TestBrowser2.ChannelOptionsFactory.class)
public class TestBrowser2 {
public static class ChannelOptionsFactory implements OptionsFactory {
@Override
public Options getOptions() {
BrowserChannel channel = getBrowserChannelEnumFromEnv();
BrowserType.LaunchOptions launchOptions = createLaunchOptions();
launchOptions.channel = channel;
return new Options().setLaunchOptions(launchOptions);
}
public static BrowserChannel getBrowserChannelEnumFromEnv() {
String channel = getBrowserChannelFromEnv();
if (channel == null) {
return null;
}
switch (channel) {
case "chrome":
return BrowserChannel.CHROME;
case "chrome-beta":
return BrowserChannel.CHROME_BETA;
case "chrome-dev":
return BrowserChannel.CHROME_DEV;
case "chrome-canary":
return BrowserChannel.CHROME_CANARY;
case "msedge":
return BrowserChannel.MSEDGE;
case "msedge-beta":
return BrowserChannel.MSEDGE_BETA;
case "msedge-dev":
return BrowserChannel.MSEDGE_DEV;
case "msedge-canary":
return BrowserChannel.MSEDGE_CANARY;
default:
throw new IllegalArgumentException("Unknown BROWSER_CHANNEL " + channel);
}
}
}
@Test
void shouldSupportDeprecatedChannelEnum(Playwright playwright) {
BrowserChannel channel = getBrowserChannelEnumFromEnv();
Assumptions.assumeTrue(channel != null);
BrowserType.LaunchOptions options = createLaunchOptions();
options.setChannel(channel);
BrowserType browserType = Utils.getBrowserTypeFromEnv(playwright);
Browser browser = browserType.launch(options);
assertNotNull(browser);
browser.close();
}
}
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.google.gson.Gson;
@@ -33,6 +33,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import static com.microsoft.playwright.Utils.copy;
@@ -469,4 +470,37 @@ public class TestBrowserContextHar extends TestBase {
assertThat(page2.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
}
}
@Test
void shouldIgnoreAbortedRequests(@TempDir Path tmpDir) {
Path path = tmpDir.resolve("test.har");
try (BrowserContext context1 = browser.newContext()) {
server.setRoute("/x", exchange -> exchange.close());
context1.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setUpdate(true));
Page page1 = context1.newPage();
page1.navigate(server.EMPTY_PAGE);
Future<Server.Request> reqPromise = server.futureRequest("/x");
Object req = page1.evaluate("url => fetch(url).catch(e => 'cancelled')", server.PREFIX + "/x");
assertEquals("cancelled", req);
}
server.reset();
try (BrowserContext context2 = browser.newContext()) {
server.setRoute("/x", exchange -> {
exchange.getResponseHeaders().add("content-type", "text/html");
exchange.sendResponseHeaders(200, 4);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("test");
}
});
context2.routeFromHAR(path);
Page page2 = context2.newPage();
page2.navigate(server.EMPTY_PAGE);
page2.evaluate("url => {\n" +
" fetch(url).catch(e => 'cancelled').then(r => { window.result = r; })\n" +
"}", server.PREFIX + "/x");
page2.waitForTimeout(1000);
assertNull(page.evaluate("window.result"));
}
}
}
@@ -139,4 +139,20 @@ public class TestBrowserContextStorageState extends TestBase {
}
context2.close();
}
@Test
void shouldSerialiseStorageStateWithLoneSurrogates() {
page.navigate(server.EMPTY_PAGE);
page.evaluate("chars => window.localStorage.setItem('foo', String.fromCharCode(55934))");
String storageState = context.storageState();
assertJsonEquals("{" +
"cookies:[]," +
"origins:[{\n" +
" origin: 'http://localhost:" + server.PORT + "',\n" +
" localStorage: [{\n" +
" name: 'foo',\n" +
" value: '" + (char)65533 + "'\n" +
" }]\n" +
"}]}", new Gson().fromJson(storageState, JsonObject.class));
}
}
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -66,7 +66,7 @@ public class TestBrowserTypeConnect extends TestBase {
Driver driver = Driver.ensureDriverInstalled(Collections.emptyMap(), false);
Path dir = driver.driverPath().getParent();
String node = dir.resolve(isWindows ? "node.exe" : "node").toString();
String cliJs = dir.resolve("package/lib/cli/cli.js").toString();
String cliJs = dir.resolve("package/cli.js").toString();
// We launch node process directly instead of using playwright.sh script as killing the script
// process will leave node process running and killing it would be more hassle.
ProcessBuilder pb = new ProcessBuilder(node, cliJs, "launch-server", "--browser", browserType.name());
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.microsoft.playwright.options.Geolocation;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.google.gson.Gson;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.microsoft.playwright.assertions.LocatorAssertions;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.google.gson.Gson;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -0,0 +1,58 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.microsoft.playwright.junit.Options;
import com.microsoft.playwright.junit.OptionsFactory;
public class TestOptionsFactories {
public static class BasicOptionsFactory implements OptionsFactory {
@Override
public Options getOptions() {
return new Options().setBrowserName(getBrowserName());
}
}
public static String getBrowserChannelFromEnv() {
return System.getenv("BROWSER_CHANNEL");
}
public static BrowserType.LaunchOptions createLaunchOptions() {
BrowserType.LaunchOptions options;
options = new BrowserType.LaunchOptions();
options.headless = !getHeadful();
return options;
}
private static boolean getHeadful() {
String headfulEnv = System.getenv("HEADFUL");
return headfulEnv != null && !"0".equals(headfulEnv) && !"false".equals(headfulEnv);
}
private static String getBrowserName() {
String browserName = System.getenv("BROWSER");
if (browserName == null) {
browserName = "chromium";
}
return browserName;
}
public static boolean isChromium() {
return getBrowserName().equals("chromium");
}
}
@@ -0,0 +1,172 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
public class TestPageAddLocatorHandler extends TestBase {
@Test
void shouldWork() {
page.navigate(server.PREFIX + "/input/handle-locator.html");
int[] beforeCount = {0};
int[] afterCount = {0};
page.addLocatorHandler(page.getByText("This interstitial covers the button"), () -> {
++beforeCount[0];
page.locator("#close").click();
++afterCount[0];
});
String[][] argsList = {
{"mouseover", "1"},
{"mouseover", "1", "capture"},
{"mouseover", "2"},
{"mouseover", "2", "capture"},
{"pointerover", "1"},
{"pointerover", "1", "capture"},
{"none", "1"},
{"remove", "1"},
{"hide", "1"},
};
for (String[] args : argsList) {
page.locator("#aside").hover();
beforeCount[0] = 0;
afterCount[0] = 0;
page.evaluate("(args) => {\n" +
" window.clicked = 0;\n" +
" window.setupAnnoyingInterstitial(...args);\n" +
"}", args);
assertEquals(0, beforeCount[0]);
assertEquals(0, afterCount[0]);
page.locator("#target").click();
assertEquals(Integer.parseInt(args[1]), beforeCount[0]);
assertEquals(Integer.parseInt(args[1]), afterCount[0]);
assertEquals(1, page.evaluate("window.clicked"));
assertThat(page.locator("#interstitial")).not().isVisible();
}
}
@Test
void shouldWorkWithCustomCheck() {
page.navigate(server.PREFIX + "/input/handle-locator.html");
page.addLocatorHandler(page.locator("body"), () -> {
if (page.getByText("This interstitial covers the button").isVisible())
page.locator("#close").click();
});
String[][] argsList = {
{"mouseover", "2"},
{"none", "1"},
{"remove", "1"},
{"hide", "1"},
};
for (String[] args : argsList) {
page.hover("#aside");
page.evaluate("(args) => {\n" +
" window.clicked = 0;\n" +
" window.setupAnnoyingInterstitial(...args);\n" +
"}", args);
page.locator("#target").click();
assertEquals(1, page.evaluate("window.clicked"));
assertThat(page.locator("#interstitial")).not().isVisible();
}
}
@Test
void shouldWorkWithLocatorHover() {
page.navigate(server.PREFIX + "/input/handle-locator.html");
page.addLocatorHandler(page.getByText("This interstitial covers the button"), () -> {
page.locator("#close").click();
});
page.locator("#aside").hover();
page.evaluate("() => {\n" +
" window.setupAnnoyingInterstitial('pointerover', 1, 'capture');\n" +
" }");
page.locator("#target").hover();
assertThat(page.locator("#interstitial")).not().isVisible();
assertEquals("rgb(255, 255, 0)", page.evalOnSelector("#target", "element => getComputedStyle(element).backgroundColor"));
}
@Test
void shouldNotWorkWithForceTrue() {
page.navigate(server.PREFIX + "/input/handle-locator.html");
page.addLocatorHandler(page.getByText("This interstitial covers the button"), () -> {
page.locator("#close").click();
});
page.locator("#aside").hover();
page.evaluate("() => {\n" +
" window.setupAnnoyingInterstitial('none', 1);\n" +
" }");
page.locator("#target").click(new Locator.ClickOptions().setForce(true).setTimeout(2000));
assertTrue(page.locator("#interstitial").isVisible());
assertNull(page.evaluate("window.clicked"));
}
@Test
void shouldThrowWhenPageCloses() {
page.navigate(server.PREFIX + "/input/handle-locator.html");
page.addLocatorHandler(page.getByText("This interstitial covers the button"), () -> {
page.close();
});
page.locator("#aside").hover();
page.evaluate("() => {\n" +
" window.clicked = 0;\n" +
" window.setupAnnoyingInterstitial('mouseover', 1);\n" +
" }");
PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.locator("#target").click());
assertTrue(e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage());
}
@Test
void shouldWorkWithToBeVisible() {
page.navigate(server.PREFIX + "/input/handle-locator.html");
int[] called = {0};
page.addLocatorHandler(page.getByText("This interstitial covers the button"), () -> {
++called[0];
page.locator("#close").click();
});
page.evaluate("() => {\n" +
" window.clicked = 0;\n" +
" window.setupAnnoyingInterstitial('remove', 1);\n" +
"}");
assertThat(page.locator("#target")).isVisible();
assertThat(page.locator("#interstitial")).not().isVisible();
assertEquals(1, called[0]);
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestPageAddScriptTag extends TestBase {
@Test
@DisabledIf(value="com.microsoft.playwright.TestBase#isWebKit", disabledReason="Upstream behavior")
void shouldIncludeSourceURLWhenPathIsProvided() {
page.navigate(server.EMPTY_PAGE);
Path path = Paths.get("src/test/resources/injectedfile.js");
page.addScriptTag(new Page.AddScriptTagOptions().setPath(path));
String result = (String) page.evaluate("() => window['__injectedError'].stack");
assertTrue(result.contains("resources" + File.separator + "injectedfile.js"), result);
}
}
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Assumptions;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -501,7 +501,8 @@ public class TestPageRoute extends TestBase {
} else {
assertEquals(1, requests.size());
}
assertEquals(400, (requests.get(0).response()).status());
// Java server fails to handle such requests.
// assertEquals(404, requests.get(0).response().status());
}
@Test
@@ -308,7 +308,18 @@ public class TestPageSetInputFiles extends TestBase {
assertEquals("file-to-upload.txt", page.evalOnSelector("input", "input => input.files[0].name"));
}
// @Test
@Test
void shouldAcceptSingleFilePayload() {
page.setContent("<input type=file oninput='javascript:console.timeStamp()'>");
FileChooser fileChooser = page.waitForFileChooser(() -> page.click("input"));
assertEquals(page, fileChooser.page());
assertNotNull(fileChooser.element());
fileChooser.setFiles(new FilePayload("test.txt", "text/plain", "Hello!".getBytes()));
assertEquals(1, page.evalOnSelector("input", "input => input.files.length"));
assertEquals("test.txt", page.evalOnSelector("input", "input => input.files[0].name"));
}
// @Test
void shouldDetectMimeType() throws ExecutionException, InterruptedException {
// TODO: Parse form fields on server
}
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -48,6 +48,21 @@ public class TestPdf extends TestBase {
assertTrue(size > 0);
}
@Test
@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="Printing to pdf is currently only supported in headless chromium.")
@DisabledIf(value="com.microsoft.playwright.TestBase#isHeadful", disabledReason="Printing to pdf is currently only supported in headless chromium.")
void shouldBeAbleToGenerateOutline(@TempDir Path tempDir) throws IOException {
page.navigate(server.PREFIX + "/headings.html");
Path outputFileNoOutline = tempDir.resolve("outputNoOutline.pdf");
Path outputFileOutline = tempDir.resolve("outputOutline.pdf");
page.pdf(new Page.PdfOptions().setPath(outputFileNoOutline));
page.pdf(new Page.PdfOptions().setPath(outputFileOutline).setTagged(true).setOutline(true));
long noOutlineSize = Files.size(outputFileNoOutline);
long outlineSize = Files.size(outputFileOutline);
assertTrue(outlineSize > noOutlineSize, "Unexpected sizes: " + outlineSize + " noOutline: " + noOutlineSize);
}
@Test
@DisabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="skip")
void shouldThrowInNonChromium() {
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,7 +1,24 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.microsoft.playwright.assertions.LocatorAssertions;
import com.microsoft.playwright.options.AriaRole;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.Collections;
@@ -12,6 +29,11 @@ import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
public class TestSelectorsGetBy extends TestBase {
@AfterEach
void resetTestId() {
playwright.selectors().setTestIdAttribute("data-testid");
}
@Test
void getByTestIdShouldWork() {
page.setContent("<div><div data-testid='Hello'>Hello world</div></div>");
@@ -20,6 +42,41 @@ public class TestSelectorsGetBy extends TestBase {
assertThat(page.locator("div").getByTestId("Hello")).hasText("Hello world");
}
@Test
void getByTestIdWithCustomTestIdShouldWork() {
page.setContent("<div><div data-my-custom-testid='Hello'>Hello world</div></div>");
playwright.selectors().setTestIdAttribute("data-my-custom-testid");
assertThat(page.getByTestId("Hello")).hasText("Hello world");
assertThat(page.mainFrame().getByTestId("Hello")).hasText("Hello world");
assertThat(page.locator("div").getByTestId("Hello")).hasText("Hello world");
}
@Test
void shouldUseDataTestidInStrictErrors() {
playwright.selectors().setTestIdAttribute("data-custom-id");
page.setContent("" +
" <div>\n" +
" <div></div>\n" +
" <div>\n" +
" <div></div>\n" +
" <div></div>\n" +
" </div>\n" +
" </div>\n" +
" <div>\n" +
" <div class='foo bar:0' data-custom-id='One'>\n" +
" </div>\n" +
" <div class='foo bar:1' data-custom-id='Two'>\n" +
" </div>\n" +
" </div>");
PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.locator(".foo").hover());
assertTrue(e.getMessage().contains("strict mode violation"), e.getMessage());
assertTrue(e.getMessage().contains("<div class=\"foo bar:0"), e.getMessage());
assertTrue(e.getMessage().contains("<div class=\"foo bar:1"), e.getMessage());
assertTrue(e.getMessage().contains("aka getByTestId(\"One\")"), e.getMessage());
assertTrue(e.getMessage().contains("aka getByTestId(\"Two\")"), e.getMessage());
}
@Test
void getByTestIdShouldEscapeId() {
page.setContent("<div><div data-testid='He\"llo'>Hello world</div></div>");
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import com.microsoft.playwright.options.AriaRole;
@@ -1,3 +1,19 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright;
import org.junit.jupiter.api.Test;

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