1
0
mirror of synced 2026-05-22 18:53:15 +00:00

Compare commits

...

162 Commits

Author SHA1 Message Date
Simon Knott 6b385a17b1 chore: mark 1.52.0 (#1783) 2025-05-02 08:51:06 +02:00
Simon Knott 8cca01851a chore: roll 1.52.0 driver, implement new features (#1780) 2025-04-29 09:13:50 -07:00
dependabot[bot] 478417bb56 chore(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.2 to 3.5.3 in the all group (#1774) 2025-04-01 11:38:02 -07:00
Max Schmitt 739202fddf test: do not send Content-Length header for HEAD requests (#1771) 2025-03-20 18:41:01 +01:00
Max Schmitt 8593941005 test: fix 'SLF4J(W): No SLF4J providers were found.' warning (#1772) 2025-03-20 18:38:34 +01:00
Max Schmitt b2852f5d57 devops: migrate to GitHub App for automation (#1770) 2025-03-19 14:24:34 +01:00
Yury Semikhatsky 6c059e351f chore: roll driver to 1.51.1 (#1768) 2025-03-17 12:03:43 -07:00
Yury Semikhatsky e86911ed2a docs: remove obsolete toc from readme (#1767) 2025-03-17 19:37:04 +01:00
Yury Semikhatsky 17cc3b8297 docs: abridge readme, redirect to playwright.dev (#1766) 2025-03-17 11:17:28 -07:00
dependabot[bot] 43016df241 chore(deps): bump the all group with 3 updates (#1764) 2025-03-17 11:16:44 -07:00
dependabot[bot] ba4eb3ce7d chore(deps): bump the actions group with 2 updates (#1765) 2025-03-17 11:16:22 -07:00
Max Schmitt ba7dd3cd89 devops: update GitHub Action workflows via dependabot (#1763) 2025-03-17 19:05:26 +01:00
Max Schmitt 5e37b7f5ca devops: add DevContainer config (#1762) 2025-03-17 19:02:33 +01:00
Yury Semikhatsky 84eaf8f3cb devops: retry failures up to 3 times on GHA (#1758) 2025-03-10 11:50:10 -07:00
Yury Semikhatsky c090824c93 chore: roll 1.51.0 driver, implement new features (#1757) 2025-03-07 19:02:19 -08:00
dependabot[bot] add7f56117 chore(deps): bump the all group with 7 updates (#1754) 2025-03-04 11:25:31 -08:00
Max Schmitt 676f38d22c devops: use PME environment for ESRP publishing (#1755) 2025-03-03 16:52:51 +01:00
Max Schmitt bc82f2fa68 chore: auto update example pw version in pom (#1748) 2025-02-13 10:08:42 +01:00
stevenfuhr 995cf902fb fix: add pfxBase64 to jsonCert instead of params (#1746) 2025-02-07 12:33:20 -08:00
Yury Semikhatsky 87152ecc71 devops: make publish step type: releaseJob (#1744) 2025-02-06 11:28:50 -08:00
Yury Semikhatsky dbc0478e40 chore: roll 1.50.1-beta (#1739) 2025-02-03 09:22:37 -08:00
dependabot[bot] 9eb1db9034 chore(deps): bump com.google.code.gson:gson from 2.11.0 to 2.12.1 in the all group (#1738) 2025-02-03 08:27:25 -08:00
Max Schmitt 4820088457 fix(urlMatcher): normalize URLs to align with Node.js parser behavior (#1734) 2025-01-27 10:26:39 -08:00
Max Schmitt fcd0444c57 fix(webSocketRoute): resolve URL against baseURL (#1722) 2025-01-27 15:43:47 +01:00
Yury Semikhatsky 067e69f339 chore: roll 1.50.0 (#1733) 2025-01-23 12:14:30 -08:00
Yury Semikhatsky 015939b150 feat: roll driver to 1.50 alpha (#1729) 2025-01-17 09:21:19 -08:00
Adam Gastineau eb8cf62d74 fix(tracing): Properly log Clock calls (#1727) 2025-01-16 07:04:53 -08:00
dependabot[bot] 308b9913e7 chore(deps): bump the all group with 5 updates (#1723) 2025-01-02 10:36:48 -08:00
Max Schmitt 6b621ce6f7 chore: refactor UrlMatcher (#1720) 2024-12-28 10:47:01 +01:00
Yury Semikhatsky 42d0203b49 fix: waitForCondition should not call predicate after it returned true (#1721) 2024-12-19 12:25:42 -08:00
Max Schmitt c591a1470a test: do not create stray files when running tests (#1718) 2024-12-18 11:49:18 +01:00
dependabot[bot] eb08046e94 chore(deps): bump the all group with 2 updates (#1710) 2024-12-02 10:49:48 -08:00
Yury Semikhatsky 2ff37da5f5 chore: mark 1.50 snapshot (#1702) 2024-11-18 17:01:09 -08:00
Yury Semikhatsky ee99afc3a3 chore: print actual message for TestBrowserContextCDPSession.shouldDe… (#1701) 2024-11-18 16:25:10 -08:00
Yury Semikhatsky 34017a26a3 chore: roll 1.49.0 (#1700) 2024-11-18 15:25:34 -08:00
Yury Semikhatsky 29f58a5840 test: unflake TestPageClock (#1699) 2024-11-15 14:38:02 -08:00
Yury Semikhatsky d2d78a7299 chore: stop using microsoft/playwright-github-action@v1 (#1698) 2024-11-15 11:20:37 -08:00
Yury Semikhatsky 6e66ee7c35 chore: roll 1.49-beta (#1697) 2024-11-15 09:24:21 -08:00
dependabot[bot] 4bda800e11 chore(deps): bump the all group with 4 updates (#1695) 2024-11-14 08:54:00 -08:00
Max Schmitt 2cce9776be devops: stop publishing Ubuntu 20.04 (#1690) 2024-10-21 17:00:06 +02:00
Yury Semikhatsky 20b13ad0c0 chore: roll driver 1.48.1 (#1687) 2024-10-17 12:06:13 -07:00
dependabot[bot] 08ac52ca53 chore(deps): bump the all group with 4 updates (#1680) 2024-10-17 11:56:19 -07:00
Yury Semikhatsky 9c220cd359 test: update web socket tests to properly dispatch ws messages (#1683) 2024-10-03 11:19:15 -07:00
Yury Semikhatsky ab443d1638 chore: roll 1.48 beta driver (#1681) 2024-10-02 14:21:13 -07:00
Max Schmitt 186aede95c devops: use wget for driver downloads (#1679) 2024-10-01 09:39:38 +02:00
Yury Semikhatsky db52fa94e7 docs: remove snapshot badge from README 2024-09-12 17:04:25 -07:00
Yury Semikhatsky ee18e1a499 chore: roll 1.47.0-beta-1726138322000 (#1672) 2024-09-12 15:02:49 -07:00
Yury Semikhatsky 9cf4bf2263 docs: Update ROLLING.md with new roll script 2024-09-12 12:03:29 -07:00
Yury Semikhatsky 36350f3c5c chore: roll 1.47.0 (#1670) 2024-09-09 11:06:17 -07:00
Max Schmitt f3476c68ff test: fix client-certificate tests (#1669) 2024-09-09 09:05:37 -07:00
Max Schmitt 8fd8f1c831 test: remove per-context proxy hacks for Windows/Chromium (#1668) 2024-09-09 10:09:00 +02:00
dependabot[bot] 7d2066693b chore(deps): bump the all group with 2 updates (#1664) 2024-09-03 08:55:04 -07:00
Yury Semikhatsky 6b01b878cb chore: roll driver to 1.47.0 alpha 2024 08 28 (#1663) 2024-08-28 16:02:34 -07:00
uchagani 2e32eb704f feat(junit): Implement automatic saving of traces and screenshots via fixtures (#1560) 2024-08-28 13:52:07 -07:00
Chris Kocel b81b144680 fix: null check in ListenerCollection notify method (#1661) 2024-08-28 13:50:55 -07:00
Yury Semikhatsky 256e41a505 docs: add SUPPORT.md (#1662) 2024-08-28 11:09:38 -07:00
Yury Semikhatsky 3054364101 devops(dependabot): update all deps in single PR monthly (#1656) 2024-08-19 13:21:31 -07:00
dependabot[bot] 40b8802874 chore(deps): bump org.apache.maven.plugins:maven-deploy-plugin from 3.1.2 to 3.1.3 (#1654) 2024-08-19 12:11:29 -07:00
dependabot[bot] 49f53fceaf chore(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.3.1 to 3.4.0 (#1653) 2024-08-19 12:11:13 -07:00
dependabot[bot] 291c12a54c chore(deps): bump org.apache.maven.plugins:maven-install-plugin from 3.1.2 to 3.1.3 (#1655) 2024-08-19 12:11:01 -07:00
dependabot[bot] 2b3413fad4 chore(deps): bump junit.version from 5.10.3 to 5.11.0 (#1652) 2024-08-19 11:16:59 -07:00
dependabot[bot] 3eab530e95 chore(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.4 to 3.2.5 (#1650) 2024-08-19 11:16:40 -07:00
Max Schmitt 7d35be4f89 devops: publish Ubuntu 24.04 Docker image (#1649) 2024-08-12 19:02:17 +02:00
Yury Semikhatsky 800c2e9c71 fix: support ControlOrMeta action modifier (#1644)
Fixes: https://github.com/microsoft/playwright-java/issues/1643
2024-08-07 17:08:33 -07:00
Meir Blachman edf8174581 docs: add discord link in readme (#1642) 2024-08-06 15:54:40 -07:00
Yury Semikhatsky 8ae67204eb test: disable failing certificate tests on webkit mac (#1641) 2024-08-06 13:08:26 -07:00
Yury Semikhatsky 5f2540e556 chore: roll driver to 1.46.0 (#1640) 2024-08-05 17:46:01 -07:00
Yury Semikhatsky 776e3f26ef chore: serizlize java Exception <==> javascript Error (#1639) 2024-08-02 10:32:18 -07:00
Yury Semikhatsky 46f4ac1f33 chore: roll driver to 1.46.0-beta (#1638) 2024-07-31 13:58:01 -07:00
dependabot[bot] 73d22552e6 chore(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.7.0 to 3.8.0 (#1631) 2024-07-23 15:03:57 -07:00
Thomas Fowler 650419c952 fix: Replaced println with logMessage in DriverJar (#1627) 2024-07-23 10:29:15 -07:00
dependabot[bot] 1e8adde480 chore(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.3.0 to 3.3.1 (#1625) 2024-07-15 13:45:23 -07:00
dependabot[bot] a9e1242aef chore(deps-dev): bump org.java-websocket:Java-WebSocket from 1.5.6 to 1.5.7 (#1626) 2024-07-15 13:45:11 -07:00
dependabot[bot] a222edee5a chore(deps): bump junit.version from 5.10.2 to 5.10.3 (#1616) 2024-07-11 08:59:14 -07:00
Yury Semikhatsky 7a12897be4 feat: support java.time.OffsetDateTime in post data (#1624)
Fixes https://github.com/microsoft/playwright-java/issues/1623
2024-07-11 08:58:46 -07:00
Yury Semikhatsky 7fa8081032 devops: test on java 21 which is latest lts (#1613) 2024-06-27 14:37:55 -07:00
Yury Semikhatsky 212bf981f7 chore: bump dev version to 1.46.0-SNAPSHOT (#1612) 2024-06-27 13:28:56 -07:00
Yury Semikhatsky d9ac70c66b chore: roll driver to 1.45.0-beta (#1610) 2024-06-27 13:22:47 -07:00
dependabot[bot] 4279a4ef3e chore(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.4.1 to 3.4.2 (#1604) 2024-06-27 10:15:05 -07:00
dependabot[bot] 141afb1f09 chore(deps): bump org.apache.maven.plugins:maven-clean-plugin from 3.3.2 to 3.4.0 (#1603) 2024-06-27 10:09:04 -07:00
dependabot[bot] 2eaae58659 chore(deps): bump org.apache.maven.plugins:maven-surefire-plugin from 3.2.5 to 3.3.0 (#1599) 2024-06-27 10:08:52 -07:00
Yury Semikhatsky e4828d00b6 chore: roll 1.45.0 (#1609) 2024-06-25 09:18:12 -07:00
Yury Semikhatsky a08ab2dcae devops: roll_driver script (#1605) 2024-06-25 08:34:20 -07:00
Jason Wu 626050d988 fix: Update README.md to fix wrong demo code (#1601)
Update README.md to fix wrong demo code

In Mobile and geolocation demo, code `page.click("a[data-original-title=\"Show My Location\"]");` will cause error while running.Because the website already change the attribute.
2024-06-19 09:24:45 -07:00
Yury Semikhatsky 4cc3fa3012 chore: roll driver to 1.45.0 beta, implement new features (#1600) 2024-06-18 17:08:45 -07:00
dependabot[bot] 226d075355 chore(deps): bump org.apache.maven.plugins:maven-javadoc-plugin from 3.6.3 to 3.7.0 (#1589) 2024-06-03 15:42:10 -07:00
Yury Semikhatsky 0a759e699e fix(fetch): support json objects with null values (#1587)
Fixes https://github.com/microsoft/playwright-java/issues/1585
2024-05-30 14:46:17 -07:00
Yury Semikhatsky f2a17b6255 devops: install msedge on macosx (#1581) 2024-05-20 16:21:27 -07:00
dependabot[bot] 202bc80d76 chore(deps): bump com.google.code.gson:gson from 2.10.1 to 2.11.0 (#1580) 2024-05-20 16:07:03 -07:00
Yury Semikhatsky 731d8e8dc2 chore: bump dev version to 1.45.0-SNAPSHOT (#1579) 2024-05-17 09:12:49 -07:00
Yury Semikhatsky 75062c4024 chore: roll 1.44.0 (#1575) 2024-05-08 12:10:05 -07:00
Yury Semikhatsky c9ea56a640 devops: stop producing .sha256 files, they are not required anymore (#1570) 2024-05-03 11:03:45 -07:00
Max Schmitt e4c427aa75 devops: fix ESRP publishing (#1569) 2024-05-03 10:55:40 -07:00
Max Schmitt 0471c5e86c devops: update to EsrpRelease@7 (#1566) 2024-05-03 09:03:41 -07:00
Yury Semikhatsky 5636edf69a test: ControlOrMeta modifier (#1564) 2024-05-02 16:30:27 -07:00
dependabot[bot] abfe50ce59 chore(deps): bump org.apache.maven.plugins:maven-jar-plugin from 3.3.0 to 3.4.1 (#1556) 2024-05-01 15:15:23 -07:00
Yury Semikhatsky d72364627b chore: roll driver to 1.44.0-beta-1714435420000 (#1563) 2024-04-30 08:57:03 -07:00
dependabot[bot] fe51fb4cf6 chore(deps): bump org.apache.maven.plugins:maven-install-plugin from 3.1.1 to 3.1.2 (#1562) 2024-04-29 17:02:12 -07:00
dependabot[bot] 764cc8cc8a chore(deps): bump org.apache.maven.plugins:maven-deploy-plugin from 3.1.1 to 3.1.2 (#1561) 2024-04-29 17:01:59 -07:00
Yury Semikhatsky 7b8efadc57 chore: roll driver, implement new features (#1559) 2024-04-27 08:46:04 -07:00
uchagani a654a4234e feat(junit): add ability to connect to remote browsers via fixtures (#1541) 2024-04-22 13:09:33 -07:00
dependabot[bot] 102d337b4a chore(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.3 to 3.2.4 (#1555) 2024-04-22 11:29:53 -07:00
Max Schmitt f5f9b8a12d devops: migrate to OIDC for Docker publishing (#1554) 2024-04-19 00:13:33 +02:00
Max Schmitt 2f264eab76 fix(cdpSession): events without payload (#1553) 2024-04-17 09:10:55 -07:00
dependabot[bot] 5c17cc49ed chore(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.2 to 3.2.3 (#1550) 2024-04-16 10:57:00 -07:00
Max Schmitt 8652942482 fix(driver): consider PLAYWRIGHT_NODEJS_PATH from host env (#1552) 2024-04-16 19:54:15 +02:00
dependabot[bot] 2829a37d58 chore(deps): bump org.apache.maven.plugins:maven-source-plugin from 3.3.0 to 3.3.1 (#1543) 2024-04-10 09:19:51 -07:00
Yury Semikhatsky 90aa4579c4 chore: move junit impl to com.microsoft.playwright.impl.junit (#1538) 2024-04-03 15:31:20 -07:00
Yury Semikhatsky 1a8f5f7746 docs: fix broken class links, format details and usage (#1536) 2024-04-03 14:15:22 -07:00
Yury Semikhatsky a917f2ef5c fix(docs): generate javadocs (#1534)
Reference https://github.com/microsoft/playwright-java/issues/1533
2024-04-03 13:22:23 -07:00
Yury Semikhatsky 452effbe3f chore: implement context.backgroundPages (#1532)
Reference https://github.com/microsoft/playwright-java/issues/1530
2024-04-02 10:54:41 -07:00
dependabot[bot] f497bcc27d chore(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.1 to 3.2.2 (#1531) 2024-04-01 18:10:20 -07:00
uchagani 240f8d0873 feat(junit): make getOrCreate fixture methods public (#1527) 2024-03-29 09:21:35 -07:00
Yury Semikhatsky e7653ddde7 chore: roll driver to 1.43.0-beta, implement clearCookie(filter) (#1525) 2024-03-27 17:51:31 -07:00
uchagani a135675580 chore(test): Convert tests to use fixtures (#1521) 2024-03-22 10:33:48 -07:00
uchagani 4b9a436d57 fix(junit): Fixes fixtures not working in nested junit test classes (#1515) 2024-03-21 17:26:47 -07:00
Yury Semikhatsky f15147ba33 chore: get rid of driver wrapper script (#1519)
Fixes https://github.com/microsoft/playwright-java/issues/1518
2024-03-20 16:52:36 -07:00
dependabot[bot] 8cdfd183b8 chore(deps): bump org.apache.maven.plugins:maven-compiler-plugin from 3.12.1 to 3.13.0 (#1516) 2024-03-20 15:44:36 -07:00
dependabot[bot] c37b13eafb chore(deps): bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.0 to 3.2.1 (#1517) 2024-03-20 15:44:19 -07:00
Yury Semikhatsky 53879c8405 chore: bump dev version to 1.43 (#1513) 2024-03-11 17:00:33 -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
Yury Semikhatsky bc2857053c chore: roll driver 1.41.0 (#1450) 2024-01-17 11:26:02 -08:00
Yury Semikhatsky 7e6f98c903 chore: roll driver to 1.41 beta (#1449) 2024-01-17 08:55:22 -08:00
Yury Semikhatsky e889b20fda chore: roll driver to 1.41.0-alpha (#1443) 2023-12-18 16:01:35 -08:00
uchagani f28ca44fa0 feat(junit-playwright) (#1412) 2023-12-04 17:32:59 -08:00
Yury Semikhatsky d73f953e68 chore: mark 1.41 dev version (#1428) 2023-11-20 11:03:13 -08:00
Yury Semikhatsky cc28516281 chore: mark version 1.40.0 (#1427) 2023-11-20 10:57:07 -08:00
276 changed files with 16004 additions and 4420 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"
}
}
}
+89 -58
View File
@@ -1,62 +1,93 @@
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"
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: Build
templateContext:
outputs:
- output: pipelineArtifact
path: $(Build.ArtifactStagingDirectory)/esrp-build
artifact: esrp-build
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.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:$(Build.ArtifactStagingDirectory)/esrp-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: |
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'
- job: Publish
dependsOn: Build
templateContext:
type: releaseJob
isProduction: true
inputs:
- input: pipelineArtifact
artifactName: esrp-build
targetPath: $(Build.ArtifactStagingDirectory)/esrp-build
steps:
- checkout: none
- task: EsrpRelease@9
inputs:
connectedservicename: 'Playwright-ESRP-PME'
usemanagedidentity: true
keyvaultname: 'playwright-esrp-pme'
signcertname: 'ESRP-Release-Sign'
clientid: '13434a40-7de4-4c23-81a3-d843dc81c2c5'
intent: 'PackageDistribution'
contenttype: 'Maven'
# Keeping it commented out as a workaround for:
# https://portal.microsofticm.com/imp/v3/incidents/incident/499972482/summary
# contentsource: 'folder'
folderlocation: '$(Build.ArtifactStagingDirectory)/esrp-build'
waitforreleasecompletion: true
owners: 'yurys@microsoft.com'
approvers: 'maxschmitt@microsoft.com'
serviceendpointurl: 'https://api.esrp.microsoft.com'
mainpublisher: 'Playwright'
domaintenantid: '975f013f-7f24-47e8-a7d3-abc4752bf346'
displayName: 'ESRP Release to Maven'
+28
View File
@@ -0,0 +1,28 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/java
{
"name": "Java",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/java:1-21-bookworm",
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "none",
"installGradle": "false",
"installMaven": "true"
},
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
}
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "java -version",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
-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
+27
View File
@@ -0,0 +1,27 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/" # Location of the pom.xml file
schedule:
interval: "monthly"
open-pull-requests-limit: 10
groups:
# Create a group of dependencies to be updated together in one pull request
all:
applies-to: version-updates
patterns:
- "*"
update-types:
- "minor"
- "patch"
allow:
- dependency-type: "direct" # Optional: Only update direct dependencies
- dependency-type: "indirect" # Optional: Only update indirect (transitive) dependencies
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
groups:
actions:
patterns:
- "*"
@@ -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
+30
View File
@@ -0,0 +1,30 @@
name: Publish Release Docker
on:
release:
types: [published]
workflow_dispatch:
jobs:
publish-canary-docker:
name: publish to DockerHub
runs-on: ubuntu-22.04
permissions:
id-token: write # This is required for OIDC login (azure/login) to succeed
contents: read # This is required for actions/checkout to succeed
environment: Docker
if: github.repository == 'microsoft/playwright-java'
steps:
- uses: actions/checkout@v4
- name: Azure login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_DOCKER_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_DOCKER_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_DOCKER_SUBSCRIPTION_ID }}
- name: Login to ACR via OIDC
run: az acr login --name playwright
- name: Set up Docker QEMU for arm64 docker builds
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- uses: actions/checkout@v4
- run: ./utils/docker/publish_docker.sh stable
@@ -1,33 +0,0 @@
name: Publish Release Docker
on:
release:
types: [published]
workflow_dispatch:
inputs:
is_release:
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
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 }}
- name: Set up Docker QEMU for arm64 docker builds
uses: docker/setup-qemu-action@v1
with:
platforms: arm64
- uses: actions/checkout@v2
- 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
if: (github.event_name != 'workflow_dispatch' && github.event.release.prerelease) || (github.event_name == 'workflow_dispatch' && github.event.inputs.is_release != 'true')
+25 -16
View File
@@ -8,6 +8,8 @@ on:
branches:
- main
- release-*
env:
PW_MAX_RETRIES: 3
jobs:
dev:
timeout-minutes: 30
@@ -18,24 +20,25 @@ jobs:
browser: [chromium, firefox, webkit]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- uses: actions/checkout@v4
- name: Set up JDK 1.8
uses: actions/setup-java@v2
uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 8
- name: Download drivers
shell: bash
run: scripts/download_driver_for_all_platforms.sh
run: scripts/download_driver.sh
- name: Build & Install
run: mvn -B install -D skipTests --no-transfer-progress
- name: Install browsers
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Run tests
run: mvn test --no-transfer-progress --fail-at-end -D org.slf4j.simpleLogger.showDateTime=true -D org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
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
@@ -62,29 +65,34 @@ jobs:
browser-channel: msedge
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- uses: actions/checkout@v4
- name: Install Media Pack
if: matrix.os == 'windows-latest'
shell: powershell
run: Install-WindowsFeature Server-Media-Foundation
- name: Set up JDK 1.8
uses: actions/setup-java@v2
uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 8
- name: Download drivers
shell: bash
run: scripts/download_driver_for_all_platforms.sh
run: scripts/download_driver.sh
- name: Build & Install
run: mvn -B install -D skipTests --no-transfer-progress
- name: Install browsers
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Install MS Edge
if: matrix.browser-channel == 'msedge' && matrix.os == 'macos-latest'
shell: bash
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install msedge" -f playwright/pom.xml
- name: Run tests
run: mvn test --no-transfer-progress --fail-at-end -D org.slf4j.simpleLogger.showDateTime=true -D org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
env:
BROWSER: chromium
BROWSER_CHANNEL: ${{ matrix.browser-channel }}
Java_17:
Java_21:
timeout-minutes: 30
strategy:
fail-fast: false
@@ -92,18 +100,19 @@ jobs:
browser: [chromium, firefox, webkit]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- name: Set up JDK 17
uses: actions/setup-java@v2
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: adopt
java-version: 17
java-version: 21
- name: Download drivers
shell: bash
run: scripts/download_driver_for_all_platforms.sh
run: scripts/download_driver.sh
- name: Build & Install
run: mvn -B install -D skipTests --no-transfer-progress
- name: Install browsers
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Run tests
run: mvn test --no-transfer-progress --fail-at-end
env:
+3 -3
View File
@@ -13,15 +13,15 @@ jobs:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Cache Maven packages
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Download drivers
run: scripts/download_driver_for_all_platforms.sh
run: scripts/download_driver.sh
- name: Intall Playwright
run: mvn install -D skipTests --no-transfer-progress
- name: Test CLI
+3 -3
View File
@@ -11,7 +11,7 @@ on:
paths:
- .github/workflows/test_docker.yml
- '**/Dockerfile*'
- scripts/CLI_VERSION
- scripts/DRIVER_VERSION
- '**/pom.xml'
branches:
- main
@@ -24,9 +24,9 @@ jobs:
strategy:
fail-fast: false
matrix:
flavor: [focal, jammy]
flavor: [jammy, noble]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build Docker image
run: bash utils/docker/build.sh --amd64 ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }}
- name: Test
+9 -3
View File
@@ -9,13 +9,19 @@ on:
jobs:
trigger:
name: "trigger"
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.PLAYWRIGHT_APP_ID }}
private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }}
repositories: playwright-browsers
- run: |
curl -X POST \
curl -X POST --fail \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${GH_TOKEN}" \
--data "{\"event_type\": \"playwright_tests_java\", \"client_payload\": {\"ref\": \"${GITHUB_SHA}\"}}" \
https://api.github.com/repos/microsoft/playwright-browsers/dispatches
env:
GH_TOKEN: ${{ secrets.REPOSITORY_DISPATCH_PERSONAL_ACCESS_TOKEN }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
+6 -3
View File
@@ -19,12 +19,15 @@ jobs:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- uses: actions/checkout@v4
- name: Download drivers
run: scripts/download_driver_for_all_platforms.sh
run: scripts/download_driver.sh
- name: Regenerate APIs
run: scripts/generate_api.sh
- name: Build & Install
run: mvn -B install -D skipTests --no-transfer-progress
- name: Install browsers
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Update browser versions in README
run: scripts/update_readme.sh
- name: Verify API is up to date
+8 -16
View File
@@ -20,46 +20,38 @@ git clone https://github.com/microsoft/playwright-java
cd playwright-java
```
2. Run the following script to download playwright-cli binaries for all platforms into `driver-bundle/src/main/resources/driver/` directory (browser binaries for Chromium, Firefox and WebKit will be automatically downloaded later on first Playwright run).
2. Run the following script to download Playwright driver for all platforms into `driver-bundle/src/main/resources/driver/` directory (browser binaries for Chromium, Firefox and WebKit will be automatically downloaded later on first Playwright run).
```bash
scripts/download_driver_for_all_platforms.sh
scripts/download_driver.sh
```
Names of published driver archives can be found at https://github.com/microsoft/playwright-cli/actions
### Building and running the tests with Maven
```bash
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
Public Java API is generated from api.json which is produced by `playwright-cli print-api-json`. To regenerate
Java interfaces for the current driver run the following commands:
Public Java API is generated from api.json which is produced by `print-api-json` command of playwright CLI. To regenerate Java interfaces for the current driver run the following commands:
```bash
./scripts/download_driver_for_all_platforms.sh
./scripts/download_driver.sh
./scripts/generate_api.sh
```
#### Updating driver version
Driver version is read from [scripts/CLI_VERSION](https://github.com/microsoft/playwright-java/blob/main/scripts/CLI_VERSION) and can be found in the upstream [GHA build](https://github.com/microsoft/playwright/actions/workflows/publish_canary.yml) logs. To update the driver to a particular version run the following commands:
Versions of published driver archives can be found in [publish canary](https://github.com/microsoft/playwright/actions/workflows/publish_canary.yml) and [publish release](https://github.com/microsoft/playwright/actions/workflows/publish_release_driver.yml) actions logs. To update the driver to a particular version run the following command:
```bash
cat > scripts/CLI_VERSION
<paste new version>
^D
./scripts/download_driver_for_all_platforms.sh -f
./scripts/generate_api.sh
./scripts/update_readme.sh
scripts/roll_driver.sh [version]
```
### Code Style
+14 -146
View File
@@ -2,8 +2,7 @@
[![javadoc](https://javadoc.io/badge2/com.microsoft.playwright/playwright/javadoc.svg)](https://javadoc.io/doc/com.microsoft.playwright/playwright)
[![maven version](https://img.shields.io/maven-central/v/com.microsoft.playwright/playwright)](https://search.maven.org/search?q=com.microsoft.playwright)
[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/https/oss.sonatype.org/com.microsoft.playwright/playwright.svg)](https://oss.sonatype.org/content/repositories/snapshots/com/microsoft/playwright/playwright/)
[![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://aka.ms/playwright-slack)
[![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord)
#### [Website](https://playwright.dev/java/) | [API reference](https://www.javadoc.io/doc/com.microsoft.playwright/playwright/latest/index.html)
@@ -11,59 +10,19 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->120.0.6099.28<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->119.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->136.0.7103.25<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->18.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->137.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.
## Documentation
* [Usage](#usage)
- [Add Maven dependency](#add-maven-dependency)
- [Is Playwright thread-safe?](#is-playwright-thread-safe)
* [Examples](#examples)
- [Page screenshot](#page-screenshot)
- [Mobile and geolocation](#mobile-and-geolocation)
- [Evaluate JavaScript in browser](#evaluate-javascript-in-browser)
- [Intercept network requests](#intercept-network-requests)
* [Documentation](#documentation)
* [Contributing](#contributing)
* [Is Playwright for Java ready?](#is-playwright-for-java-ready)
[https://playwright.dev/java/docs/intro](https://playwright.dev/java/docs/intro)
## Usage
## API Reference
Playwright requires **Java 8** or newer.
[https://playwright.dev/java/docs/api/class-playwright](https://playwright.dev/java/docs/api/class-playwright)
#### Add Maven dependency
Playwright is distributed as a set of [Maven](https://maven.apache.org/what-is-maven.html) modules. The easiest way to use it is to add one dependency to your Maven `pom.xml` file as described below. If you're not familiar with Maven please refer to its [documentation](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html).
To run Playwright simply add following dependency to your Maven project:
```xml
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.28.1</version>
</dependency>
```
To run Playwright using Gradle add following dependency to your build.gradle file:
```gradle
dependencies {
implementation group: 'com.microsoft.playwright', name: 'playwright', version: '1.28.1'
}
```
#### Is Playwright thread-safe?
No, Playwright is not thread safe, i.e. all its methods as well as methods on all objects created by it (such as BrowserContext, Browser, Page etc.) are expected to be called on the same thread where Playwright object was created or proper synchronization should be implemented to ensure only one thread calls Playwright methods at any given time. Having said that it's okay to create multiple Playwright instances each on its own thread.
## Examples
You can find Maven project with the examples [here](./examples).
#### Page screenshot
## Example
This code snippet navigates to Playwright homepage in Chromium, Firefox and WebKit, and saves 3 screenshots.
@@ -95,100 +54,9 @@ public class PageScreenshot {
}
```
#### Mobile and geolocation
## Other languages
This snippet emulates Mobile Chromium on a device at a given geolocation, navigates to openstreetmap.org, performs action and takes a screenshot.
```java
import com.microsoft.playwright.options.*;
import com.microsoft.playwright.*;
import java.nio.file.Paths;
import static java.util.Arrays.asList;
public class MobileAndGeolocation {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch();
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.setUserAgent("Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36")
.setViewportSize(411, 731)
.setDeviceScaleFactor(2.625)
.setIsMobile(true)
.setHasTouch(true)
.setLocale("en-US")
.setGeolocation(41.889938, 12.492507)
.setPermissions(asList("geolocation")));
Page page = context.newPage();
page.navigate("https://www.openstreetmap.org/");
page.click("a[data-original-title=\"Show My Location\"]");
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("colosseum-pixel2.png")));
}
}
}
```
#### Evaluate JavaScript in browser
This code snippet navigates to example.com in Firefox, and executes a script in the page context.
```java
import com.microsoft.playwright.*;
public class EvaluateInBrowserContext {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.firefox().launch();
BrowserContext context = browser.newContext();
Page page = context.newPage();
page.navigate("https://www.example.com/");
Object dimensions = page.evaluate("() => {\n" +
" return {\n" +
" width: document.documentElement.clientWidth,\n" +
" height: document.documentElement.clientHeight,\n" +
" deviceScaleFactor: window.devicePixelRatio\n" +
" }\n" +
"}");
System.out.println(dimensions);
}
}
}
```
#### Intercept network requests
This code snippet sets up request routing for a WebKit page to log all network requests.
```java
import com.microsoft.playwright.*;
public class InterceptNetworkRequests {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.webkit().launch();
BrowserContext context = browser.newContext();
Page page = context.newPage();
page.route("**", route -> {
System.out.println(route.request().url());
route.resume();
});
page.navigate("http://todomvc.com");
}
}
}
```
## Documentation
Check out our official [documentation site](https://playwright.dev/java).
You can also browse [javadoc online](https://www.javadoc.io/doc/com.microsoft.playwright/playwright/latest/index.html).
## Contributing
Follow [the instructions](https://github.com/microsoft/playwright-java/blob/main/CONTRIBUTING.md#getting-code) to build the project from source and install the driver.
## Is Playwright for Java ready?
Yes, Playwright for Java is ready. v1.10.0 is the first stable release. Going forward we will adhere to [semantic versioning](https://semver.org/) of the API.
More comfortable in another programming language? [Playwright](https://playwright.dev) is also available in
- [Node.js (JavaScript / TypeScript)](https://playwright.dev/docs/intro),
- [Python](https://playwright.dev/python/docs/intro).
- [.NET](https://playwright.dev/dotnet/docs/intro),
+2 -3
View File
@@ -2,11 +2,10 @@
* make sure to have at least Java 8 and Maven 3.6.3
* clone playwright for java: http://github.com/microsoft/playwright-java
* set new driver version in `scripts/CLI_VERSION`
* regenerate API: `./scripts/download_driver_for_all_platforms.sh -f && ./scripts/generate_api.sh && ./scripts/update_readme.sh`
* `./scripts/roll_driver.sh 1.47.0-beta-1726138322000`
* commit & send PR with the roll
### Finding driver version
## Finding driver version
For development versions of Playwright, you can find the latest version by looking at [publish_canary](https://github.com/microsoft/playwright/actions/workflows/publish_canary.yml) workflow -> `publish canary NPM & Publish canary Docker` -> `build & publish driver` step -> `PACKAGE_VERSION`
<img width="960" alt="image" src="https://github.com/microsoft/playwright-java/assets/9798949/4f33a7f1-b39a-4179-8ae7-fb1d84094c75">
+17
View File
@@ -0,0 +1,17 @@
# Support
## How to file issues and get help
This project uses GitHub issues to track bugs and feature requests. Please search the [existing issues][gh-issues] before filing new ones to avoid duplicates. For new issues, file your bug or feature request as a new issue using corresponding template.
For help and questions about using this project, please see the [docs site for Playwright for Java][docs].
Join our community [Discord Server][discord-server] to connect with other developers using Playwright and ask questions in our 'help-playwright' forum.
## Microsoft Support Policy
Support for Playwright for Java is limited to the resources listed above.
[gh-issues]: https://github.com/microsoft/playwright-java/issues/
[docs]: https://playwright.dev/java/
[discord-server]: https://aka.ms/playwright/discord
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.40.0-SNAPSHOT</version>
<version>1.52.0</version>
</parent>
<artifactId>driver-bundle</artifactId>
@@ -29,7 +29,6 @@ import java.util.concurrent.TimeUnit;
public class DriverJar extends Driver {
private static final String PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD";
private static final String SELENIUM_REMOTE_URL = "SELENIUM_REMOTE_URL";
static final String PLAYWRIGHT_NODEJS_PATH = "PLAYWRIGHT_NODEJS_PATH";
private final Path driverTempDir;
private Path preinstalledNodePath;
@@ -64,7 +63,7 @@ public class DriverJar extends Driver {
env.put(PLAYWRIGHT_NODEJS_PATH, preinstalledNodePath.toString());
}
extractDriverToTempDir();
logMessage("extracted driver from jar to " + driverPath());
logMessage("extracted driver from jar to " + driverDir());
if (installBrowsers)
installBrowsers(env);
}
@@ -75,14 +74,14 @@ public class DriverJar extends Driver {
skip = System.getenv(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD);
}
if (skip != null && !"0".equals(skip) && !"false".equals(skip)) {
System.out.println("Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set");
logMessage("Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set");
return;
}
if (env.get(SELENIUM_REMOTE_URL) != null || System.getenv(SELENIUM_REMOTE_URL) != null) {
logMessage("Skipping browsers download because `SELENIUM_REMOTE_URL` env variable is set");
return;
}
Path driver = driverPath();
Path driver = driverDir();
if (!Files.exists(driver)) {
throw new RuntimeException("Failed to find driver: " + driver);
}
@@ -204,7 +203,7 @@ public class DriverJar extends Driver {
}
@Override
protected Path driverDir() {
public Path driverDir() {
return driverTempDir;
}
}
@@ -32,7 +32,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.microsoft.playwright.impl.driver.jar.DriverJar.PLAYWRIGHT_NODEJS_PATH;
import static com.microsoft.playwright.impl.driver.Driver.PLAYWRIGHT_NODEJS_PATH;
import static java.util.Collections.singletonMap;
import static org.junit.jupiter.api.Assertions.*;
@@ -83,7 +83,7 @@ public class TestInstall {
@Test
void playwrightCliInstalled() throws Exception {
Driver driver = Driver.createAndInstall(Collections.emptyMap(), false);
assertTrue(Files.exists(driver.driverPath()));
assertTrue(Files.exists(driver.driverDir()));
ProcessBuilder pb = driver.createProcessBuilder();
pb.command().add("install");
@@ -98,7 +98,7 @@ public class TestInstall {
void playwrightDriverInAlternativeTmpdir(@TempDir Path tmpdir) throws Exception {
System.setProperty("playwright.driver.tmpdir", tmpdir.toString());
DriverJar driver = new DriverJar();
assertTrue(driver.driverPath().startsWith(tmpdir), "Driver path: " + driver.driverPath() + " tmp: " + tmpdir);
assertTrue(driver.driverDir().startsWith(tmpdir), "Driver path: " + driver.driverDir() + " tmp: " + tmpdir);
}
@Test
@@ -135,7 +135,7 @@ public class TestInstall {
private static String extractNodeJsToTemp() throws URISyntaxException, IOException {
DriverJar auxDriver = new DriverJar();
auxDriver.extractDriverToTempDir();
String nodePath = auxDriver.driverPath().getParent().resolve(isWindows() ? "node.exe" : "node").toString();
String nodePath = auxDriver.driverDir().resolve(isWindows() ? "node.exe" : "node").toString();
return nodePath;
}
@@ -145,9 +145,9 @@ public class TestInstall {
}
private static void canSpecifyPreinstalledNodeJsShared(Driver driver, Path tmpDir) throws IOException, URISyntaxException, InterruptedException {
Path builtinNode = driver.driverPath().getParent().resolve("node");
Path builtinNode = driver.driverDir().resolve("node");
assertFalse(Files.exists(builtinNode), builtinNode.toString());
Path builtinNodeExe = driver.driverPath().getParent().resolve("node.exe");
Path builtinNodeExe = driver.driverDir().resolve("node.exe");
assertFalse(Files.exists(builtinNodeExe), builtinNodeExe.toString());
ProcessBuilder pb = driver.createProcessBuilder();
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.40.0-SNAPSHOT</version>
<version>1.52.0</version>
</parent>
<artifactId>driver</artifactId>
@@ -18,7 +18,6 @@ package com.microsoft.playwright.impl.driver;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -30,7 +29,8 @@ import static com.microsoft.playwright.impl.driver.DriverLogging.logWithTimestam
* loaded from the driver-bundle module if that module is in the classpath.
*/
public abstract class Driver {
protected final Map<String, String> env = new LinkedHashMap<>();
protected final Map<String, String> env = new LinkedHashMap<>(System.getenv());
public static final String PLAYWRIGHT_NODEJS_PATH = "PLAYWRIGHT_NODEJS_PATH";
private static Driver instance;
@@ -47,7 +47,7 @@ public abstract class Driver {
}
@Override
protected Path driverDir() {
public Path driverDir() {
return driverDir;
}
}
@@ -65,14 +65,14 @@ public abstract class Driver {
}
protected abstract void initialize(Boolean installBrowsers) throws Exception;
public Path driverPath() {
String cliFileName = System.getProperty("os.name").toLowerCase().contains("windows") ?
"playwright.cmd" : "playwright.sh";
return driverDir().resolve(cliFileName);
}
public ProcessBuilder createProcessBuilder() {
ProcessBuilder pb = new ProcessBuilder(driverPath().toString());
String nodePath = env.get("PLAYWRIGHT_NODEJS_PATH");
if (nodePath == null) {
String node = System.getProperty("os.name").toLowerCase().contains("windows") ? "node.exe" : "node";
nodePath = driverDir().resolve(node).toAbsolutePath().toString();
}
ProcessBuilder pb = new ProcessBuilder(nodePath);
pb.command().add(driverDir().resolve("package").resolve("cli.js").toAbsolutePath().toString());
pb.environment().putAll(env);
pb.environment().put("PW_LANG_NAME", "java");
pb.environment().put("PW_LANG_NAME_VERSION", getMajorJavaVersion());
@@ -118,7 +118,7 @@ public abstract class Driver {
return (Driver) jarDriver.getDeclaredConstructor().newInstance();
}
protected abstract Path driverDir();
public abstract Path driverDir();
protected static void logMessage(String message) {
// This matches log format produced by the server.
+4 -3
View File
@@ -6,16 +6,17 @@
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>1.40.0-SNAPSHOT</version>
<version>1.52.0</version>
<name>Playwright Client Examples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<playwright.version>1.51.0</playwright.version>
</properties>
<dependencies>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.30.0</version>
<version>${playwright.version}</version>
</dependency>
</dependencies>
<build>
@@ -23,7 +24,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>
+14 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.40.0-SNAPSHOT</version>
<version>1.52.0</version>
</parent>
<artifactId>playwright</artifactId>
@@ -57,10 +57,23 @@
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
</dependency>
<!--
The following slf4j-simple dependency resolves the warning:
'SLF4J(W): No SLF4J providers were found.'
This warning is produced by the org.java-websocket library.
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
</dependency>
<dependency>
<groupId>org.opentest4j</groupId>
<artifactId>opentest4j</artifactId>
@@ -23,14 +23,14 @@ import java.util.*;
/**
* Exposes API that can be used for the Web API testing. This class is used for creating {@code APIRequestContext} instance
* which in turn can be used for sending web requests. An instance of this class can be obtained via {@link
* Playwright#request Playwright.request()}. For more information see {@code APIRequestContext}.
* com.microsoft.playwright.Playwright#request Playwright.request()}. For more information see {@code APIRequestContext}.
*/
public interface APIRequest {
class NewContextOptions {
/**
* Methods like {@link APIRequestContext#get APIRequestContext.get()} take the base URL into consideration by using the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* corresponding URL. Examples:
* Methods like {@link com.microsoft.playwright.APIRequestContext#get APIRequestContext.get()} take the base URL into
* consideration by using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a>
* constructor for building the corresponding URL. Examples:
* <ul>
* <li> baseURL: {@code http://localhost:3000} and sending request to {@code /bar.html} results in {@code
* http://localhost:3000/bar.html}</li>
@@ -41,10 +41,28 @@ public interface APIRequest {
* </ul>
*/
public String baseURL;
/**
* TLS Client Authentication allows the server to request a client certificate and verify it.
*
* <p> <strong>Details</strong>
*
* <p> An array of client certificates to be used. Each certificate object must have either both {@code certPath} and {@code
* keyPath}, a single {@code pfxPath}, or their corresponding direct value equivalents ({@code cert} and {@code key}, or
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
*
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
* replacing {@code localhost} with {@code local.playwright}.
*/
public List<ClientCertificate> clientCertificates;
/**
* An object containing additional HTTP headers to be sent with every request. Defaults to none.
*/
public Map<String, String> extraHTTPHeaders;
/**
* Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes.
*/
public Boolean failOnStatusCode;
/**
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>. If
* no origin is specified, the username and password are sent to any servers upon unauthorized responses.
@@ -54,22 +72,29 @@ public interface APIRequest {
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
*/
public Boolean ignoreHTTPSErrors;
/**
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects. This can be overwritten for each request
* individually.
*/
public Integer maxRedirects;
/**
* Network proxy settings.
*/
public Proxy proxy;
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()} or {@link APIRequestContext#storageState
* APIRequestContext.storageState()}. Either a path to the file with saved storage, or the value returned by one of {@link
* BrowserContext#storageState BrowserContext.storageState()} or {@link APIRequestContext#storageState
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()} or {@link
* com.microsoft.playwright.APIRequestContext#storageState APIRequestContext.storageState()}. Either a path to the file
* with saved storage, or the value returned by one of {@link com.microsoft.playwright.BrowserContext#storageState
* BrowserContext.storageState()} or {@link com.microsoft.playwright.APIRequestContext#storageState
* APIRequestContext.storageState()} methods.
*/
public String storageState;
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
* state.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}. Path to the
* file with saved storage state.
*/
public Path storageStatePath;
/**
@@ -83,9 +108,9 @@ public interface APIRequest {
public String userAgent;
/**
* Methods like {@link APIRequestContext#get APIRequestContext.get()} take the base URL into consideration by using the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* corresponding URL. Examples:
* Methods like {@link com.microsoft.playwright.APIRequestContext#get APIRequestContext.get()} take the base URL into
* consideration by using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a>
* constructor for building the corresponding URL. Examples:
* <ul>
* <li> baseURL: {@code http://localhost:3000} and sending request to {@code /bar.html} results in {@code
* http://localhost:3000/bar.html}</li>
@@ -99,6 +124,23 @@ public interface APIRequest {
this.baseURL = baseURL;
return this;
}
/**
* TLS Client Authentication allows the server to request a client certificate and verify it.
*
* <p> <strong>Details</strong>
*
* <p> An array of client certificates to be used. Each certificate object must have either both {@code certPath} and {@code
* keyPath}, a single {@code pfxPath}, or their corresponding direct value equivalents ({@code cert} and {@code key}, or
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
*
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
* replacing {@code localhost} with {@code local.playwright}.
*/
public NewContextOptions setClientCertificates(List<ClientCertificate> clientCertificates) {
this.clientCertificates = clientCertificates;
return this;
}
/**
* An object containing additional HTTP headers to be sent with every request. Defaults to none.
*/
@@ -106,6 +148,13 @@ public interface APIRequest {
this.extraHTTPHeaders = extraHTTPHeaders;
return this;
}
/**
* Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes.
*/
public NewContextOptions setFailOnStatusCode(boolean failOnStatusCode) {
this.failOnStatusCode = failOnStatusCode;
return this;
}
/**
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>. If
* no origin is specified, the username and password are sent to any servers upon unauthorized responses.
@@ -128,6 +177,15 @@ public interface APIRequest {
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
return this;
}
/**
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects. This can be overwritten for each request
* individually.
*/
public NewContextOptions setMaxRedirects(int maxRedirects) {
this.maxRedirects = maxRedirects;
return this;
}
/**
* Network proxy settings.
*/
@@ -143,9 +201,10 @@ public interface APIRequest {
}
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()} or {@link APIRequestContext#storageState
* APIRequestContext.storageState()}. Either a path to the file with saved storage, or the value returned by one of {@link
* BrowserContext#storageState BrowserContext.storageState()} or {@link APIRequestContext#storageState
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()} or {@link
* com.microsoft.playwright.APIRequestContext#storageState APIRequestContext.storageState()}. Either a path to the file
* with saved storage, or the value returned by one of {@link com.microsoft.playwright.BrowserContext#storageState
* BrowserContext.storageState()} or {@link com.microsoft.playwright.APIRequestContext#storageState
* APIRequestContext.storageState()} methods.
*/
public NewContextOptions setStorageState(String storageState) {
@@ -154,8 +213,8 @@ public interface APIRequest {
}
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
* state.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}. Path to the
* file with saved storage state.
*/
public NewContextOptions setStorageStatePath(Path storageStatePath) {
this.storageStatePath = storageStatePath;
@@ -24,30 +24,57 @@ import java.nio.file.Path;
* environment or the service to your e2e test.
*
* <p> Each Playwright browser context has associated with it {@code APIRequestContext} instance which shares cookie storage
* with the browser context and can be accessed via {@link BrowserContext#request BrowserContext.request()} or {@link
* Page#request Page.request()}. It is also possible to create a new APIRequestContext instance manually by calling {@link
* APIRequest#newContext APIRequest.newContext()}.
* with the browser context and can be accessed via {@link com.microsoft.playwright.BrowserContext#request
* BrowserContext.request()} or {@link com.microsoft.playwright.Page#request Page.request()}. It is also possible to create
* a new APIRequestContext instance manually by calling {@link com.microsoft.playwright.APIRequest#newContext
* APIRequest.newContext()}.
*
* <p> **Cookie management**
* <p> <strong>Cookie management</strong>
*
* <p> {@code APIRequestContext} returned by {@link BrowserContext#request BrowserContext.request()} and {@link Page#request
* Page.request()} shares cookie storage with the corresponding {@code BrowserContext}. Each API request will have {@code
* Cookie} header populated with the values from the browser context. If the API response contains {@code Set-Cookie}
* header it will automatically update {@code BrowserContext} cookies and requests made from the page will pick them up.
* This means that if you log in using this API, your e2e test will be logged in and vice versa.
* <p> {@code APIRequestContext} returned by {@link com.microsoft.playwright.BrowserContext#request BrowserContext.request()}
* and {@link com.microsoft.playwright.Page#request Page.request()} shares cookie storage with the corresponding {@code
* BrowserContext}. Each API request will have {@code Cookie} header populated with the values from the browser context. If
* the API response contains {@code Set-Cookie} header it will automatically update {@code BrowserContext} cookies and
* requests made from the page will pick them up. This means that if you log in using this API, your e2e test will be
* logged in and vice versa.
*
* <p> If you want API requests to not interfere with the browser cookies you should create a new {@code APIRequestContext} by
* calling {@link APIRequest#newContext APIRequest.newContext()}. Such {@code APIRequestContext} object will have its own
* isolated cookie storage.
* calling {@link com.microsoft.playwright.APIRequest#newContext APIRequest.newContext()}. Such {@code APIRequestContext}
* object will have its own isolated cookie storage.
*/
public interface APIRequestContext {
class DisposeOptions {
/**
* The reason to be reported to the operations interrupted by the context disposal.
*/
public String reason;
/**
* The reason to be reported to the operations interrupted by the context disposal.
*/
public DisposeOptions setReason(String reason) {
this.reason = reason;
return this;
}
}
class StorageStateOptions {
/**
* Set to {@code true} to include IndexedDB in the storage state snapshot.
*/
public Boolean indexedDB;
/**
* The file path to save the storage state to. If {@code path} is a relative path, then it is resolved relative to current
* working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.
*/
public Path path;
/**
* Set to {@code true} to include IndexedDB in the storage state snapshot.
*/
public StorageStateOptions setIndexedDB(boolean indexedDB) {
this.indexedDB = indexedDB;
return this;
}
/**
* The file path to save the storage state to. If {@code path} is a relative path, then it is resolved relative to current
* working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.
@@ -79,19 +106,32 @@ public interface APIRequestContext {
*/
APIResponse delete(String url, RequestOptions params);
/**
* All responses returned by {@link APIRequestContext#get APIRequestContext.get()} and similar methods are stored in the
* memory, so that you can later call {@link APIResponse#body APIResponse.body()}.This method discards all its resources,
* calling any method on disposed {@code APIRequestContext} will throw an exception.
* All responses returned by {@link com.microsoft.playwright.APIRequestContext#get APIRequestContext.get()} and similar
* methods are stored in the memory, so that you can later call {@link com.microsoft.playwright.APIResponse#body
* APIResponse.body()}.This method discards all its resources, calling any method on disposed {@code APIRequestContext}
* will throw an exception.
*
* @since v1.16
*/
void dispose();
default void dispose() {
dispose(null);
}
/**
* All responses returned by {@link com.microsoft.playwright.APIRequestContext#get APIRequestContext.get()} and similar
* methods are stored in the memory, so that you can later call {@link com.microsoft.playwright.APIResponse#body
* APIResponse.body()}.This method discards all its resources, calling any method on disposed {@code APIRequestContext}
* will throw an exception.
*
* @since v1.16
*/
void dispose(DisposeOptions options);
/**
* Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
* context cookies from the response. The method will automatically follow redirects. JSON objects can be passed directly
* to the request.
* context cookies from the response. The method will automatically follow redirects.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> JSON objects can be passed directly to the request:
* <pre>{@code
* Map<String, Object> data = new HashMap();
* data.put("title", "Book Title");
@@ -99,8 +139,8 @@ public interface APIRequestContext {
* request.fetch("https://example.com/api/createBook", RequestOptions.create().setMethod("post").setData(data));
* }</pre>
*
* <p> The common way to send file(s) in the body of a request is to encode it as form fields with {@code multipart/form-data}
* encoding. You can achieve that with Playwright API like this:
* <p> The common way to send file(s) in the body of a request is to upload them as form fields with {@code
* multipart/form-data} encoding, by specifiying the {@code multipart} parameter:
* <pre>{@code
* // Pass file path to the form data constructor:
* Path file = Paths.get("team.csv");
@@ -111,7 +151,7 @@ public interface APIRequestContext {
* // Or you can pass the file content directly as FilePayload object:
* FilePayload filePayload = new FilePayload("f.js", "text/javascript",
* "console.log(2022);".getBytes(StandardCharsets.UTF_8));
* APIResponse response = request.fetch("https://example.com/api/uploadTeamList",
* APIResponse response = request.fetch("https://example.com/api/uploadScript",
* RequestOptions.create().setMethod("post").setMultipart(
* FormData.create().set("fileField", filePayload)));
* }</pre>
@@ -124,10 +164,11 @@ public interface APIRequestContext {
}
/**
* Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
* context cookies from the response. The method will automatically follow redirects. JSON objects can be passed directly
* to the request.
* context cookies from the response. The method will automatically follow redirects.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> JSON objects can be passed directly to the request:
* <pre>{@code
* Map<String, Object> data = new HashMap();
* data.put("title", "Book Title");
@@ -135,8 +176,8 @@ public interface APIRequestContext {
* request.fetch("https://example.com/api/createBook", RequestOptions.create().setMethod("post").setData(data));
* }</pre>
*
* <p> The common way to send file(s) in the body of a request is to encode it as form fields with {@code multipart/form-data}
* encoding. You can achieve that with Playwright API like this:
* <p> The common way to send file(s) in the body of a request is to upload them as form fields with {@code
* multipart/form-data} encoding, by specifiying the {@code multipart} parameter:
* <pre>{@code
* // Pass file path to the form data constructor:
* Path file = Paths.get("team.csv");
@@ -147,7 +188,7 @@ public interface APIRequestContext {
* // Or you can pass the file content directly as FilePayload object:
* FilePayload filePayload = new FilePayload("f.js", "text/javascript",
* "console.log(2022);".getBytes(StandardCharsets.UTF_8));
* APIResponse response = request.fetch("https://example.com/api/uploadTeamList",
* APIResponse response = request.fetch("https://example.com/api/uploadScript",
* RequestOptions.create().setMethod("post").setMultipart(
* FormData.create().set("fileField", filePayload)));
* }</pre>
@@ -159,10 +200,11 @@ public interface APIRequestContext {
APIResponse fetch(String urlOrRequest, RequestOptions params);
/**
* Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
* context cookies from the response. The method will automatically follow redirects. JSON objects can be passed directly
* to the request.
* context cookies from the response. The method will automatically follow redirects.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> JSON objects can be passed directly to the request:
* <pre>{@code
* Map<String, Object> data = new HashMap();
* data.put("title", "Book Title");
@@ -170,8 +212,8 @@ public interface APIRequestContext {
* request.fetch("https://example.com/api/createBook", RequestOptions.create().setMethod("post").setData(data));
* }</pre>
*
* <p> The common way to send file(s) in the body of a request is to encode it as form fields with {@code multipart/form-data}
* encoding. You can achieve that with Playwright API like this:
* <p> The common way to send file(s) in the body of a request is to upload them as form fields with {@code
* multipart/form-data} encoding, by specifiying the {@code multipart} parameter:
* <pre>{@code
* // Pass file path to the form data constructor:
* Path file = Paths.get("team.csv");
@@ -182,7 +224,7 @@ public interface APIRequestContext {
* // Or you can pass the file content directly as FilePayload object:
* FilePayload filePayload = new FilePayload("f.js", "text/javascript",
* "console.log(2022);".getBytes(StandardCharsets.UTF_8));
* APIResponse response = request.fetch("https://example.com/api/uploadTeamList",
* APIResponse response = request.fetch("https://example.com/api/uploadScript",
* RequestOptions.create().setMethod("post").setMultipart(
* FormData.create().set("fileField", filePayload)));
* }</pre>
@@ -195,10 +237,11 @@ public interface APIRequestContext {
}
/**
* Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
* context cookies from the response. The method will automatically follow redirects. JSON objects can be passed directly
* to the request.
* context cookies from the response. The method will automatically follow redirects.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> JSON objects can be passed directly to the request:
* <pre>{@code
* Map<String, Object> data = new HashMap();
* data.put("title", "Book Title");
@@ -206,8 +249,8 @@ public interface APIRequestContext {
* request.fetch("https://example.com/api/createBook", RequestOptions.create().setMethod("post").setData(data));
* }</pre>
*
* <p> The common way to send file(s) in the body of a request is to encode it as form fields with {@code multipart/form-data}
* encoding. You can achieve that with Playwright API like this:
* <p> The common way to send file(s) in the body of a request is to upload them as form fields with {@code
* multipart/form-data} encoding, by specifiying the {@code multipart} parameter:
* <pre>{@code
* // Pass file path to the form data constructor:
* Path file = Paths.get("team.csv");
@@ -218,7 +261,7 @@ public interface APIRequestContext {
* // Or you can pass the file content directly as FilePayload object:
* FilePayload filePayload = new FilePayload("f.js", "text/javascript",
* "console.log(2022);".getBytes(StandardCharsets.UTF_8));
* APIResponse response = request.fetch("https://example.com/api/uploadTeamList",
* APIResponse response = request.fetch("https://example.com/api/uploadScript",
* RequestOptions.create().setMethod("post").setMultipart(
* FormData.create().set("fileField", filePayload)));
* }</pre>
@@ -233,7 +276,7 @@ public interface APIRequestContext {
* response. The method will populate request cookies from the context and update context cookies from the response. The
* method will automatically follow redirects.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Request parameters can be configured with {@code params} option, they will be serialized into the URL search parameters:
* <pre>{@code
@@ -253,7 +296,7 @@ public interface APIRequestContext {
* response. The method will populate request cookies from the context and update context cookies from the response. The
* method will automatically follow redirects.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Request parameters can be configured with {@code params} option, they will be serialized into the URL search parameters:
* <pre>{@code
@@ -314,7 +357,7 @@ public interface APIRequestContext {
* response. The method will populate request cookies from the context and update context cookies from the response. The
* method will automatically follow redirects.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> JSON objects can be passed directly to the request:
* <pre>{@code
@@ -334,7 +377,8 @@ public interface APIRequestContext {
* }</pre>
*
* <p> The common way to send file(s) in the body of a request is to upload them as form fields with {@code
* multipart/form-data} encoding. You can achieve that with Playwright API like this:
* multipart/form-data} encoding. Use {@code FormData} to construct request body and pass it to the request as {@code
* multipart} parameter:
* <pre>{@code
* // Pass file path to the form data constructor:
* Path file = Paths.get("team.csv");
@@ -343,9 +387,9 @@ public interface APIRequestContext {
* FormData.create().set("fileField", file)));
*
* // Or you can pass the file content directly as FilePayload object:
* FilePayload filePayload = new FilePayload("f.js", "text/javascript",
* FilePayload filePayload1 = new FilePayload("f1.js", "text/javascript",
* "console.log(2022);".getBytes(StandardCharsets.UTF_8));
* APIResponse response = request.post("https://example.com/api/uploadTeamList",
* APIResponse response = request.post("https://example.com/api/uploadScript",
* RequestOptions.create().setMultipart(
* FormData.create().set("fileField", filePayload)));
* }</pre>
@@ -361,7 +405,7 @@ public interface APIRequestContext {
* response. The method will populate request cookies from the context and update context cookies from the response. The
* method will automatically follow redirects.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> JSON objects can be passed directly to the request:
* <pre>{@code
@@ -381,7 +425,8 @@ public interface APIRequestContext {
* }</pre>
*
* <p> The common way to send file(s) in the body of a request is to upload them as form fields with {@code
* multipart/form-data} encoding. You can achieve that with Playwright API like this:
* multipart/form-data} encoding. Use {@code FormData} to construct request body and pass it to the request as {@code
* multipart} parameter:
* <pre>{@code
* // Pass file path to the form data constructor:
* Path file = Paths.get("team.csv");
@@ -390,9 +435,9 @@ public interface APIRequestContext {
* FormData.create().set("fileField", file)));
*
* // Or you can pass the file content directly as FilePayload object:
* FilePayload filePayload = new FilePayload("f.js", "text/javascript",
* FilePayload filePayload1 = new FilePayload("f1.js", "text/javascript",
* "console.log(2022);".getBytes(StandardCharsets.UTF_8));
* APIResponse response = request.post("https://example.com/api/uploadTeamList",
* APIResponse response = request.post("https://example.com/api/uploadScript",
* RequestOptions.create().setMultipart(
* FormData.create().set("fileField", filePayload)));
* }</pre>
@@ -20,8 +20,8 @@ import com.microsoft.playwright.options.*;
import java.util.*;
/**
* {@code APIResponse} class represents responses returned by {@link APIRequestContext#get APIRequestContext.get()} and
* similar methods.
* {@code APIResponse} class represents responses returned by {@link com.microsoft.playwright.APIRequestContext#get
* APIRequestContext.get()} and similar methods.
*/
public interface APIResponse {
/**
@@ -43,8 +43,8 @@ public interface APIResponse {
*/
Map<String, String> headers();
/**
* An array with all the request HTTP headers associated with this response. Header names are not lower-cased. Headers with
* multiple entries, such as {@code Set-Cookie}, appear in the array multiple times.
* An array with all the response HTTP headers associated with this response. Header names are not lower-cased. Headers
* with multiple entries, such as {@code Set-Cookie}, appear in the array multiple times.
*
* @since v1.16
*/
@@ -23,21 +23,21 @@ import java.util.function.Consumer;
import java.util.regex.Pattern;
/**
* A Browser is created via {@link BrowserType#launch BrowserType.launch()}. An example of using a {@code Browser} to
* create a {@code Page}:
* A Browser is created via {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}. An example of using a
* {@code Browser} to create a {@code Page}:
* <pre>{@code
* import com.microsoft.playwright.*;
*
* public class Example {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType firefox = playwright.firefox()
* Browser browser = firefox.launch();
* Page page = browser.newPage();
* page.navigate('https://example.com');
* browser.close();
* }
* }
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType firefox = playwright.firefox();
* Browser browser = firefox.launch();
* Page page = browser.newPage();
* page.navigate("https://example.com");
* browser.close();
* }
* }
* }
* }</pre>
*/
@@ -47,7 +47,7 @@ public interface Browser extends AutoCloseable {
* Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following:
* <ul>
* <li> Browser application is closed or crashed.</li>
* <li> The {@link Browser#close Browser.close()} method was called.</li>
* <li> The {@link com.microsoft.playwright.Browser#close Browser.close()} method was called.</li>
* </ul>
*/
void onDisconnected(Consumer<Browser> handler);
@@ -76,10 +76,11 @@ public interface Browser extends AutoCloseable {
*/
public Boolean acceptDownloads;
/**
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* When using {@link com.microsoft.playwright.Page#navigate Page.navigate()}, {@link com.microsoft.playwright.Page#route
* Page.route()}, {@link com.microsoft.playwright.Page#waitForURL Page.waitForURL()}, {@link
* com.microsoft.playwright.Page#waitForRequest Page.waitForRequest()}, or {@link
* com.microsoft.playwright.Page#waitForResponse Page.waitForResponse()} it takes the base URL in consideration by using
* the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* corresponding URL. Unset by default. Examples:
* <ul>
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code
@@ -96,11 +97,33 @@ public interface Browser extends AutoCloseable {
*/
public Boolean bypassCSP;
/**
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "light"}.
* TLS Client Authentication allows the server to request a client certificate and verify it.
*
* <p> <strong>Details</strong>
*
* <p> An array of client certificates to be used. Each certificate object must have either both {@code certPath} and {@code
* keyPath}, a single {@code pfxPath}, or their corresponding direct value equivalents ({@code cert} and {@code key}, or
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
*
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
* replacing {@code localhost} with {@code local.playwright}.
*/
public List<ClientCertificate> clientCertificates;
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
*/
public Optional<ColorScheme> colorScheme;
/**
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public Optional<Contrast> contrast;
/**
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
@@ -112,8 +135,8 @@ public interface Browser extends AutoCloseable {
public Map<String, String> extraHTTPHeaders;
/**
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link
* Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults.
* Defaults to {@code "none"}.
* com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation
* to system defaults. Defaults to {@code "none"}.
*/
public Optional<ForcedColors> forcedColors;
public Geolocation geolocation;
@@ -155,16 +178,13 @@ public interface Browser extends AutoCloseable {
*/
public Boolean offline;
/**
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
* BrowserContext.grantPermissions()} for more details. Defaults to none.
* A list of permissions to grant to all pages in this context. See {@link
* com.microsoft.playwright.BrowserContext#grantPermissions BrowserContext.grantPermissions()} for more details. Defaults
* to none.
*/
public List<String> permissions;
/**
* Network proxy settings to use with this context. Defaults to none.
*
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
* 'http://per-context' } })}.
*/
public Proxy proxy;
/**
@@ -185,14 +205,14 @@ public interface Browser extends AutoCloseable {
public Boolean recordHarOmitContent;
/**
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
* BrowserContext.close()} for the HAR to be saved.
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for the HAR to be saved.
*/
public Path recordHarPath;
public Object recordHarUrlFilter;
/**
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
* to call {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for videos to be saved.
*/
public Path recordVideoDir;
/**
@@ -203,8 +223,8 @@ public interface Browser extends AutoCloseable {
public RecordVideoSize recordVideoSize;
/**
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}.
* See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system
* defaults. Defaults to {@code "no-preference"}.
* See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public Optional<ReducedMotion> reducedMotion;
/**
@@ -223,13 +243,13 @@ public interface Browser extends AutoCloseable {
public ServiceWorkerPolicy serviceWorkers;
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}.
*/
public String storageState;
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
* state.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}. Path to the
* file with saved storage state.
*/
public Path storageStatePath;
/**
@@ -267,10 +287,11 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* When using {@link com.microsoft.playwright.Page#navigate Page.navigate()}, {@link com.microsoft.playwright.Page#route
* Page.route()}, {@link com.microsoft.playwright.Page#waitForURL Page.waitForURL()}, {@link
* com.microsoft.playwright.Page#waitForRequest Page.waitForRequest()}, or {@link
* com.microsoft.playwright.Page#waitForResponse Page.waitForResponse()} it takes the base URL in consideration by using
* the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* corresponding URL. Unset by default. Examples:
* <ul>
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code
@@ -293,14 +314,42 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "light"}.
* TLS Client Authentication allows the server to request a client certificate and verify it.
*
* <p> <strong>Details</strong>
*
* <p> An array of client certificates to be used. Each certificate object must have either both {@code certPath} and {@code
* keyPath}, a single {@code pfxPath}, or their corresponding direct value equivalents ({@code cert} and {@code key}, or
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
*
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
* replacing {@code localhost} with {@code local.playwright}.
*/
public NewContextOptions setClientCertificates(List<ClientCertificate> clientCertificates) {
this.clientCertificates = clientCertificates;
return this;
}
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
*/
public NewContextOptions setColorScheme(ColorScheme colorScheme) {
this.colorScheme = Optional.ofNullable(colorScheme);
return this;
}
/**
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public NewContextOptions setContrast(Contrast contrast) {
this.contrast = Optional.ofNullable(contrast);
return this;
}
/**
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
@@ -318,8 +367,8 @@ public interface Browser extends AutoCloseable {
}
/**
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link
* Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults.
* Defaults to {@code "none"}.
* com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation
* to system defaults. Defaults to {@code "none"}.
*/
public NewContextOptions setForcedColors(ForcedColors forcedColors) {
this.forcedColors = Optional.ofNullable(forcedColors);
@@ -398,8 +447,9 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
* BrowserContext.grantPermissions()} for more details. Defaults to none.
* A list of permissions to grant to all pages in this context. See {@link
* com.microsoft.playwright.BrowserContext#grantPermissions BrowserContext.grantPermissions()} for more details. Defaults
* to none.
*/
public NewContextOptions setPermissions(List<String> permissions) {
this.permissions = permissions;
@@ -407,20 +457,12 @@ public interface Browser extends AutoCloseable {
}
/**
* Network proxy settings to use with this context. Defaults to none.
*
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
* 'http://per-context' } })}.
*/
public NewContextOptions setProxy(String server) {
return setProxy(new Proxy(server));
}
/**
* Network proxy settings to use with this context. Defaults to none.
*
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
* 'http://per-context' } })}.
*/
public NewContextOptions setProxy(Proxy proxy) {
this.proxy = proxy;
@@ -453,8 +495,8 @@ public interface Browser extends AutoCloseable {
}
/**
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
* BrowserContext.close()} for the HAR to be saved.
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for the HAR to be saved.
*/
public NewContextOptions setRecordHarPath(Path recordHarPath) {
this.recordHarPath = recordHarPath;
@@ -470,7 +512,7 @@ public interface Browser extends AutoCloseable {
}
/**
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
* to call {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for videos to be saved.
*/
public NewContextOptions setRecordVideoDir(Path recordVideoDir) {
this.recordVideoDir = recordVideoDir;
@@ -495,8 +537,8 @@ public interface Browser extends AutoCloseable {
}
/**
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}.
* See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system
* defaults. Defaults to {@code "no-preference"}.
* See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public NewContextOptions setReducedMotion(ReducedMotion reducedMotion) {
this.reducedMotion = Optional.ofNullable(reducedMotion);
@@ -531,7 +573,7 @@ public interface Browser extends AutoCloseable {
}
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}.
*/
public NewContextOptions setStorageState(String storageState) {
this.storageState = storageState;
@@ -539,8 +581,8 @@ public interface Browser extends AutoCloseable {
}
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
* state.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}. Path to the
* file with saved storage state.
*/
public NewContextOptions setStorageStatePath(Path storageStatePath) {
this.storageStatePath = storageStatePath;
@@ -602,10 +644,11 @@ public interface Browser extends AutoCloseable {
*/
public Boolean acceptDownloads;
/**
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* When using {@link com.microsoft.playwright.Page#navigate Page.navigate()}, {@link com.microsoft.playwright.Page#route
* Page.route()}, {@link com.microsoft.playwright.Page#waitForURL Page.waitForURL()}, {@link
* com.microsoft.playwright.Page#waitForRequest Page.waitForRequest()}, or {@link
* com.microsoft.playwright.Page#waitForResponse Page.waitForResponse()} it takes the base URL in consideration by using
* the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* corresponding URL. Unset by default. Examples:
* <ul>
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code
@@ -622,11 +665,33 @@ public interface Browser extends AutoCloseable {
*/
public Boolean bypassCSP;
/**
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "light"}.
* TLS Client Authentication allows the server to request a client certificate and verify it.
*
* <p> <strong>Details</strong>
*
* <p> An array of client certificates to be used. Each certificate object must have either both {@code certPath} and {@code
* keyPath}, a single {@code pfxPath}, or their corresponding direct value equivalents ({@code cert} and {@code key}, or
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
*
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
* replacing {@code localhost} with {@code local.playwright}.
*/
public List<ClientCertificate> clientCertificates;
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
*/
public Optional<ColorScheme> colorScheme;
/**
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public Optional<Contrast> contrast;
/**
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
@@ -638,8 +703,8 @@ public interface Browser extends AutoCloseable {
public Map<String, String> extraHTTPHeaders;
/**
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link
* Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults.
* Defaults to {@code "none"}.
* com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation
* to system defaults. Defaults to {@code "none"}.
*/
public Optional<ForcedColors> forcedColors;
public Geolocation geolocation;
@@ -681,16 +746,13 @@ public interface Browser extends AutoCloseable {
*/
public Boolean offline;
/**
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
* BrowserContext.grantPermissions()} for more details. Defaults to none.
* A list of permissions to grant to all pages in this context. See {@link
* com.microsoft.playwright.BrowserContext#grantPermissions BrowserContext.grantPermissions()} for more details. Defaults
* to none.
*/
public List<String> permissions;
/**
* Network proxy settings to use with this context. Defaults to none.
*
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
* 'http://per-context' } })}.
*/
public Proxy proxy;
/**
@@ -711,14 +773,14 @@ public interface Browser extends AutoCloseable {
public Boolean recordHarOmitContent;
/**
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
* BrowserContext.close()} for the HAR to be saved.
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for the HAR to be saved.
*/
public Path recordHarPath;
public Object recordHarUrlFilter;
/**
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
* to call {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for videos to be saved.
*/
public Path recordVideoDir;
/**
@@ -729,8 +791,8 @@ public interface Browser extends AutoCloseable {
public RecordVideoSize recordVideoSize;
/**
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}.
* See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system
* defaults. Defaults to {@code "no-preference"}.
* See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public Optional<ReducedMotion> reducedMotion;
/**
@@ -749,13 +811,13 @@ public interface Browser extends AutoCloseable {
public ServiceWorkerPolicy serviceWorkers;
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}.
*/
public String storageState;
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
* state.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}. Path to the
* file with saved storage state.
*/
public Path storageStatePath;
/**
@@ -793,10 +855,11 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* When using {@link com.microsoft.playwright.Page#navigate Page.navigate()}, {@link com.microsoft.playwright.Page#route
* Page.route()}, {@link com.microsoft.playwright.Page#waitForURL Page.waitForURL()}, {@link
* com.microsoft.playwright.Page#waitForRequest Page.waitForRequest()}, or {@link
* com.microsoft.playwright.Page#waitForResponse Page.waitForResponse()} it takes the base URL in consideration by using
* the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* corresponding URL. Unset by default. Examples:
* <ul>
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code
@@ -819,14 +882,42 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "light"}.
* TLS Client Authentication allows the server to request a client certificate and verify it.
*
* <p> <strong>Details</strong>
*
* <p> An array of client certificates to be used. Each certificate object must have either both {@code certPath} and {@code
* keyPath}, a single {@code pfxPath}, or their corresponding direct value equivalents ({@code cert} and {@code key}, or
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
*
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
* replacing {@code localhost} with {@code local.playwright}.
*/
public NewPageOptions setClientCertificates(List<ClientCertificate> clientCertificates) {
this.clientCertificates = clientCertificates;
return this;
}
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
*/
public NewPageOptions setColorScheme(ColorScheme colorScheme) {
this.colorScheme = Optional.ofNullable(colorScheme);
return this;
}
/**
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public NewPageOptions setContrast(Contrast contrast) {
this.contrast = Optional.ofNullable(contrast);
return this;
}
/**
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
@@ -844,8 +935,8 @@ public interface Browser extends AutoCloseable {
}
/**
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link
* Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults.
* Defaults to {@code "none"}.
* com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation
* to system defaults. Defaults to {@code "none"}.
*/
public NewPageOptions setForcedColors(ForcedColors forcedColors) {
this.forcedColors = Optional.ofNullable(forcedColors);
@@ -924,8 +1015,9 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
* BrowserContext.grantPermissions()} for more details. Defaults to none.
* A list of permissions to grant to all pages in this context. See {@link
* com.microsoft.playwright.BrowserContext#grantPermissions BrowserContext.grantPermissions()} for more details. Defaults
* to none.
*/
public NewPageOptions setPermissions(List<String> permissions) {
this.permissions = permissions;
@@ -933,20 +1025,12 @@ public interface Browser extends AutoCloseable {
}
/**
* Network proxy settings to use with this context. Defaults to none.
*
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
* 'http://per-context' } })}.
*/
public NewPageOptions setProxy(String server) {
return setProxy(new Proxy(server));
}
/**
* Network proxy settings to use with this context. Defaults to none.
*
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
* 'http://per-context' } })}.
*/
public NewPageOptions setProxy(Proxy proxy) {
this.proxy = proxy;
@@ -979,8 +1063,8 @@ public interface Browser extends AutoCloseable {
}
/**
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
* BrowserContext.close()} for the HAR to be saved.
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for the HAR to be saved.
*/
public NewPageOptions setRecordHarPath(Path recordHarPath) {
this.recordHarPath = recordHarPath;
@@ -996,7 +1080,7 @@ public interface Browser extends AutoCloseable {
}
/**
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
* to call {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for videos to be saved.
*/
public NewPageOptions setRecordVideoDir(Path recordVideoDir) {
this.recordVideoDir = recordVideoDir;
@@ -1021,8 +1105,8 @@ public interface Browser extends AutoCloseable {
}
/**
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}.
* See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system
* defaults. Defaults to {@code "no-preference"}.
* See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public NewPageOptions setReducedMotion(ReducedMotion reducedMotion) {
this.reducedMotion = Optional.ofNullable(reducedMotion);
@@ -1057,7 +1141,7 @@ public interface Browser extends AutoCloseable {
}
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}.
*/
public NewPageOptions setStorageState(String storageState) {
this.storageState = storageState;
@@ -1065,8 +1149,8 @@ public interface Browser extends AutoCloseable {
}
/**
* Populates context with given storage state. This option can be used to initialize context with logged-in information
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
* state.
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}. Path to the
* file with saved storage state.
*/
public NewPageOptions setStorageStatePath(Path storageStatePath) {
this.storageStatePath = storageStatePath;
@@ -1165,15 +1249,16 @@ public interface Browser extends AutoCloseable {
*/
BrowserType browserType();
/**
* In case this browser is obtained using {@link BrowserType#launch BrowserType.launch()}, closes the browser and all of
* its pages (if any were opened).
* In case this browser is obtained using {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}, closes
* the browser and all of its pages (if any were opened).
*
* <p> In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from the
* browser server.
*
* <p> <strong>NOTE:</strong> This is similar to force quitting the browser. Therefore, you should call {@link BrowserContext#close
* BrowserContext.close()} on any {@code BrowserContext}'s you explicitly created earlier with {@link Browser#newContext
* Browser.newContext()} **before** calling {@link Browser#close Browser.close()}.
* <p> <strong>NOTE:</strong> This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close events, call
* {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext} instances you
* explicitly created earlier using {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before**
* calling {@link com.microsoft.playwright.Browser#close Browser.close()}.
*
* <p> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
*
@@ -1183,15 +1268,16 @@ public interface Browser extends AutoCloseable {
close(null);
}
/**
* In case this browser is obtained using {@link BrowserType#launch BrowserType.launch()}, closes the browser and all of
* its pages (if any were opened).
* In case this browser is obtained using {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}, closes
* the browser and all of its pages (if any were opened).
*
* <p> In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from the
* browser server.
*
* <p> <strong>NOTE:</strong> This is similar to force quitting the browser. Therefore, you should call {@link BrowserContext#close
* BrowserContext.close()} on any {@code BrowserContext}'s you explicitly created earlier with {@link Browser#newContext
* Browser.newContext()} **before** calling {@link Browser#close Browser.close()}.
* <p> <strong>NOTE:</strong> This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close events, call
* {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext} instances you
* explicitly created earlier using {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before**
* calling {@link com.microsoft.playwright.Browser#close Browser.close()}.
*
* <p> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
*
@@ -1201,7 +1287,7 @@ public interface Browser extends AutoCloseable {
/**
* Returns an array of all open browser contexts. In a newly created browser, this will return zero browser contexts.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* Browser browser = pw.webkit().launch();
* System.out.println(browser.contexts().size()); // prints "0"
@@ -1230,18 +1316,18 @@ public interface Browser extends AutoCloseable {
* Creates a new browser context. It won't share cookies/cache with other browser contexts.
*
* <p> <strong>NOTE:</strong> If directly using this method to create {@code BrowserContext}s, it is best practice to explicitly close the returned
* context via {@link BrowserContext#close BrowserContext.close()} when your code is done with the {@code BrowserContext},
* and before calling {@link Browser#close Browser.close()}. This will ensure the {@code context} is closed gracefully and
* any artifacts—like HARs and videos—are fully flushed and saved.
* context via {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} when your code is done with the
* {@code BrowserContext}, and before calling {@link com.microsoft.playwright.Browser#close Browser.close()}. This will
* ensure the {@code context} is closed gracefully and any artifacts—like HARs and videos—are fully flushed and saved.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* Browser browser = playwright.firefox().launch(); // Or 'chromium' or 'webkit'.
* // Create a new incognito browser context.
* BrowserContext context = browser.newContext();
* // Create a new page in a pristine context.
* Page page = context.newPage();
* page.navigate('https://example.com');
* page.navigate("https://example.com");
*
* // Graceful close up everything
* context.close();
@@ -1257,18 +1343,18 @@ public interface Browser extends AutoCloseable {
* Creates a new browser context. It won't share cookies/cache with other browser contexts.
*
* <p> <strong>NOTE:</strong> If directly using this method to create {@code BrowserContext}s, it is best practice to explicitly close the returned
* context via {@link BrowserContext#close BrowserContext.close()} when your code is done with the {@code BrowserContext},
* and before calling {@link Browser#close Browser.close()}. This will ensure the {@code context} is closed gracefully and
* any artifacts—like HARs and videos—are fully flushed and saved.
* context via {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} when your code is done with the
* {@code BrowserContext}, and before calling {@link com.microsoft.playwright.Browser#close Browser.close()}. This will
* ensure the {@code context} is closed gracefully and any artifacts—like HARs and videos—are fully flushed and saved.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* Browser browser = playwright.firefox().launch(); // Or 'chromium' or 'webkit'.
* // Create a new incognito browser context.
* BrowserContext context = browser.newContext();
* // Create a new page in a pristine context.
* Page page = context.newPage();
* page.navigate('https://example.com');
* page.navigate("https://example.com");
*
* // Graceful close up everything
* context.close();
@@ -1282,8 +1368,9 @@ public interface Browser extends AutoCloseable {
* Creates a new page in a new browser context. Closing this page will close the context as well.
*
* <p> This is a convenience API that should only be used for the single-page scenarios and short snippets. Production code and
* testing frameworks should explicitly create {@link Browser#newContext Browser.newContext()} followed by the {@link
* BrowserContext#newPage BrowserContext.newPage()} to control their exact life times.
* testing frameworks should explicitly create {@link com.microsoft.playwright.Browser#newContext Browser.newContext()}
* followed by the {@link com.microsoft.playwright.BrowserContext#newPage BrowserContext.newPage()} to control their exact
* life times.
*
* @since v1.8
*/
@@ -1294,8 +1381,9 @@ public interface Browser extends AutoCloseable {
* Creates a new page in a new browser context. Closing this page will close the context as well.
*
* <p> This is a convenience API that should only be used for the single-page scenarios and short snippets. Production code and
* testing frameworks should explicitly create {@link Browser#newContext Browser.newContext()} followed by the {@link
* BrowserContext#newPage BrowserContext.newPage()} to control their exact life times.
* testing frameworks should explicitly create {@link com.microsoft.playwright.Browser#newContext Browser.newContext()}
* followed by the {@link com.microsoft.playwright.BrowserContext#newPage BrowserContext.newPage()} to control their exact
* life times.
*
* @since v1.8
*/
@@ -1306,14 +1394,15 @@ public interface Browser extends AutoCloseable {
* href="https://playwright.dev/java/docs/trace-viewer">Playwright Tracing</a> could be found <a
* href="https://playwright.dev/java/docs/api/class-tracing">here</a>.
*
* <p> You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to
* create a trace file that can be opened in Chrome DevTools performance panel.
* <p> You can use {@link com.microsoft.playwright.Browser#startTracing Browser.startTracing()} and {@link
* com.microsoft.playwright.Browser#stopTracing Browser.stopTracing()} to create a trace file that can be opened in Chrome
* DevTools performance panel.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* browser.startTracing(page, new Browser.StartTracingOptions()
* .setPath(Paths.get("trace.json")));
* page.goto('https://www.google.com');
* page.navigate("https://www.google.com");
* browser.stopTracing();
* }</pre>
*
@@ -1329,14 +1418,15 @@ public interface Browser extends AutoCloseable {
* href="https://playwright.dev/java/docs/trace-viewer">Playwright Tracing</a> could be found <a
* href="https://playwright.dev/java/docs/api/class-tracing">here</a>.
*
* <p> You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to
* create a trace file that can be opened in Chrome DevTools performance panel.
* <p> You can use {@link com.microsoft.playwright.Browser#startTracing Browser.startTracing()} and {@link
* com.microsoft.playwright.Browser#stopTracing Browser.stopTracing()} to create a trace file that can be opened in Chrome
* DevTools performance panel.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* browser.startTracing(page, new Browser.StartTracingOptions()
* .setPath(Paths.get("trace.json")));
* page.goto('https://www.google.com');
* page.navigate("https://www.google.com");
* browser.stopTracing();
* }</pre>
*
@@ -1351,14 +1441,15 @@ public interface Browser extends AutoCloseable {
* href="https://playwright.dev/java/docs/trace-viewer">Playwright Tracing</a> could be found <a
* href="https://playwright.dev/java/docs/api/class-tracing">here</a>.
*
* <p> You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to
* create a trace file that can be opened in Chrome DevTools performance panel.
* <p> You can use {@link com.microsoft.playwright.Browser#startTracing Browser.startTracing()} and {@link
* com.microsoft.playwright.Browser#stopTracing Browser.stopTracing()} to create a trace file that can be opened in Chrome
* DevTools performance panel.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* browser.startTracing(page, new Browser.StartTracingOptions()
* .setPath(Paths.get("trace.json")));
* page.goto('https://www.google.com');
* page.navigate("https://www.google.com");
* browser.stopTracing();
* }</pre>
*
File diff suppressed because it is too large Load Diff
@@ -165,14 +165,21 @@ public interface BrowserType {
}
class LaunchOptions {
/**
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
* <strong>NOTE:</strong> Use custom browser args at your own risk, as some of them may break Playwright functionality.
*
* <p> Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
* href="https://peter.sh/experiments/chromium-command-line-switches/">here</a>.
*/
public List<String> args;
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public Object channel;
/**
@@ -180,8 +187,7 @@ public interface BrowserType {
*/
public Boolean chromiumSandbox;
/**
* **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is {@code true}, the {@code
* headless} option will be set {@code false}.
* @deprecated Use <a href="https://playwright.dev/java/docs/debug">debugging tools</a> instead.
*/
public Boolean devtools;
/**
@@ -220,8 +226,8 @@ public interface BrowserType {
/**
* Whether to run browser in headless mode. More details for <a
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
* unless the {@code devtools} option is {@code true}.
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true} unless
* the {@code devtools} option is {@code true}.
*/
public Boolean headless;
/**
@@ -253,8 +259,10 @@ public interface BrowserType {
public Path tracesDir;
/**
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
* <strong>NOTE:</strong> Use custom browser args at your own risk, as some of them may break Playwright functionality.
*
* <p> Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
* href="https://peter.sh/experiments/chromium-command-line-switches/">here</a>.
*/
public LaunchOptions setArgs(List<String> args) {
this.args = args;
@@ -262,18 +270,28 @@ public interface BrowserType {
}
@Deprecated
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public LaunchOptions setChannel(BrowserChannel channel) {
this.channel = channel;
return this;
}
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public LaunchOptions setChannel(String channel) {
this.channel = channel;
@@ -287,8 +305,7 @@ public interface BrowserType {
return this;
}
/**
* **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is {@code true}, the {@code
* headless} option will be set {@code false}.
* @deprecated Use <a href="https://playwright.dev/java/docs/debug">debugging tools</a> instead.
*/
public LaunchOptions setDevtools(boolean devtools) {
this.devtools = devtools;
@@ -351,8 +368,8 @@ public interface BrowserType {
/**
* Whether to run browser in headless mode. More details for <a
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
* unless the {@code devtools} option is {@code true}.
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true} unless
* the {@code devtools} option is {@code true}.
*/
public LaunchOptions setHeadless(boolean headless) {
this.headless = headless;
@@ -416,15 +433,18 @@ public interface BrowserType {
*/
public Boolean acceptDownloads;
/**
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
* <strong>NOTE:</strong> Use custom browser args at your own risk, as some of them may break Playwright functionality.
*
* <p> Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
* href="https://peter.sh/experiments/chromium-command-line-switches/">here</a>.
*/
public List<String> args;
/**
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* When using {@link com.microsoft.playwright.Page#navigate Page.navigate()}, {@link com.microsoft.playwright.Page#route
* Page.route()}, {@link com.microsoft.playwright.Page#waitForURL Page.waitForURL()}, {@link
* com.microsoft.playwright.Page#waitForRequest Page.waitForRequest()}, or {@link
* com.microsoft.playwright.Page#waitForResponse Page.waitForResponse()} it takes the base URL in consideration by using
* the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* corresponding URL. Unset by default. Examples:
* <ul>
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code
@@ -441,9 +461,14 @@ public interface BrowserType {
*/
public Boolean bypassCSP;
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public Object channel;
/**
@@ -451,19 +476,40 @@ public interface BrowserType {
*/
public Boolean chromiumSandbox;
/**
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "light"}.
* TLS Client Authentication allows the server to request a client certificate and verify it.
*
* <p> <strong>Details</strong>
*
* <p> An array of client certificates to be used. Each certificate object must have either both {@code certPath} and {@code
* keyPath}, a single {@code pfxPath}, or their corresponding direct value equivalents ({@code cert} and {@code key}, or
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
*
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
* replacing {@code localhost} with {@code local.playwright}.
*/
public List<ClientCertificate> clientCertificates;
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
*/
public Optional<ColorScheme> colorScheme;
/**
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public Optional<Contrast> contrast;
/**
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
*/
public Double deviceScaleFactor;
/**
* **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is {@code true}, the {@code
* headless} option will be set {@code false}.
* @deprecated Use <a href="https://playwright.dev/java/docs/debug">debugging tools</a> instead.
*/
public Boolean devtools;
/**
@@ -493,8 +539,8 @@ public interface BrowserType {
public Map<String, Object> firefoxUserPrefs;
/**
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link
* Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults.
* Defaults to {@code "none"}.
* com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation
* to system defaults. Defaults to {@code "none"}.
*/
public Optional<ForcedColors> forcedColors;
public Geolocation geolocation;
@@ -518,8 +564,8 @@ public interface BrowserType {
/**
* Whether to run browser in headless mode. More details for <a
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
* unless the {@code devtools} option is {@code true}.
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true} unless
* the {@code devtools} option is {@code true}.
*/
public Boolean headless;
/**
@@ -565,8 +611,9 @@ public interface BrowserType {
*/
public Boolean offline;
/**
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
* BrowserContext.grantPermissions()} for more details. Defaults to none.
* A list of permissions to grant to all pages in this context. See {@link
* com.microsoft.playwright.BrowserContext#grantPermissions BrowserContext.grantPermissions()} for more details. Defaults
* to none.
*/
public List<String> permissions;
/**
@@ -591,14 +638,14 @@ public interface BrowserType {
public Boolean recordHarOmitContent;
/**
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
* BrowserContext.close()} for the HAR to be saved.
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for the HAR to be saved.
*/
public Path recordHarPath;
public Object recordHarUrlFilter;
/**
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
* to call {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for videos to be saved.
*/
public Path recordVideoDir;
/**
@@ -609,8 +656,8 @@ public interface BrowserType {
public RecordVideoSize recordVideoSize;
/**
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}.
* See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system
* defaults. Defaults to {@code "no-preference"}.
* See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public Optional<ReducedMotion> reducedMotion;
/**
@@ -675,18 +722,21 @@ public interface BrowserType {
return this;
}
/**
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
* <strong>NOTE:</strong> Use custom browser args at your own risk, as some of them may break Playwright functionality.
*
* <p> Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
* href="https://peter.sh/experiments/chromium-command-line-switches/">here</a>.
*/
public LaunchPersistentContextOptions setArgs(List<String> args) {
this.args = args;
return this;
}
/**
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* When using {@link com.microsoft.playwright.Page#navigate Page.navigate()}, {@link com.microsoft.playwright.Page#route
* Page.route()}, {@link com.microsoft.playwright.Page#waitForURL Page.waitForURL()}, {@link
* com.microsoft.playwright.Page#waitForRequest Page.waitForRequest()}, or {@link
* com.microsoft.playwright.Page#waitForResponse Page.waitForResponse()} it takes the base URL in consideration by using
* the <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the
* corresponding URL. Unset by default. Examples:
* <ul>
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code
@@ -710,18 +760,28 @@ public interface BrowserType {
}
@Deprecated
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public LaunchPersistentContextOptions setChannel(BrowserChannel channel) {
this.channel = channel;
return this;
}
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public LaunchPersistentContextOptions setChannel(String channel) {
this.channel = channel;
@@ -735,14 +795,42 @@ public interface BrowserType {
return this;
}
/**
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "light"}.
* TLS Client Authentication allows the server to request a client certificate and verify it.
*
* <p> <strong>Details</strong>
*
* <p> An array of client certificates to be used. Each certificate object must have either both {@code certPath} and {@code
* keyPath}, a single {@code pfxPath}, or their corresponding direct value equivalents ({@code cert} and {@code key}, or
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
*
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
* replacing {@code localhost} with {@code local.playwright}.
*/
public LaunchPersistentContextOptions setClientCertificates(List<ClientCertificate> clientCertificates) {
this.clientCertificates = clientCertificates;
return this;
}
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
*/
public LaunchPersistentContextOptions setColorScheme(ColorScheme colorScheme) {
this.colorScheme = Optional.ofNullable(colorScheme);
return this;
}
/**
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public LaunchPersistentContextOptions setContrast(Contrast contrast) {
this.contrast = Optional.ofNullable(contrast);
return this;
}
/**
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
@@ -752,8 +840,7 @@ public interface BrowserType {
return this;
}
/**
* **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is {@code true}, the {@code
* headless} option will be set {@code false}.
* @deprecated Use <a href="https://playwright.dev/java/docs/debug">debugging tools</a> instead.
*/
public LaunchPersistentContextOptions setDevtools(boolean devtools) {
this.devtools = devtools;
@@ -801,8 +888,8 @@ public interface BrowserType {
}
/**
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link
* Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults.
* Defaults to {@code "none"}.
* com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation
* to system defaults. Defaults to {@code "none"}.
*/
public LaunchPersistentContextOptions setForcedColors(ForcedColors forcedColors) {
this.forcedColors = Optional.ofNullable(forcedColors);
@@ -847,8 +934,8 @@ public interface BrowserType {
/**
* Whether to run browser in headless mode. More details for <a
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
* unless the {@code devtools} option is {@code true}.
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true} unless
* the {@code devtools} option is {@code true}.
*/
public LaunchPersistentContextOptions setHeadless(boolean headless) {
this.headless = headless;
@@ -928,8 +1015,9 @@ public interface BrowserType {
return this;
}
/**
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
* BrowserContext.grantPermissions()} for more details. Defaults to none.
* A list of permissions to grant to all pages in this context. See {@link
* com.microsoft.playwright.BrowserContext#grantPermissions BrowserContext.grantPermissions()} for more details. Defaults
* to none.
*/
public LaunchPersistentContextOptions setPermissions(List<String> permissions) {
this.permissions = permissions;
@@ -975,8 +1063,8 @@ public interface BrowserType {
}
/**
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
* BrowserContext.close()} for the HAR to be saved.
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for the HAR to be saved.
*/
public LaunchPersistentContextOptions setRecordHarPath(Path recordHarPath) {
this.recordHarPath = recordHarPath;
@@ -992,7 +1080,7 @@ public interface BrowserType {
}
/**
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
* to call {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} for videos to be saved.
*/
public LaunchPersistentContextOptions setRecordVideoDir(Path recordVideoDir) {
this.recordVideoDir = recordVideoDir;
@@ -1017,8 +1105,8 @@ public interface BrowserType {
}
/**
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}.
* See {@link Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system
* defaults. Defaults to {@code "no-preference"}.
* See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
* emulation to system defaults. Defaults to {@code "no-preference"}.
*/
public LaunchPersistentContextOptions setReducedMotion(ReducedMotion reducedMotion) {
this.reducedMotion = Optional.ofNullable(reducedMotion);
@@ -1124,33 +1212,40 @@ public interface BrowserType {
}
}
/**
* This method attaches Playwright to an existing browser instance. When connecting to another browser launched via {@code
* BrowserType.launchServer} in Node.js, the major and minor version needs to match the client version (1.2.3 → is
* compatible with 1.2.x).
* This method attaches Playwright to an existing browser instance created via {@code BrowserType.launchServer} in Node.js.
*
* @param wsEndpoint A browser websocket endpoint to connect to.
* <p> <strong>NOTE:</strong> The major and minor version of the Playwright instance that connects needs to match the version of Playwright that
* launches the browser (1.2.3 → is compatible with 1.2.x).
*
* @param wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via {@code BrowserServer.wsEndpoint}.
* @since v1.8
*/
default Browser connect(String wsEndpoint) {
return connect(wsEndpoint, null);
}
/**
* This method attaches Playwright to an existing browser instance. When connecting to another browser launched via {@code
* BrowserType.launchServer} in Node.js, the major and minor version needs to match the client version (1.2.3 → is
* compatible with 1.2.x).
* This method attaches Playwright to an existing browser instance created via {@code BrowserType.launchServer} in Node.js.
*
* @param wsEndpoint A browser websocket endpoint to connect to.
* <p> <strong>NOTE:</strong> The major and minor version of the Playwright instance that connects needs to match the version of Playwright that
* launches the browser (1.2.3 → is compatible with 1.2.x).
*
* @param wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via {@code BrowserServer.wsEndpoint}.
* @since v1.8
*/
Browser connect(String wsEndpoint, ConnectOptions options);
/**
* This method attaches Playwright to an existing browser instance using the Chrome DevTools Protocol.
*
* <p> The default browser context is accessible via {@link Browser#contexts Browser.contexts()}.
* <p> The default browser context is accessible via {@link com.microsoft.playwright.Browser#contexts Browser.contexts()}.
*
* <p> <strong>NOTE:</strong> Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers.
*
* <p> **Usage**
* <p> <strong>NOTE:</strong> This connection is significantly lower fidelity than the Playwright protocol connection via {@link
* com.microsoft.playwright.BrowserType#connect BrowserType.connect()}. If you are experiencing issues or attempting to use
* advanced functionality, you probably want to use {@link com.microsoft.playwright.BrowserType#connect
* BrowserType.connect()}.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* Browser browser = playwright.chromium().connectOverCDP("http://localhost:9222");
* BrowserContext defaultContext = browser.contexts().get(0);
@@ -1167,11 +1262,16 @@ public interface BrowserType {
/**
* This method attaches Playwright to an existing browser instance using the Chrome DevTools Protocol.
*
* <p> The default browser context is accessible via {@link Browser#contexts Browser.contexts()}.
* <p> The default browser context is accessible via {@link com.microsoft.playwright.Browser#contexts Browser.contexts()}.
*
* <p> <strong>NOTE:</strong> Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers.
*
* <p> **Usage**
* <p> <strong>NOTE:</strong> This connection is significantly lower fidelity than the Playwright protocol connection via {@link
* com.microsoft.playwright.BrowserType#connect BrowserType.connect()}. If you are experiencing issues or attempting to use
* advanced functionality, you probably want to use {@link com.microsoft.playwright.BrowserType#connect
* BrowserType.connect()}.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* Browser browser = playwright.chromium().connectOverCDP("http://localhost:9222");
* BrowserContext defaultContext = browser.contexts().get(0);
@@ -1192,7 +1292,7 @@ public interface BrowserType {
/**
* Returns the browser instance.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> You can use {@code ignoreDefaultArgs} to filter out {@code --mute-audio} from default arguments:
* <pre>{@code
@@ -1228,7 +1328,7 @@ public interface BrowserType {
/**
* Returns the browser instance.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> You can use {@code ignoreDefaultArgs} to filter out {@code --mute-audio} from default arguments:
* <pre>{@code
@@ -1265,11 +1365,15 @@ public interface BrowserType {
* <p> Launches browser that uses persistent storage located at {@code userDataDir} and returns the only context. Closing this
* context will automatically close the browser.
*
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty string to
* create a temporary directory.
*
* <p> More details for <a
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}. Pass
* an empty string to use a temporary directory instead.
* href="https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile">Firefox</a>. Chromium's user data directory is
* the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
*
* <p> Note that browsers do not allow launching multiple instances with the same User Data Directory.
* @since v1.8
*/
default BrowserContext launchPersistentContext(Path userDataDir) {
@@ -1281,11 +1385,15 @@ public interface BrowserType {
* <p> Launches browser that uses persistent storage located at {@code userDataDir} and returns the only context. Closing this
* context will automatically close the browser.
*
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty string to
* create a temporary directory.
*
* <p> More details for <a
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}. Pass
* an empty string to use a temporary directory instead.
* href="https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile">Firefox</a>. Chromium's user data directory is
* the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
*
* <p> Note that browsers do not allow launching multiple instances with the same User Data Directory.
* @since v1.8
*/
BrowserContext launchPersistentContext(Path userDataDir, LaunchPersistentContextOptions options);
@@ -31,6 +31,7 @@ import com.google.gson.JsonObject;
* <li> Documentation on DevTools Protocol can be found here: <a
* href="https://chromedevtools.github.io/devtools-protocol/">DevTools Protocol Viewer</a>.</li>
* <li> Getting Started with DevTools Protocol: https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md</li>
* </ul>
* <pre>{@code
* CDPSession client = page.context().newCDPSession(page);
* client.send("Runtime.enable");
@@ -45,7 +46,6 @@ import com.google.gson.JsonObject;
* params.addProperty("playbackRate", playbackRate / 2);
* client.send("Animation.setPlaybackRate", params);
* }</pre>
* </ul>
*/
public interface CDPSession {
/**
@@ -0,0 +1,366 @@
/*
* 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 java.util.Date;
/**
* Accurately simulating time-dependent behavior is essential for verifying the correctness of applications. Learn more
* about <a href="https://playwright.dev/java/docs/clock">clock emulation</a>.
*
* <p> Note that clock is installed for the entire {@code BrowserContext}, so the time in all the pages and iframes is
* controlled by the same clock.
*/
public interface Clock {
class InstallOptions {
/**
* Time to initialize with, current system time by default.
*/
public Object time;
/**
* Time to initialize with, current system time by default.
*/
public InstallOptions setTime(long time) {
this.time = time;
return this;
}
/**
* Time to initialize with, current system time by default.
*/
public InstallOptions setTime(String time) {
this.time = time;
return this;
}
/**
* Time to initialize with, current system time by default.
*/
public InstallOptions setTime(Date time) {
this.time = time;
return this;
}
}
/**
* Advance the clock by jumping forward in time. Only fires due timers at most once. This is equivalent to user closing the
* laptop lid for a while and reopening it later, after given time.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().fastForward(1000);
* page.clock().fastForward("30:00");
* }</pre>
*
* @param ticks Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are "08"
* for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds.
* @since v1.45
*/
void fastForward(long ticks);
/**
* Advance the clock by jumping forward in time. Only fires due timers at most once. This is equivalent to user closing the
* laptop lid for a while and reopening it later, after given time.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().fastForward(1000);
* page.clock().fastForward("30:00");
* }</pre>
*
* @param ticks Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are "08"
* for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds.
* @since v1.45
*/
void fastForward(String ticks);
/**
* Install fake implementations for the following time-related functions:
* <ul>
* <li> {@code Date}</li>
* <li> {@code setTimeout}</li>
* <li> {@code clearTimeout}</li>
* <li> {@code setInterval}</li>
* <li> {@code clearInterval}</li>
* <li> {@code requestAnimationFrame}</li>
* <li> {@code cancelAnimationFrame}</li>
* <li> {@code requestIdleCallback}</li>
* <li> {@code cancelIdleCallback}</li>
* <li> {@code performance}</li>
* </ul>
*
* <p> Fake timers are used to manually control the flow of time in tests. They allow you to advance time, fire timers, and
* control the behavior of time-dependent functions. See {@link com.microsoft.playwright.Clock#runFor Clock.runFor()} and
* {@link com.microsoft.playwright.Clock#fastForward Clock.fastForward()} for more information.
*
* @since v1.45
*/
default void install() {
install(null);
}
/**
* Install fake implementations for the following time-related functions:
* <ul>
* <li> {@code Date}</li>
* <li> {@code setTimeout}</li>
* <li> {@code clearTimeout}</li>
* <li> {@code setInterval}</li>
* <li> {@code clearInterval}</li>
* <li> {@code requestAnimationFrame}</li>
* <li> {@code cancelAnimationFrame}</li>
* <li> {@code requestIdleCallback}</li>
* <li> {@code cancelIdleCallback}</li>
* <li> {@code performance}</li>
* </ul>
*
* <p> Fake timers are used to manually control the flow of time in tests. They allow you to advance time, fire timers, and
* control the behavior of time-dependent functions. See {@link com.microsoft.playwright.Clock#runFor Clock.runFor()} and
* {@link com.microsoft.playwright.Clock#fastForward Clock.fastForward()} for more information.
*
* @since v1.45
*/
void install(InstallOptions options);
/**
* Advance the clock, firing all the time-related callbacks.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().runFor(1000);
* page.clock().runFor("30:00");
* }</pre>
*
* @param ticks Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are "08"
* for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds.
* @since v1.45
*/
void runFor(long ticks);
/**
* Advance the clock, firing all the time-related callbacks.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().runFor(1000);
* page.clock().runFor("30:00");
* }</pre>
*
* @param ticks Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are "08"
* for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds.
* @since v1.45
*/
void runFor(String ticks);
/**
* Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers are fired unless
* {@link com.microsoft.playwright.Clock#runFor Clock.runFor()}, {@link com.microsoft.playwright.Clock#fastForward
* Clock.fastForward()}, {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} or {@link
* com.microsoft.playwright.Clock#resume Clock.resume()} is called.
*
* <p> Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and reopening it at
* the specified time and pausing.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd");
* page.clock().pauseAt(format.parse("2020-02-02"));
* page.clock().pauseAt("2020-02-02");
* }</pre>
*
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
* clock.
* <pre>{@code
* // Initialize clock with some time before the test time and let the page load
* // naturally. `Date.now` will progress as the timers fire.
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
* page.navigate("http://localhost:3333");
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
* }</pre>
*
* @param time Time to pause at.
* @since v1.45
*/
void pauseAt(long time);
/**
* Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers are fired unless
* {@link com.microsoft.playwright.Clock#runFor Clock.runFor()}, {@link com.microsoft.playwright.Clock#fastForward
* Clock.fastForward()}, {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} or {@link
* com.microsoft.playwright.Clock#resume Clock.resume()} is called.
*
* <p> Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and reopening it at
* the specified time and pausing.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd");
* page.clock().pauseAt(format.parse("2020-02-02"));
* page.clock().pauseAt("2020-02-02");
* }</pre>
*
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
* clock.
* <pre>{@code
* // Initialize clock with some time before the test time and let the page load
* // naturally. `Date.now` will progress as the timers fire.
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
* page.navigate("http://localhost:3333");
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
* }</pre>
*
* @param time Time to pause at.
* @since v1.45
*/
void pauseAt(String time);
/**
* Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers are fired unless
* {@link com.microsoft.playwright.Clock#runFor Clock.runFor()}, {@link com.microsoft.playwright.Clock#fastForward
* Clock.fastForward()}, {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} or {@link
* com.microsoft.playwright.Clock#resume Clock.resume()} is called.
*
* <p> Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and reopening it at
* the specified time and pausing.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd");
* page.clock().pauseAt(format.parse("2020-02-02"));
* page.clock().pauseAt("2020-02-02");
* }</pre>
*
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
* clock.
* <pre>{@code
* // Initialize clock with some time before the test time and let the page load
* // naturally. `Date.now` will progress as the timers fire.
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
* page.navigate("http://localhost:3333");
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
* }</pre>
*
* @param time Time to pause at.
* @since v1.45
*/
void pauseAt(Date time);
/**
* Resumes timers. Once this method is called, time resumes flowing, timers are fired as usual.
*
* @since v1.45
*/
void resume();
/**
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
*
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setFixedTime(new Date());
* page.clock().setFixedTime(new SimpleDateFormat("yyy-MM-dd").parse("2020-02-02"));
* page.clock().setFixedTime("2020-02-02");
* }</pre>
*
* @param time Time to be set in milliseconds.
* @since v1.45
*/
void setFixedTime(long time);
/**
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
*
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setFixedTime(new Date());
* page.clock().setFixedTime(new SimpleDateFormat("yyy-MM-dd").parse("2020-02-02"));
* page.clock().setFixedTime("2020-02-02");
* }</pre>
*
* @param time Time to be set in milliseconds.
* @since v1.45
*/
void setFixedTime(String time);
/**
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
*
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setFixedTime(new Date());
* page.clock().setFixedTime(new SimpleDateFormat("yyy-MM-dd").parse("2020-02-02"));
* page.clock().setFixedTime("2020-02-02");
* }</pre>
*
* @param time Time to be set in milliseconds.
* @since v1.45
*/
void setFixedTime(Date time);
/**
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
* switching from summer to winter time, or changing time zones.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setSystemTime(new Date());
* page.clock().setSystemTime(new SimpleDateFormat("yyy-MM-dd").parse("2020-02-02"));
* page.clock().setSystemTime("2020-02-02");
* }</pre>
*
* @param time Time to be set in milliseconds.
* @since v1.45
*/
void setSystemTime(long time);
/**
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
* switching from summer to winter time, or changing time zones.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setSystemTime(new Date());
* page.clock().setSystemTime(new SimpleDateFormat("yyy-MM-dd").parse("2020-02-02"));
* page.clock().setSystemTime("2020-02-02");
* }</pre>
*
* @param time Time to be set in milliseconds.
* @since v1.45
*/
void setSystemTime(String time);
/**
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
* switching from summer to winter time, or changing time zones.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setSystemTime(new Date());
* page.clock().setSystemTime(new SimpleDateFormat("yyy-MM-dd").parse("2020-02-02"));
* page.clock().setSystemTime("2020-02-02");
* }</pre>
*
* @param time Time to be set in milliseconds.
* @since v1.45
*/
void setSystemTime(Date time);
}
@@ -19,8 +19,9 @@ package com.microsoft.playwright;
import java.util.*;
/**
* {@code ConsoleMessage} objects are dispatched by page via the {@link Page#onConsoleMessage Page.onConsoleMessage()}
* event. For each console messages logged in the page there will be corresponding event in the Playwright context.
* {@code ConsoleMessage} objects are dispatched by page via the {@link com.microsoft.playwright.Page#onConsoleMessage
* Page.onConsoleMessage()} event. For each console message logged in the page there will be corresponding event in the
* Playwright context.
* <pre>{@code
* // Listen for all console messages and print them to the standard output.
* page.onConsoleMessage(msg -> System.out.println(msg.text()));
@@ -38,14 +39,14 @@ import java.util.*;
* });
*
* // Deconstruct console.log arguments
* msg.args().get(0).jsonValue() // hello
* msg.args().get(1).jsonValue() // 42
* msg.args().get(0).jsonValue(); // hello
* msg.args().get(1).jsonValue(); // 42
* }</pre>
*/
public interface ConsoleMessage {
/**
* List of arguments passed to a {@code console} function call. See also {@link Page#onConsoleMessage
* Page.onConsoleMessage()}.
* List of arguments passed to a {@code console} function call. See also {@link
* com.microsoft.playwright.Page#onConsoleMessage Page.onConsoleMessage()}.
*
* @since v1.8
*/
@@ -18,7 +18,8 @@ package com.microsoft.playwright;
/**
* {@code Dialog} objects are dispatched by page via the {@link Page#onDialog Page.onDialog()} event.
* {@code Dialog} objects are dispatched by page via the {@link com.microsoft.playwright.Page#onDialog Page.onDialog()}
* event.
*
* <p> An example of using {@code Dialog} class:
* <pre>{@code
@@ -41,9 +42,9 @@ package com.microsoft.playwright;
* }
* }</pre>
*
* <p> <strong>NOTE:</strong> Dialogs are dismissed automatically, unless there is a {@link Page#onDialog Page.onDialog()} listener. When listener is
* present, it **must** either {@link Dialog#accept Dialog.accept()} or {@link Dialog#dismiss Dialog.dismiss()} the dialog
* - otherwise the page will <a
* <p> <strong>NOTE:</strong> Dialogs are dismissed automatically, unless there is a {@link com.microsoft.playwright.Page#onDialog Page.onDialog()}
* listener. When listener is present, it **must** either {@link com.microsoft.playwright.Dialog#accept Dialog.accept()} or
* {@link com.microsoft.playwright.Dialog#dismiss Dialog.dismiss()} the dialog - otherwise the page will <a
* href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking">freeze</a> waiting for the
* dialog, and actions like click will never finish.
*/
@@ -20,7 +20,8 @@ import java.io.InputStream;
import java.nio.file.Path;
/**
* {@code Download} objects are dispatched by page via the {@link Page#onDownload Page.onDownload()} event.
* {@code Download} objects are dispatched by page via the {@link com.microsoft.playwright.Page#onDownload
* Page.onDownload()} event.
*
* <p> All the downloaded files belonging to the browser context are deleted when the browser context is closed.
*
@@ -72,8 +73,8 @@ public interface Download {
* Returns path to the downloaded file for a successful download, or throws for a failed/canceled download. The method will
* wait for the download to finish if necessary. The method throws when connected remotely.
*
* <p> Note that the download's file name is a random GUID, use {@link Download#suggestedFilename Download.suggestedFilename()}
* to get suggested file name.
* <p> Note that the download's file name is a random GUID, use {@link com.microsoft.playwright.Download#suggestedFilename
* Download.suggestedFilename()} to get suggested file name.
*
* @since v1.8
*/
@@ -82,7 +83,7 @@ public interface Download {
* Copy the download to a user-specified path. It is safe to call this method while the download is still in progress. Will
* wait for the download to finish if necessary.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* download.saveAs(Paths.get("/path/to/save/at/", download.suggestedFilename()));
* }</pre>
File diff suppressed because it is too large Load Diff
@@ -20,7 +20,8 @@ import com.microsoft.playwright.options.*;
import java.nio.file.Path;
/**
* {@code FileChooser} objects are dispatched by the page in the {@link Page#onFileChooser Page.onFileChooser()} event.
* {@code FileChooser} objects are dispatched by the page in the {@link com.microsoft.playwright.Page#onFileChooser
* Page.onFileChooser()} event.
* <pre>{@code
* FileChooser fileChooser = page.waitForFileChooser(() -> page.getByText("Upload file").click());
* fileChooser.setFiles(Paths.get("myfile.pdf"));
@@ -29,22 +30,19 @@ import java.nio.file.Path;
public interface FileChooser {
class SetFilesOptions {
/**
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
* inaccessible pages. Defaults to {@code false}.
* @deprecated This option has no effect.
*/
public Boolean noWaitAfter;
/**
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link
* Page#setDefaultTimeout Page.setDefaultTimeout()} methods.
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
* methods.
*/
public Double timeout;
/**
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
* inaccessible pages. Defaults to {@code false}.
* @deprecated This option has no effect.
*/
public SetFilesOptions setNoWaitAfter(boolean noWaitAfter) {
this.noWaitAfter = noWaitAfter;
@@ -52,8 +50,9 @@ public interface FileChooser {
}
/**
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link
* Page#setDefaultTimeout Page.setDefaultTimeout()} methods.
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
* methods.
*/
public SetFilesOptions setTimeout(double timeout) {
this.timeout = timeout;
File diff suppressed because it is too large Load Diff
@@ -21,32 +21,35 @@ import java.util.regex.Pattern;
/**
* FrameLocator represents a view to the {@code iframe} on the page. It captures the logic sufficient to retrieve the
* {@code iframe} and locate elements in that iframe. FrameLocator can be created with either {@link Page#frameLocator
* Page.frameLocator()} or {@link Locator#frameLocator Locator.frameLocator()} method.
* {@code iframe} and locate elements in that iframe. FrameLocator can be created with either {@link
* com.microsoft.playwright.Locator#contentFrame Locator.contentFrame()}, {@link com.microsoft.playwright.Page#frameLocator
* Page.frameLocator()} or {@link com.microsoft.playwright.Locator#frameLocator Locator.frameLocator()} method.
* <pre>{@code
* Locator locator = page.frameLocator("#my-frame").getByText("Submit");
* Locator locator = page.locator("#my-frame").contentFrame().getByText("Submit");
* locator.click();
* }</pre>
*
* <p> **Strictness**
* <p> <strong>Strictness</strong>
*
* <p> Frame locators are strict. This means that all operations on frame locators will throw if more than one element matches
* a given selector.
* <pre>{@code
* // Throws if there are several frames in DOM:
* page.frame_locator(".result-frame").getByRole(AriaRole.BUTTON).click();
* page.locator(".result-frame").contentFrame().getByRole(AriaRole.BUTTON).click();
*
* // Works because we explicitly tell locator to pick the first frame:
* page.frame_locator(".result-frame").first().getByRole(AriaRole.BUTTON).click();
* page.locator(".result-frame").first().contentFrame().getByRole(AriaRole.BUTTON).click();
* }</pre>
*
* <p> **Converting Locator to FrameLocator**
* <p> <strong>Converting Locator to FrameLocator</strong>
*
* <p> If you have a {@code Locator} object pointing to an {@code iframe} it can be converted to {@code FrameLocator} using <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/:scope">{@code :scope}</a> CSS selector:
* <pre>{@code
* Locator frameLocator = locator.frameLocator(':scope');
* }</pre>
* <p> If you have a {@code Locator} object pointing to an {@code iframe} it can be converted to {@code FrameLocator} using
* {@link com.microsoft.playwright.Locator#contentFrame Locator.contentFrame()}.
*
* <p> <strong>Converting FrameLocator to Locator</strong>
*
* <p> If you have a {@code FrameLocator} object it can be converted to {@code Locator} pointing to the same {@code iframe}
* using {@link com.microsoft.playwright.FrameLocator#owner FrameLocator.owner()}.
*/
public interface FrameLocator {
class GetByAltTextOptions {
@@ -285,8 +288,14 @@ public interface FrameLocator {
}
class LocatorOptions {
/**
* Matches elements containing an element that matches an inner locator. Inner locator is queried against the outer one.
* For example, {@code article} that has {@code text=Playwright} matches {@code <article><div>Playwright</div></article>}.
* Narrows down the results of the method to those which contain elements matching this relative locator. For example,
* {@code article} that has {@code text=Playwright} matches {@code <article><div>Playwright</div></article>}.
*
* <p> Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not the
* document root. For example, you can find {@code content} that has {@code div} in {@code
* <article><content><div>Playwright</div></content></article>}. However, looking for {@code content} that has {@code
* article div} will fail, because the inner locator must be relative and should not use any elements outside the {@code
* content}.
*
* <p> Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s.
*/
@@ -312,8 +321,14 @@ public interface FrameLocator {
public Object hasText;
/**
* Matches elements containing an element that matches an inner locator. Inner locator is queried against the outer one.
* For example, {@code article} that has {@code text=Playwright} matches {@code <article><div>Playwright</div></article>}.
* Narrows down the results of the method to those which contain elements matching this relative locator. For example,
* {@code article} that has {@code text=Playwright} matches {@code <article><div>Playwright</div></article>}.
*
* <p> Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not the
* document root. For example, you can find {@code content} that has {@code div} in {@code
* <article><content><div>Playwright</div></content></article>}. However, looking for {@code content} that has {@code
* article div} will fail, because the inner locator must be relative and should not use any elements outside the {@code
* content}.
*
* <p> Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s.
*/
@@ -368,7 +383,8 @@ public interface FrameLocator {
}
}
/**
* Returns locator to the first matching frame.
* @deprecated Use {@link com.microsoft.playwright.Locator#first Locator.first()} followed by {@link
* com.microsoft.playwright.Locator#contentFrame Locator.contentFrame()} instead.
*
* @since v1.17
*/
@@ -384,7 +400,7 @@ public interface FrameLocator {
/**
* Allows locating elements by their alt text.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, this method will find the image by alt text "Playwright logo":
* <pre>{@code
@@ -400,7 +416,7 @@ public interface FrameLocator {
/**
* Allows locating elements by their alt text.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, this method will find the image by alt text "Playwright logo":
* <pre>{@code
@@ -414,7 +430,7 @@ public interface FrameLocator {
/**
* Allows locating elements by their alt text.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, this method will find the image by alt text "Playwright logo":
* <pre>{@code
@@ -430,7 +446,7 @@ public interface FrameLocator {
/**
* Allows locating elements by their alt text.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, this method will find the image by alt text "Playwright logo":
* <pre>{@code
@@ -445,7 +461,7 @@ public interface FrameLocator {
* Allows locating input elements by the text of the associated {@code <label>} or {@code aria-labelledby} element, or by
* the {@code aria-label} attribute.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, this method will find inputs by label "Username" and "Password" in the following DOM:
* <pre>{@code
@@ -463,7 +479,7 @@ public interface FrameLocator {
* Allows locating input elements by the text of the associated {@code <label>} or {@code aria-labelledby} element, or by
* the {@code aria-label} attribute.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, this method will find inputs by label "Username" and "Password" in the following DOM:
* <pre>{@code
@@ -479,7 +495,7 @@ public interface FrameLocator {
* Allows locating input elements by the text of the associated {@code <label>} or {@code aria-labelledby} element, or by
* the {@code aria-label} attribute.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, this method will find inputs by label "Username" and "Password" in the following DOM:
* <pre>{@code
@@ -497,7 +513,7 @@ public interface FrameLocator {
* Allows locating input elements by the text of the associated {@code <label>} or {@code aria-labelledby} element, or by
* the {@code aria-label} attribute.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, this method will find inputs by label "Username" and "Password" in the following DOM:
* <pre>{@code
@@ -512,7 +528,7 @@ public interface FrameLocator {
/**
* Allows locating input elements by the placeholder text.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, consider the following DOM structure.
*
@@ -530,7 +546,7 @@ public interface FrameLocator {
/**
* Allows locating input elements by the placeholder text.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, consider the following DOM structure.
*
@@ -546,7 +562,7 @@ public interface FrameLocator {
/**
* Allows locating input elements by the placeholder text.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, consider the following DOM structure.
*
@@ -564,7 +580,7 @@ public interface FrameLocator {
/**
* Allows locating input elements by the placeholder text.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, consider the following DOM structure.
*
@@ -582,7 +598,7 @@ public interface FrameLocator {
* href="https://www.w3.org/TR/wai-aria-1.2/#aria-attributes">ARIA attributes</a> and <a
* href="https://w3c.github.io/accname/#dfn-accessible-name">accessible name</a>.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure.
*
@@ -603,7 +619,7 @@ public interface FrameLocator {
* .click();
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback about the
* ARIA guidelines.
@@ -625,7 +641,7 @@ public interface FrameLocator {
* href="https://www.w3.org/TR/wai-aria-1.2/#aria-attributes">ARIA attributes</a> and <a
* href="https://w3c.github.io/accname/#dfn-accessible-name">accessible name</a>.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure.
*
@@ -646,7 +662,7 @@ public interface FrameLocator {
* .click();
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback about the
* ARIA guidelines.
@@ -664,7 +680,7 @@ public interface FrameLocator {
/**
* Locate element by the test id.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure.
*
@@ -673,10 +689,11 @@ public interface FrameLocator {
* page.getByTestId("directions").click();
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> By default, the {@code data-testid} attribute is used as a test id. Use {@link Selectors#setTestIdAttribute
* Selectors.setTestIdAttribute()} to configure a different test id attribute if necessary.
* <p> By default, the {@code data-testid} attribute is used as a test id. Use {@link
* com.microsoft.playwright.Selectors#setTestIdAttribute Selectors.setTestIdAttribute()} to configure a different test id
* attribute if necessary.
*
* @param testId Id to locate the element by.
* @since v1.27
@@ -685,7 +702,7 @@ public interface FrameLocator {
/**
* Locate element by the test id.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure.
*
@@ -694,10 +711,11 @@ public interface FrameLocator {
* page.getByTestId("directions").click();
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> By default, the {@code data-testid} attribute is used as a test id. Use {@link Selectors#setTestIdAttribute
* Selectors.setTestIdAttribute()} to configure a different test id attribute if necessary.
* <p> By default, the {@code data-testid} attribute is used as a test id. Use {@link
* com.microsoft.playwright.Selectors#setTestIdAttribute Selectors.setTestIdAttribute()} to configure a different test id
* attribute if necessary.
*
* @param testId Id to locate the element by.
* @since v1.27
@@ -706,32 +724,32 @@ public interface FrameLocator {
/**
* Allows locating elements that contain given text.
*
* <p> See also {@link Locator#filter Locator.filter()} that allows to match by another criteria, like an accessible role, and
* then filter by the text content.
* <p> See also {@link com.microsoft.playwright.Locator#filter Locator.filter()} that allows to match by another criteria, like
* an accessible role, and then filter by the text content.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure:
*
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world")
* page.getByText("world");
*
* // Matches first <div>
* page.getByText("Hello world")
* page.getByText("Hello world");
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"))
* page.getByText(Pattern.compile("Hello"));
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into one,
* turns line breaks into spaces and ignores leading and trailing whitespace.
@@ -748,32 +766,32 @@ public interface FrameLocator {
/**
* Allows locating elements that contain given text.
*
* <p> See also {@link Locator#filter Locator.filter()} that allows to match by another criteria, like an accessible role, and
* then filter by the text content.
* <p> See also {@link com.microsoft.playwright.Locator#filter Locator.filter()} that allows to match by another criteria, like
* an accessible role, and then filter by the text content.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure:
*
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world")
* page.getByText("world");
*
* // Matches first <div>
* page.getByText("Hello world")
* page.getByText("Hello world");
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"))
* page.getByText(Pattern.compile("Hello"));
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into one,
* turns line breaks into spaces and ignores leading and trailing whitespace.
@@ -788,32 +806,32 @@ public interface FrameLocator {
/**
* Allows locating elements that contain given text.
*
* <p> See also {@link Locator#filter Locator.filter()} that allows to match by another criteria, like an accessible role, and
* then filter by the text content.
* <p> See also {@link com.microsoft.playwright.Locator#filter Locator.filter()} that allows to match by another criteria, like
* an accessible role, and then filter by the text content.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure:
*
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world")
* page.getByText("world");
*
* // Matches first <div>
* page.getByText("Hello world")
* page.getByText("Hello world");
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"))
* page.getByText(Pattern.compile("Hello"));
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into one,
* turns line breaks into spaces and ignores leading and trailing whitespace.
@@ -830,32 +848,32 @@ public interface FrameLocator {
/**
* Allows locating elements that contain given text.
*
* <p> See also {@link Locator#filter Locator.filter()} that allows to match by another criteria, like an accessible role, and
* then filter by the text content.
* <p> See also {@link com.microsoft.playwright.Locator#filter Locator.filter()} that allows to match by another criteria, like
* an accessible role, and then filter by the text content.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure:
*
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world")
* page.getByText("world");
*
* // Matches first <div>
* page.getByText("Hello world")
* page.getByText("Hello world");
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"))
* page.getByText(Pattern.compile("Hello"));
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into one,
* turns line breaks into spaces and ignores leading and trailing whitespace.
@@ -870,7 +888,7 @@ public interface FrameLocator {
/**
* Allows locating elements by their title attribute.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure.
*
@@ -888,7 +906,7 @@ public interface FrameLocator {
/**
* Allows locating elements by their title attribute.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure.
*
@@ -904,7 +922,7 @@ public interface FrameLocator {
/**
* Allows locating elements by their title attribute.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure.
*
@@ -922,7 +940,7 @@ public interface FrameLocator {
/**
* Allows locating elements by their title attribute.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Consider the following DOM structure.
*
@@ -936,14 +954,15 @@ public interface FrameLocator {
*/
Locator getByTitle(Pattern text, GetByTitleOptions options);
/**
* Returns locator to the last matching frame.
* @deprecated Use {@link com.microsoft.playwright.Locator#last Locator.last()} followed by {@link
* com.microsoft.playwright.Locator#contentFrame Locator.contentFrame()} instead.
*
* @since v1.17
*/
FrameLocator last();
/**
* The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
* similar to {@link Locator#filter Locator.filter()} method.
* similar to {@link com.microsoft.playwright.Locator#filter Locator.filter()} method.
*
* <p> <a href="https://playwright.dev/java/docs/locators">Learn more about locators</a>.
*
@@ -955,7 +974,7 @@ public interface FrameLocator {
}
/**
* The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
* similar to {@link Locator#filter Locator.filter()} method.
* similar to {@link com.microsoft.playwright.Locator#filter Locator.filter()} method.
*
* <p> <a href="https://playwright.dev/java/docs/locators">Learn more about locators</a>.
*
@@ -965,7 +984,7 @@ public interface FrameLocator {
Locator locator(String selectorOrLocator, LocatorOptions options);
/**
* The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
* similar to {@link Locator#filter Locator.filter()} method.
* similar to {@link com.microsoft.playwright.Locator#filter Locator.filter()} method.
*
* <p> <a href="https://playwright.dev/java/docs/locators">Learn more about locators</a>.
*
@@ -977,7 +996,7 @@ public interface FrameLocator {
}
/**
* The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
* similar to {@link Locator#filter Locator.filter()} method.
* similar to {@link com.microsoft.playwright.Locator#filter Locator.filter()} method.
*
* <p> <a href="https://playwright.dev/java/docs/locators">Learn more about locators</a>.
*
@@ -986,10 +1005,30 @@ public interface FrameLocator {
*/
Locator locator(Locator selectorOrLocator, LocatorOptions options);
/**
* Returns locator to the n-th matching frame. It's zero based, {@code nth(0)} selects the first frame.
* @deprecated Use {@link com.microsoft.playwright.Locator#nth Locator.nth()} followed by {@link
* com.microsoft.playwright.Locator#contentFrame Locator.contentFrame()} instead.
*
* @since v1.17
*/
FrameLocator nth(int index);
/**
* Returns a {@code Locator} object pointing to the same {@code iframe} as this frame locator.
*
* <p> Useful when you have a {@code FrameLocator} object obtained somewhere, and later on would like to interact with the
* {@code iframe} element.
*
* <p> For a reverse operation, use {@link com.microsoft.playwright.Locator#contentFrame Locator.contentFrame()}.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* FrameLocator frameLocator = page.locator("iframe[name=\"embedded\"]").contentFrame();
* // ...
* Locator locator = frameLocator.owner();
* assertThat(locator).isVisible();
* }</pre>
*
* @since v1.43
*/
Locator owner();
}
@@ -19,19 +19,20 @@ package com.microsoft.playwright;
import java.util.*;
/**
* JSHandle represents an in-page JavaScript object. JSHandles can be created with the {@link Page#evaluateHandle
* Page.evaluateHandle()} method.
* JSHandle represents an in-page JavaScript object. JSHandles can be created with the {@link
* com.microsoft.playwright.Page#evaluateHandle Page.evaluateHandle()} method.
* <pre>{@code
* JSHandle windowHandle = page.evaluateHandle("() => window");
* // ...
* }</pre>
*
* <p> JSHandle prevents the referenced JavaScript object being garbage collected unless the handle is exposed with {@link
* JSHandle#dispose JSHandle.dispose()}. JSHandles are auto-disposed when their origin frame gets navigated or the parent
* context gets destroyed.
* com.microsoft.playwright.JSHandle#dispose JSHandle.dispose()}. JSHandles are auto-disposed when their origin frame gets
* navigated or the parent context gets destroyed.
*
* <p> JSHandle instances can be used as an argument in {@link Page#evalOnSelector Page.evalOnSelector()}, {@link Page#evaluate
* Page.evaluate()} and {@link Page#evaluateHandle Page.evaluateHandle()} methods.
* <p> JSHandle instances can be used as an argument in {@link com.microsoft.playwright.Page#evalOnSelector
* Page.evalOnSelector()}, {@link com.microsoft.playwright.Page#evaluate Page.evaluate()} and {@link
* com.microsoft.playwright.Page#evaluateHandle Page.evaluateHandle()} methods.
*/
public interface JSHandle {
/**
@@ -55,7 +56,7 @@ public interface JSHandle {
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@code
* handle.evaluate} would wait for the promise to resolve and return its value.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* ElementHandle tweetHandle = page.querySelector(".tweet .retweets");
* assertEquals("10 retweets", tweetHandle.evaluate("node => node.innerText"));
@@ -77,7 +78,7 @@ public interface JSHandle {
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@code
* handle.evaluate} would wait for the promise to resolve and return its value.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* ElementHandle tweetHandle = page.querySelector(".tweet .retweets");
* assertEquals("10 retweets", tweetHandle.evaluate("node => node.innerText"));
@@ -101,7 +102,7 @@ public interface JSHandle {
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@code
* jsHandle.evaluateHandle} would wait for the promise to resolve and return its value.
*
* <p> See {@link Page#evaluateHandle Page.evaluateHandle()} for more details.
* <p> See {@link com.microsoft.playwright.Page#evaluateHandle Page.evaluateHandle()} for more details.
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
@@ -122,7 +123,7 @@ public interface JSHandle {
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@code
* jsHandle.evaluateHandle} would wait for the promise to resolve and return its value.
*
* <p> See {@link Page#evaluateHandle Page.evaluateHandle()} for more details.
* <p> See {@link com.microsoft.playwright.Page#evaluateHandle Page.evaluateHandle()} for more details.
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
@@ -133,7 +134,7 @@ public interface JSHandle {
/**
* The method returns a map with **own property names** as keys and JSHandle instances for the property values.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* JSHandle handle = page.evaluateHandle("() => ({ window, document })");
* Map<String, JSHandle> properties = handle.getProperties();
@@ -19,12 +19,13 @@ package com.microsoft.playwright;
import com.microsoft.playwright.options.*;
/**
* Keyboard provides an api for managing a virtual keyboard. The high level api is {@link Keyboard#type Keyboard.type()},
* which takes raw characters and generates proper {@code keydown}, {@code keypress}/{@code input}, and {@code keyup}
* events on your page.
* Keyboard provides an api for managing a virtual keyboard. The high level api is {@link
* com.microsoft.playwright.Keyboard#type Keyboard.type()}, which takes raw characters and generates proper {@code
* keydown}, {@code keypress}/{@code input}, and {@code keyup} events on your page.
*
* <p> For finer control, you can use {@link Keyboard#down Keyboard.down()}, {@link Keyboard#up Keyboard.up()}, and {@link
* Keyboard#insertText Keyboard.insertText()} to manually fire events as if they were generated from a real keyboard.
* <p> For finer control, you can use {@link com.microsoft.playwright.Keyboard#down Keyboard.down()}, {@link
* com.microsoft.playwright.Keyboard#up Keyboard.up()}, and {@link com.microsoft.playwright.Keyboard#insertText
* Keyboard.insertText()} to manually fire events as if they were generated from a real keyboard.
*
* <p> An example of holding down {@code Shift} in order to select and delete some text:
* <pre>{@code
@@ -47,10 +48,7 @@ import com.microsoft.playwright.options.*;
*
* <p> An example to trigger select-all with the keyboard
* <pre>{@code
* // on Windows and Linux
* page.keyboard().press("Control+A");
* // on macOS
* page.keyboard().press("Meta+A");
* page.keyboard().press("ControlOrMeta+A");
* }</pre>
*/
public interface Keyboard {
@@ -96,7 +94,8 @@ public interface Keyboard {
* ArrowUp}, etc.
*
* <p> Following modification shortcuts are also supported: {@code Shift}, {@code Control}, {@code Alt}, {@code Meta}, {@code
* ShiftLeft}.
* ShiftLeft}, {@code ControlOrMeta}. {@code ControlOrMeta} resolves to {@code Control} on Windows and Linux and to {@code
* Meta} on macOS.
*
* <p> Holding down {@code Shift} will type the text that corresponds to the {@code key} in the upper case.
*
@@ -104,11 +103,12 @@ public interface Keyboard {
* different respective texts.
*
* <p> If {@code key} is a modifier key, {@code Shift}, {@code Meta}, {@code Control}, or {@code Alt}, subsequent key presses
* will be sent with that modifier active. To release the modifier key, use {@link Keyboard#up Keyboard.up()}.
* will be sent with that modifier active. To release the modifier key, use {@link com.microsoft.playwright.Keyboard#up
* Keyboard.up()}.
*
* <p> After the key is pressed once, subsequent calls to {@link Keyboard#down Keyboard.down()} will have <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat">repeat</a> set to true. To release the key,
* use {@link Keyboard#up Keyboard.up()}.
* <p> After the key is pressed once, subsequent calls to {@link com.microsoft.playwright.Keyboard#down Keyboard.down()} will
* have <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat">repeat</a> set to true. To release
* the key, use {@link com.microsoft.playwright.Keyboard#up Keyboard.up()}.
*
* <p> <strong>NOTE:</strong> Modifier keys DO influence {@code keyboard.down}. Holding down {@code Shift} will type the text in upper case.
*
@@ -119,7 +119,7 @@ public interface Keyboard {
/**
* Dispatches only {@code input} event, does not emit the {@code keydown}, {@code keyup} or {@code keypress} events.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* page.keyboard().insertText("嗨");
* }</pre>
@@ -132,7 +132,7 @@ public interface Keyboard {
*/
void insertText(String text);
/**
* <strong>NOTE:</strong> In most cases, you should use {@link Locator#press Locator.press()} instead.
* <strong>NOTE:</strong> In most cases, you should use {@link com.microsoft.playwright.Locator#press Locator.press()} instead.
*
* <p> {@code key} can specify the intended <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key">keyboardEvent.key</a> value or a single
@@ -145,22 +145,23 @@ public interface Keyboard {
* ArrowUp}, etc.
*
* <p> Following modification shortcuts are also supported: {@code Shift}, {@code Control}, {@code Alt}, {@code Meta}, {@code
* ShiftLeft}.
* ShiftLeft}, {@code ControlOrMeta}. {@code ControlOrMeta} resolves to {@code Control} on Windows and Linux and to {@code
* Meta} on macOS.
*
* <p> Holding down {@code Shift} will type the text that corresponds to the {@code key} in the upper case.
*
* <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**
* <p> <strong>Usage</strong>
* <pre>{@code
* Page page = browser.newPage();
* page.navigate("https://keycode.info");
* page.keyboard().press("A");
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"));
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")));
* page.keyboard().press("ArrowLeft");
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
* page.keyboard().press("Shift+O");
@@ -168,7 +169,8 @@ public interface Keyboard {
* browser.close();
* }</pre>
*
* <p> Shortcut for {@link Keyboard#down Keyboard.down()} and {@link Keyboard#up Keyboard.up()}.
* <p> Shortcut for {@link com.microsoft.playwright.Keyboard#down Keyboard.down()} and {@link
* com.microsoft.playwright.Keyboard#up Keyboard.up()}.
*
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
* @since v1.8
@@ -177,7 +179,7 @@ public interface Keyboard {
press(key, null);
}
/**
* <strong>NOTE:</strong> In most cases, you should use {@link Locator#press Locator.press()} instead.
* <strong>NOTE:</strong> In most cases, you should use {@link com.microsoft.playwright.Locator#press Locator.press()} instead.
*
* <p> {@code key} can specify the intended <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key">keyboardEvent.key</a> value or a single
@@ -190,22 +192,23 @@ public interface Keyboard {
* ArrowUp}, etc.
*
* <p> Following modification shortcuts are also supported: {@code Shift}, {@code Control}, {@code Alt}, {@code Meta}, {@code
* ShiftLeft}.
* ShiftLeft}, {@code ControlOrMeta}. {@code ControlOrMeta} resolves to {@code Control} on Windows and Linux and to {@code
* Meta} on macOS.
*
* <p> Holding down {@code Shift} will type the text that corresponds to the {@code key} in the upper case.
*
* <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**
* <p> <strong>Usage</strong>
* <pre>{@code
* Page page = browser.newPage();
* page.navigate("https://keycode.info");
* page.keyboard().press("A");
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"));
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")));
* page.keyboard().press("ArrowLeft");
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
* page.keyboard().press("Shift+O");
@@ -213,22 +216,24 @@ public interface Keyboard {
* browser.close();
* }</pre>
*
* <p> Shortcut for {@link Keyboard#down Keyboard.down()} and {@link Keyboard#up Keyboard.up()}.
* <p> Shortcut for {@link com.microsoft.playwright.Keyboard#down Keyboard.down()} and {@link
* com.microsoft.playwright.Keyboard#up Keyboard.up()}.
*
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
* @since v1.8
*/
void press(String key, PressOptions options);
/**
* <strong>NOTE:</strong> In most cases, you should use {@link Locator#fill Locator.fill()} instead. You only need to press keys one by one if
* there is special keyboard handling on the page - in this case use {@link Locator#pressSequentially
* Locator.pressSequentially()}.
* <strong>NOTE:</strong> In most cases, you should use {@link com.microsoft.playwright.Locator#fill Locator.fill()} instead. You only need to
* press keys one by one if there is special keyboard handling on the page - in this case use {@link
* com.microsoft.playwright.Locator#pressSequentially Locator.pressSequentially()}.
*
* <p> Sends a {@code keydown}, {@code keypress}/{@code input}, and {@code keyup} event for each character in the text.
*
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use {@link Keyboard#press Keyboard.press()}.
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use {@link com.microsoft.playwright.Keyboard#press
* Keyboard.press()}.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* // Types instantly
* page.keyboard().type("Hello");
@@ -247,15 +252,16 @@ public interface Keyboard {
type(text, null);
}
/**
* <strong>NOTE:</strong> In most cases, you should use {@link Locator#fill Locator.fill()} instead. You only need to press keys one by one if
* there is special keyboard handling on the page - in this case use {@link Locator#pressSequentially
* Locator.pressSequentially()}.
* <strong>NOTE:</strong> In most cases, you should use {@link com.microsoft.playwright.Locator#fill Locator.fill()} instead. You only need to
* press keys one by one if there is special keyboard handling on the page - in this case use {@link
* com.microsoft.playwright.Locator#pressSequentially Locator.pressSequentially()}.
*
* <p> Sends a {@code keydown}, {@code keypress}/{@code input}, and {@code keyup} event for each character in the text.
*
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use {@link Keyboard#press Keyboard.press()}.
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use {@link com.microsoft.playwright.Keyboard#press
* Keyboard.press()}.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* // Types instantly
* page.keyboard().type("Hello");
File diff suppressed because it is too large Load Diff
@@ -21,7 +21,7 @@ import com.microsoft.playwright.options.*;
/**
* The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport.
*
* <p> Every {@code page} object has its own Mouse, accessible with {@link Page#mouse Page.mouse()}.
* <p> Every {@code page} object has its own Mouse, accessible with {@link com.microsoft.playwright.Page#mouse Page.mouse()}.
* <pre>{@code
* // Using page.mouse to trace a 100x100 square.
* page.mouse().move(0, 0);
@@ -160,32 +160,44 @@ public interface Mouse {
}
}
/**
* Shortcut for {@link Mouse#move Mouse.move()}, {@link Mouse#down Mouse.down()}, {@link Mouse#up Mouse.up()}.
* Shortcut for {@link com.microsoft.playwright.Mouse#move Mouse.move()}, {@link com.microsoft.playwright.Mouse#down
* Mouse.down()}, {@link com.microsoft.playwright.Mouse#up Mouse.up()}.
*
* @param x X coordinate relative to the main frame's viewport in CSS pixels.
* @param y Y coordinate relative to the main frame's viewport in CSS pixels.
* @since v1.8
*/
default void click(double x, double y) {
click(x, y, null);
}
/**
* Shortcut for {@link Mouse#move Mouse.move()}, {@link Mouse#down Mouse.down()}, {@link Mouse#up Mouse.up()}.
* Shortcut for {@link com.microsoft.playwright.Mouse#move Mouse.move()}, {@link com.microsoft.playwright.Mouse#down
* Mouse.down()}, {@link com.microsoft.playwright.Mouse#up Mouse.up()}.
*
* @param x X coordinate relative to the main frame's viewport in CSS pixels.
* @param y Y coordinate relative to the main frame's viewport in CSS pixels.
* @since v1.8
*/
void click(double x, double y, ClickOptions options);
/**
* Shortcut for {@link Mouse#move Mouse.move()}, {@link Mouse#down Mouse.down()}, {@link Mouse#up Mouse.up()}, {@link
* Mouse#down Mouse.down()} and {@link Mouse#up Mouse.up()}.
* Shortcut for {@link com.microsoft.playwright.Mouse#move Mouse.move()}, {@link com.microsoft.playwright.Mouse#down
* Mouse.down()}, {@link com.microsoft.playwright.Mouse#up Mouse.up()}, {@link com.microsoft.playwright.Mouse#down
* Mouse.down()} and {@link com.microsoft.playwright.Mouse#up Mouse.up()}.
*
* @param x X coordinate relative to the main frame's viewport in CSS pixels.
* @param y Y coordinate relative to the main frame's viewport in CSS pixels.
* @since v1.8
*/
default void dblclick(double x, double y) {
dblclick(x, y, null);
}
/**
* Shortcut for {@link Mouse#move Mouse.move()}, {@link Mouse#down Mouse.down()}, {@link Mouse#up Mouse.up()}, {@link
* Mouse#down Mouse.down()} and {@link Mouse#up Mouse.up()}.
* Shortcut for {@link com.microsoft.playwright.Mouse#move Mouse.move()}, {@link com.microsoft.playwright.Mouse#down
* Mouse.down()}, {@link com.microsoft.playwright.Mouse#up Mouse.up()}, {@link com.microsoft.playwright.Mouse#down
* Mouse.down()} and {@link com.microsoft.playwright.Mouse#up Mouse.up()}.
*
* @param x X coordinate relative to the main frame's viewport in CSS pixels.
* @param y Y coordinate relative to the main frame's viewport in CSS pixels.
* @since v1.8
*/
void dblclick(double x, double y, DblclickOptions options);
@@ -206,6 +218,8 @@ public interface Mouse {
/**
* Dispatches a {@code mousemove} event.
*
* @param x X coordinate relative to the main frame's viewport in CSS pixels.
* @param y Y coordinate relative to the main frame's viewport in CSS pixels.
* @since v1.8
*/
default void move(double x, double y) {
@@ -214,6 +228,8 @@ public interface Mouse {
/**
* Dispatches a {@code mousemove} event.
*
* @param x X coordinate relative to the main frame's viewport in CSS pixels.
* @param y Y coordinate relative to the main frame's viewport in CSS pixels.
* @since v1.8
*/
void move(double x, double y, MoveOptions options);
@@ -232,7 +248,8 @@ public interface Mouse {
*/
void up(UpOptions options);
/**
* Dispatches a {@code wheel} event.
* Dispatches a {@code wheel} event. This method is usually used to manually scroll the page. See <a
* href="https://playwright.dev/java/docs/input#scrolling">scrolling</a> for alternative ways to scroll.
*
* <p> <strong>NOTE:</strong> Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling to finish
* before returning.
File diff suppressed because it is too large Load Diff
@@ -94,8 +94,8 @@ public interface Playwright extends AutoCloseable {
*/
void close();
/**
* Launches new Playwright driver process and connects to it. {@link Playwright#close Playwright.close()} should be called
* when the instance is no longer needed.
* Launches new Playwright driver process and connects to it. {@link com.microsoft.playwright.Playwright#close
* Playwright.close()} should be called when the instance is no longer needed.
* <pre>{@code
* Playwright playwright = Playwright.create();
* Browser browser = playwright.webkit().launch();
@@ -22,14 +22,15 @@ import java.util.*;
/**
* Whenever the page sends a request for a network resource the following sequence of events are emitted by {@code Page}:
* <ul>
* <li> {@link Page#onRequest Page.onRequest()} emitted when the request is issued by the page.</li>
* <li> {@link Page#onResponse Page.onResponse()} emitted when/if the response status and headers are received for the request.</li>
* <li> {@link Page#onRequestFinished Page.onRequestFinished()} emitted when the response body is downloaded and the request is
* complete.</li>
* <li> {@link com.microsoft.playwright.Page#onRequest Page.onRequest()} emitted when the request is issued by the page.</li>
* <li> {@link com.microsoft.playwright.Page#onResponse Page.onResponse()} emitted when/if the response status and headers are
* received for the request.</li>
* <li> {@link com.microsoft.playwright.Page#onRequestFinished Page.onRequestFinished()} emitted when the response body is
* downloaded and the request is complete.</li>
* </ul>
*
* <p> If request fails at some point, then instead of {@code "requestfinished"} event (and possibly instead of 'response'
* event), the {@link Page#onRequestFailed Page.onRequestFailed()} event is emitted.
* event), the {@link com.microsoft.playwright.Page#onRequestFailed Page.onRequestFailed()} event is emitted.
*
* <p> <strong>NOTE:</strong> HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will complete
* with {@code "requestfinished"} event.
@@ -47,7 +48,7 @@ public interface Request {
/**
* The method returns {@code null} unless this request has failed, as reported by {@code requestfailed} event.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> Example of logging of all the failed requests:
* <pre>{@code
@@ -62,18 +63,18 @@ public interface Request {
/**
* Returns the {@code Frame} that initiated this request.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* String frameUrl = request.frame().url();
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Note that in some cases the frame is not available, and this method will throw.
* <ul>
* <li> When request originates in the Service Worker. You can use {@code request.serviceWorker()} to check that.</li>
* <li> When navigation request is issued before the corresponding frame is created. You can use {@link
* Request#isNavigationRequest Request.isNavigationRequest()} to check that.</li>
* com.microsoft.playwright.Request#isNavigationRequest Request.isNavigationRequest()} to check that.</li>
* </ul>
*
* <p> Here is an example that handles all the cases:
@@ -83,22 +84,22 @@ public interface Request {
Frame frame();
/**
* An object with the request HTTP headers. The header names are lower-cased. Note that this method does not return
* security-related headers, including cookie-related ones. You can use {@link Request#allHeaders Request.allHeaders()} for
* complete list of headers that include {@code cookie} information.
* security-related headers, including cookie-related ones. You can use {@link com.microsoft.playwright.Request#allHeaders
* Request.allHeaders()} for complete list of headers that include {@code cookie} information.
*
* @since v1.8
*/
Map<String, String> headers();
/**
* An array with all the request HTTP headers associated with this request. Unlike {@link Request#allHeaders
* Request.allHeaders()}, header names are NOT lower-cased. Headers with multiple entries, such as {@code Set-Cookie},
* appear in the array multiple times.
* An array with all the request HTTP headers associated with this request. Unlike {@link
* com.microsoft.playwright.Request#allHeaders Request.allHeaders()}, header names are NOT lower-cased. Headers with
* multiple entries, such as {@code Set-Cookie}, appear in the array multiple times.
*
* @since v1.15
*/
List<HttpHeader> headersArray();
/**
* Returns the value of the header matching the name. The name is case insensitive.
* Returns the value of the header matching the name. The name is case-insensitive.
*
* @param name Name of the header.
* @since v1.15
@@ -108,7 +109,7 @@ public interface Request {
* Whether this request is driving frame's navigation.
*
* <p> Some navigation requests are issued before the corresponding frame is created, and therefore do not have {@link
* Request#frame Request.frame()} available.
* com.microsoft.playwright.Request#frame Request.frame()} available.
*
* @since v1.8
*/
@@ -138,7 +139,7 @@ public interface Request {
* connected by {@code redirectedFrom()} and {@code redirectedTo()} methods. When multiple server redirects has happened,
* it is possible to construct the whole redirect chain by repeatedly calling {@code redirectedFrom()}.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> For example, if the website {@code http://example.com} redirects to {@code https://example.com}:
* <pre>{@code
@@ -158,9 +159,9 @@ public interface Request {
/**
* New request issued by the browser if the server responded with redirect.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> This method is the opposite of {@link Request#redirectedFrom Request.redirectedFrom()}:
* <p> This method is the opposite of {@link com.microsoft.playwright.Request#redirectedFrom Request.redirectedFrom()}:
* <pre>{@code
* System.out.println(request.redirectedFrom().redirectedTo() == request); // true
* }</pre>
@@ -193,7 +194,7 @@ public interface Request {
* {@code responseEnd} becomes available when request finishes. Find more information at <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming">Resource Timing API</a>.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* page.onRequestFinished(request -> {
* Timing timing = request.timing();
@@ -56,22 +56,22 @@ public interface Response {
boolean fromServiceWorker();
/**
* An object with the response HTTP headers. The header names are lower-cased. Note that this method does not return
* security-related headers, including cookie-related ones. You can use {@link Response#allHeaders Response.allHeaders()}
* for complete list of headers that include {@code cookie} information.
* security-related headers, including cookie-related ones. You can use {@link com.microsoft.playwright.Response#allHeaders
* Response.allHeaders()} for complete list of headers that include {@code cookie} information.
*
* @since v1.8
*/
Map<String, String> headers();
/**
* An array with all the request HTTP headers associated with this response. Unlike {@link Response#allHeaders
* Response.allHeaders()}, header names are NOT lower-cased. Headers with multiple entries, such as {@code Set-Cookie},
* appear in the array multiple times.
* An array with all the request HTTP headers associated with this response. Unlike {@link
* com.microsoft.playwright.Response#allHeaders Response.allHeaders()}, header names are NOT lower-cased. Headers with
* multiple entries, such as {@code Set-Cookie}, appear in the array multiple times.
*
* @since v1.15
*/
List<HttpHeader> headersArray();
/**
* Returns the value of the header matching the name. The name is case insensitive. If multiple headers have the same name
* Returns the value of the header matching the name. The name is case-insensitive. If multiple headers have the same name
* (except {@code set-cookie}), they are returned as a list separated by {@code , }. For {@code set-cookie}, the {@code \n}
* separator is used. If no headers are found, {@code null} is returned.
*
@@ -80,7 +80,7 @@ public interface Response {
*/
String headerValue(String name);
/**
* Returns all values of the headers matching the name, for example {@code set-cookie}. The name is case insensitive.
* Returns all values of the headers matching the name, for example {@code set-cookie}. The name is case-insensitive.
*
* @param name Name of the header.
* @since v1.15
@@ -20,8 +20,9 @@ import java.nio.file.Path;
import java.util.*;
/**
* Whenever a network route is set up with {@link Page#route Page.route()} or {@link BrowserContext#route
* BrowserContext.route()}, the {@code Route} object allows to handle the route.
* Whenever a network route is set up with {@link com.microsoft.playwright.Page#route Page.route()} or {@link
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}, the {@code Route} object allows to handle the
* route.
*
* <p> Learn more about <a href="https://playwright.dev/java/docs/network">networking</a>.
*/
@@ -146,6 +147,12 @@ public interface Route {
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects.
*/
public Integer maxRedirects;
/**
* Maximum number of times network errors should be retried. Currently only {@code ECONNRESET} error is retried. Does not
* retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to {@code 0} - no
* retries.
*/
public Integer maxRetries;
/**
* If set changes the request method (e.g. GET or POST).
*/
@@ -178,6 +185,15 @@ public interface Route {
this.maxRedirects = maxRedirects;
return this;
}
/**
* Maximum number of times network errors should be retried. Currently only {@code ECONNRESET} error is retried. Does not
* retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to {@code 0} - no
* retries.
*/
public FetchOptions setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
return this;
}
/**
* If set changes the request method (e.g. GET or POST).
*/
@@ -332,9 +348,9 @@ public interface Route {
*/
void abort(String errorCode);
/**
* Continues route's request with optional overrides.
* Sends route's request to the network with optional overrides.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* page.route("**\/*", route -> {
* // Override headers
@@ -345,12 +361,18 @@ public interface Route {
* });
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request
* results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header
* through redirects, use the combination of {@link Route#fetch Route.fetch()} and {@link Route#fulfill Route.fulfill()}
* instead.
* <p> The {@code headers} option applies to both the routed request and any redirects it initiates. However, {@code url},
* {@code method}, and {@code postData} only apply to the original request and are not carried over to redirected requests.
*
* <p> {@link com.microsoft.playwright.Route#resume Route.resume()} will immediately send the request to the network, other
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
* next matching handler in the chain to be invoked.
*
* <p> <strong>NOTE:</strong> The {@code Cookie} header cannot be overridden using this method. If a value is provided, it will be ignored, and the
* cookie will be loaded from the browser's cookie store. To set custom cookies, use {@link
* com.microsoft.playwright.BrowserContext#addCookies BrowserContext.addCookies()}.
*
* @since v1.8
*/
@@ -358,9 +380,9 @@ public interface Route {
resume(null);
}
/**
* Continues route's request with optional overrides.
* Sends route's request to the network with optional overrides.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* page.route("**\/*", route -> {
* // Override headers
@@ -371,23 +393,32 @@ public interface Route {
* });
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request
* results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header
* through redirects, use the combination of {@link Route#fetch Route.fetch()} and {@link Route#fulfill Route.fulfill()}
* instead.
* <p> The {@code headers} option applies to both the routed request and any redirects it initiates. However, {@code url},
* {@code method}, and {@code postData} only apply to the original request and are not carried over to redirected requests.
*
* <p> {@link com.microsoft.playwright.Route#resume Route.resume()} will immediately send the request to the network, other
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
* next matching handler in the chain to be invoked.
*
* <p> <strong>NOTE:</strong> The {@code Cookie} header cannot be overridden using this method. If a value is provided, it will be ignored, and the
* cookie will be loaded from the browser's cookie store. To set custom cookies, use {@link
* com.microsoft.playwright.BrowserContext#addCookies BrowserContext.addCookies()}.
*
* @since v1.8
*/
void resume(ResumeOptions options);
/**
* When several routes match the given pattern, they run in the order opposite to their registration. That way the last
* Continues route's request with optional overrides. The method is similar to {@link com.microsoft.playwright.Route#resume
* Route.resume()} with the difference that other matching handlers will be invoked before sending the request.
*
* <p> <strong>Usage</strong>
*
* <p> When several routes match the given pattern, they run in the order opposite to their registration. That way the last
* registered route can always override all the previous ones. In the example below, request will be handled by the
* bottom-most handler first, then it'll fall back to the previous one and in the end will be aborted by the first
* registered route.
*
* <p> **Usage**
* <pre>{@code
* page.route("**\/*", route -> {
* // Runs last.
@@ -441,18 +472,24 @@ public interface Route {
* });
* }</pre>
*
* <p> Use {@link com.microsoft.playwright.Route#resume Route.resume()} to immediately send the request to the network, other
* matching handlers won't be invoked in that case.
*
* @since v1.23
*/
default void fallback() {
fallback(null);
}
/**
* When several routes match the given pattern, they run in the order opposite to their registration. That way the last
* Continues route's request with optional overrides. The method is similar to {@link com.microsoft.playwright.Route#resume
* Route.resume()} with the difference that other matching handlers will be invoked before sending the request.
*
* <p> <strong>Usage</strong>
*
* <p> When several routes match the given pattern, they run in the order opposite to their registration. That way the last
* registered route can always override all the previous ones. In the example below, request will be handled by the
* bottom-most handler first, then it'll fall back to the previous one and in the end will be aborted by the first
* registered route.
*
* <p> **Usage**
* <pre>{@code
* page.route("**\/*", route -> {
* // Runs last.
@@ -506,6 +543,9 @@ public interface Route {
* });
* }</pre>
*
* <p> Use {@link com.microsoft.playwright.Route#resume Route.resume()} to immediately send the request to the network, other
* matching handlers won't be invoked in that case.
*
* @since v1.23
*/
void fallback(FallbackOptions options);
@@ -513,7 +553,7 @@ public interface Route {
* Performs the request and fetches result without fulfilling it, so that the response could be modified and then
* fulfilled.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* page.route("https://dog.ceo/api/breeds/list/all", route -> {
* APIResponse response = route.fetch();
@@ -526,11 +566,11 @@ public interface Route {
* });
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Note that {@code headers} option will apply to the fetched request as well as any redirects initiated by it. If you want
* to only apply {@code headers} to the original request, but not to redirects, look into {@link Route#resume
* Route.resume()} instead.
* to only apply {@code headers} to the original request, but not to redirects, look into {@link
* com.microsoft.playwright.Route#resume Route.resume()} instead.
*
* @since v1.29
*/
@@ -541,7 +581,7 @@ public interface Route {
* Performs the request and fetches result without fulfilling it, so that the response could be modified and then
* fulfilled.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* page.route("https://dog.ceo/api/breeds/list/all", route -> {
* APIResponse response = route.fetch();
@@ -554,11 +594,11 @@ public interface Route {
* });
* }</pre>
*
* <p> **Details**
* <p> <strong>Details</strong>
*
* <p> Note that {@code headers} option will apply to the fetched request as well as any redirects initiated by it. If you want
* to only apply {@code headers} to the original request, but not to redirects, look into {@link Route#resume
* Route.resume()} instead.
* to only apply {@code headers} to the original request, but not to redirects, look into {@link
* com.microsoft.playwright.Route#resume Route.resume()} instead.
*
* @since v1.29
*/
@@ -566,7 +606,7 @@ public interface Route {
/**
* Fulfills route's request with given response.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> An example of fulfilling all requests with 404 responses:
* <pre>{@code
@@ -592,7 +632,7 @@ public interface Route {
/**
* Fulfills route's request with given response.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> An example of fulfilling all requests with 404 responses:
* <pre>{@code
@@ -44,7 +44,7 @@ public interface Selectors {
/**
* Selectors must be registered before creating the page.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> An example of registering selector engine that queries elements based on a tag name:
* <pre>{@code
@@ -84,7 +84,7 @@ public interface Selectors {
/**
* Selectors must be registered before creating the page.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> An example of registering selector engine that queries elements based on a tag name:
* <pre>{@code
@@ -122,7 +122,7 @@ public interface Selectors {
/**
* Selectors must be registered before creating the page.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> An example of registering selector engine that queries elements based on a tag name:
* <pre>{@code
@@ -162,7 +162,7 @@ public interface Selectors {
/**
* Selectors must be registered before creating the page.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
*
* <p> An example of registering selector engine that queries elements based on a tag name:
* <pre>{@code
@@ -198,8 +198,8 @@ public interface Selectors {
*/
void register(String name, Path script, RegisterOptions options);
/**
* Defines custom attribute name to be used in {@link Page#getByTestId Page.getByTestId()}. {@code data-testid} is used by
* default.
* Defines custom attribute name to be used in {@link com.microsoft.playwright.Page#getByTestId Page.getByTestId()}. {@code
* data-testid} is used by default.
*
* @param attributeName Test id attribute name.
* @since v1.27
@@ -20,13 +20,19 @@ package com.microsoft.playwright;
/**
* The Touchscreen class operates in main-frame CSS pixels relative to the top-left corner of the viewport. Methods on the
* touchscreen can only be used in browser contexts that have been initialized with {@code hasTouch} set to true.
*
* <p> This class is limited to emulating tap gestures. For examples of other gestures simulated by manually dispatching touch
* events, see the <a href="https://playwright.dev/java/docs/touch-events">emulating legacy touch events</a> page.
*/
public interface Touchscreen {
/**
* Dispatches a {@code touchstart} and {@code touchend} event with a single touch at the position ({@code x},{@code y}).
*
* <p> <strong>NOTE:</strong> {@link Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser context is false.
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser
* context is false.
*
* @param x X coordinate relative to the main frame's viewport in CSS pixels.
* @param y Y coordinate relative to the main frame's viewport in CSS pixels.
* @since v1.8
*/
void tap(double x, double y);
@@ -16,6 +16,7 @@
package com.microsoft.playwright;
import com.microsoft.playwright.options.*;
import java.nio.file.Path;
/**
@@ -38,8 +39,10 @@ import java.nio.file.Path;
public interface Tracing {
class StartOptions {
/**
* If specified, the trace is going to be saved into the file with the given name inside the {@code tracesDir} folder
* specified in {@link BrowserType#launch BrowserType.launch()}.
* If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the {@code
* tracesDir} directory specified in {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}. To specify
* the final trace zip file name, you need to pass {@code path} option to {@link com.microsoft.playwright.Tracing#stop
* Tracing.stop()} instead.
*/
public String name;
/**
@@ -66,8 +69,10 @@ public interface Tracing {
public String title;
/**
* If specified, the trace is going to be saved into the file with the given name inside the {@code tracesDir} folder
* specified in {@link BrowserType#launch BrowserType.launch()}.
* If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the {@code
* tracesDir} directory specified in {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}. To specify
* the final trace zip file name, you need to pass {@code path} option to {@link com.microsoft.playwright.Tracing#stop
* Tracing.stop()} instead.
*/
public StartOptions setName(String name) {
this.name = name;
@@ -110,8 +115,10 @@ public interface Tracing {
}
class StartChunkOptions {
/**
* If specified, the trace is going to be saved into the file with the given name inside the {@code tracesDir} folder
* specified in {@link BrowserType#launch BrowserType.launch()}.
* If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the {@code
* tracesDir} directory specified in {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}. To specify
* the final trace zip file name, you need to pass {@code path} option to {@link com.microsoft.playwright.Tracing#stopChunk
* Tracing.stopChunk()} instead.
*/
public String name;
/**
@@ -120,8 +127,10 @@ public interface Tracing {
public String title;
/**
* If specified, the trace is going to be saved into the file with the given name inside the {@code tracesDir} folder
* specified in {@link BrowserType#launch BrowserType.launch()}.
* If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the {@code
* tracesDir} directory specified in {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}. To specify
* the final trace zip file name, you need to pass {@code path} option to {@link com.microsoft.playwright.Tracing#stopChunk
* Tracing.stopChunk()} instead.
*/
public StartChunkOptions setName(String name) {
this.name = name;
@@ -135,6 +144,29 @@ public interface Tracing {
return this;
}
}
class GroupOptions {
/**
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
*/
public Location location;
/**
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
*/
public GroupOptions setLocation(String file) {
return setLocation(new Location(file));
}
/**
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
*/
public GroupOptions setLocation(Location location) {
this.location = location;
return this;
}
}
class StopOptions {
/**
* Export trace into the file with the given path.
@@ -151,14 +183,14 @@ public interface Tracing {
}
class StopChunkOptions {
/**
* Export trace collected since the last {@link Tracing#startChunk Tracing.startChunk()} call into the file with the given
* path.
* Export trace collected since the last {@link com.microsoft.playwright.Tracing#startChunk Tracing.startChunk()} call into
* the file with the given path.
*/
public Path path;
/**
* Export trace collected since the last {@link Tracing#startChunk Tracing.startChunk()} call into the file with the given
* path.
* Export trace collected since the last {@link com.microsoft.playwright.Tracing#startChunk Tracing.startChunk()} call into
* the file with the given path.
*/
public StopChunkOptions setPath(Path path) {
this.path = path;
@@ -168,7 +200,7 @@ public interface Tracing {
/**
* Start tracing.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* context.tracing().start(new Tracing.StartOptions()
* .setScreenshots(true)
@@ -187,7 +219,7 @@ public interface Tracing {
/**
* Start tracing.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* context.tracing().start(new Tracing.StartOptions()
* .setScreenshots(true)
@@ -203,10 +235,11 @@ public interface Tracing {
void start(StartOptions options);
/**
* Start a new trace chunk. If you'd like to record multiple traces on the same {@code BrowserContext}, use {@link
* Tracing#start Tracing.start()} once, and then create multiple trace chunks with {@link Tracing#startChunk
* Tracing.startChunk()} and {@link Tracing#stopChunk Tracing.stopChunk()}.
* com.microsoft.playwright.Tracing#start Tracing.start()} once, and then create multiple trace chunks with {@link
* com.microsoft.playwright.Tracing#startChunk Tracing.startChunk()} and {@link com.microsoft.playwright.Tracing#stopChunk
* Tracing.stopChunk()}.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* context.tracing().start(new Tracing.StartOptions()
* .setScreenshots(true)
@@ -234,10 +267,11 @@ public interface Tracing {
}
/**
* Start a new trace chunk. If you'd like to record multiple traces on the same {@code BrowserContext}, use {@link
* Tracing#start Tracing.start()} once, and then create multiple trace chunks with {@link Tracing#startChunk
* Tracing.startChunk()} and {@link Tracing#stopChunk Tracing.stopChunk()}.
* com.microsoft.playwright.Tracing#start Tracing.start()} once, and then create multiple trace chunks with {@link
* com.microsoft.playwright.Tracing#startChunk Tracing.startChunk()} and {@link com.microsoft.playwright.Tracing#stopChunk
* Tracing.stopChunk()}.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* context.tracing().start(new Tracing.StartOptions()
* .setScreenshots(true)
@@ -261,6 +295,56 @@ public interface Tracing {
* @since v1.15
*/
void startChunk(StartChunkOptions options);
/**
* <strong>NOTE:</strong> Use {@code test.step} instead when available.
*
* <p> Creates a new group within the trace, assigning any subsequent API calls to this group, until {@link
* com.microsoft.playwright.Tracing#groupEnd Tracing.groupEnd()} is called. Groups can be nested and will be visible in the
* trace viewer.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* // All actions between group and groupEnd
* // will be shown in the trace viewer as a group.
* page.context().tracing().group("Open Playwright.dev > API");
* page.navigate("https://playwright.dev/");
* page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
* page.context().tracing().groupEnd();
* }</pre>
*
* @param name Group name shown in the trace viewer.
* @since v1.49
*/
default void group(String name) {
group(name, null);
}
/**
* <strong>NOTE:</strong> Use {@code test.step} instead when available.
*
* <p> Creates a new group within the trace, assigning any subsequent API calls to this group, until {@link
* com.microsoft.playwright.Tracing#groupEnd Tracing.groupEnd()} is called. Groups can be nested and will be visible in the
* trace viewer.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* // All actions between group and groupEnd
* // will be shown in the trace viewer as a group.
* page.context().tracing().group("Open Playwright.dev > API");
* page.navigate("https://playwright.dev/");
* page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
* page.context().tracing().groupEnd();
* }</pre>
*
* @param name Group name shown in the trace viewer.
* @since v1.49
*/
void group(String name, GroupOptions options);
/**
* Closes the last group created by {@link com.microsoft.playwright.Tracing#group Tracing.group()}.
*
* @since v1.49
*/
void groupEnd();
/**
* Stop tracing.
*
@@ -276,7 +360,8 @@ public interface Tracing {
*/
void stop(StopOptions options);
/**
* Stop the trace chunk. See {@link Tracing#startChunk Tracing.startChunk()} for more details about multiple trace chunks.
* Stop the trace chunk. See {@link com.microsoft.playwright.Tracing#startChunk Tracing.startChunk()} for more details
* about multiple trace chunks.
*
* @since v1.15
*/
@@ -284,7 +369,8 @@ public interface Tracing {
stopChunk(null);
}
/**
* Stop the trace chunk. See {@link Tracing#startChunk Tracing.startChunk()} for more details about multiple trace chunks.
* Stop the trace chunk. See {@link com.microsoft.playwright.Tracing#startChunk Tracing.startChunk()} for more details
* about multiple trace chunks.
*
* @since v1.15
*/
@@ -19,7 +19,7 @@ package com.microsoft.playwright;
/**
* {@code WebError} class represents an unhandled exception thrown in the page. It is dispatched via the {@link
* BrowserContext#onWebError BrowserContext.onWebError()} event.
* com.microsoft.playwright.BrowserContext#onWebError BrowserContext.onWebError()} event.
* <pre>{@code
* // Log all uncaught errors to the terminal
* context.onWebError(webError -> {
@@ -20,7 +20,10 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* The {@code WebSocket} class represents websocket connections in the page.
* The {@code WebSocket} class represents WebSocket connections within a page. It provides the ability to inspect and
* manipulate the data being transmitted and received.
*
* <p> If you want to intercept or modify WebSocket frames, consider using {@code WebSocketRoute}.
*/
public interface WebSocket {
@@ -67,7 +70,8 @@ public interface WebSocket {
public Predicate<WebSocketFrame> predicate;
/**
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The
* default value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
* default value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()}.
*/
public Double timeout;
@@ -80,7 +84,8 @@ public interface WebSocket {
}
/**
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The
* default value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
* default value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()}.
*/
public WaitForFrameReceivedOptions setTimeout(double timeout) {
this.timeout = timeout;
@@ -94,7 +99,8 @@ public interface WebSocket {
public Predicate<WebSocketFrame> predicate;
/**
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The
* default value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
* default value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()}.
*/
public Double timeout;
@@ -107,7 +113,8 @@ public interface WebSocket {
}
/**
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The
* default value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
* default value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()}.
*/
public WaitForFrameSentOptions setTimeout(double timeout) {
this.timeout = timeout;
@@ -19,8 +19,8 @@ package com.microsoft.playwright;
/**
* The {@code WebSocketFrame} class represents frames sent over {@code WebSocket} connections in the page. Frame payload is
* returned by either {@link WebSocketFrame#text WebSocketFrame.text()} or {@link WebSocketFrame#binary
* WebSocketFrame.binary()} method depending on the its type.
* returned by either {@link com.microsoft.playwright.WebSocketFrame#text WebSocketFrame.text()} or {@link
* com.microsoft.playwright.WebSocketFrame#binary WebSocketFrame.binary()} method depending on the its type.
*/
public interface WebSocketFrame {
/**
@@ -0,0 +1,223 @@
/*
* 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 java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* Whenever a <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">{@code WebSocket}</a> route is set up
* with {@link com.microsoft.playwright.Page#routeWebSocket Page.routeWebSocket()} or {@link
* com.microsoft.playwright.BrowserContext#routeWebSocket BrowserContext.routeWebSocket()}, the {@code WebSocketRoute}
* object allows to handle the WebSocket, like an actual server would do.
*
* <p> <strong>Mocking</strong>
*
* <p> By default, the routed WebSocket will not connect to the server. This way, you can mock entire communcation over the
* WebSocket. Here is an example that responds to a {@code "request"} with a {@code "response"}.
* <pre>{@code
* page.routeWebSocket("wss://example.com/ws", ws -> {
* ws.onMessage(frame -> {
* if ("request".equals(frame.text()))
* ws.send("response");
* });
* });
* }</pre>
*
* <p> Since we do not call {@link com.microsoft.playwright.WebSocketRoute#connectToServer WebSocketRoute.connectToServer()}
* inside the WebSocket route handler, Playwright assumes that WebSocket will be mocked, and opens the WebSocket inside the
* page automatically.
*
* <p> Here is another example that handles JSON messages:
* <pre>{@code
* page.routeWebSocket("wss://example.com/ws", ws -> {
* ws.onMessage(frame -> {
* JsonObject json = new JsonParser().parse(frame.text()).getAsJsonObject();
* if ("question".equals(json.get("request").getAsString())) {
* Map<String, String> result = new HashMap();
* result.put("response", "answer");
* ws.send(gson.toJson(result));
* }
* });
* });
* }</pre>
*
* <p> <strong>Intercepting</strong>
*
* <p> Alternatively, you may want to connect to the actual server, but intercept messages in-between and modify or block them.
* Calling {@link com.microsoft.playwright.WebSocketRoute#connectToServer WebSocketRoute.connectToServer()} returns a
* server-side {@code WebSocketRoute} instance that you can send messages to, or handle incoming messages.
*
* <p> Below is an example that modifies some messages sent by the page to the server. Messages sent from the server to the
* page are left intact, relying on the default forwarding.
* <pre>{@code
* page.routeWebSocket("/ws", ws -> {
* WebSocketRoute server = ws.connectToServer();
* ws.onMessage(frame -> {
* if ("request".equals(frame.text()))
* server.send("request2");
* else
* server.send(frame.text());
* });
* });
* }</pre>
*
* <p> After connecting to the server, all **messages are forwarded** between the page and the server by default.
*
* <p> However, if you call {@link com.microsoft.playwright.WebSocketRoute#onMessage WebSocketRoute.onMessage()} on the
* original route, messages from the page to the server **will not be forwarded** anymore, but should instead be handled by
* the {@code handler}.
*
* <p> Similarly, calling {@link com.microsoft.playwright.WebSocketRoute#onMessage WebSocketRoute.onMessage()} on the
* server-side WebSocket will **stop forwarding messages** from the server to the page, and {@code handler} should take
* care of them.
*
* <p> The following example blocks some messages in both directions. Since it calls {@link
* com.microsoft.playwright.WebSocketRoute#onMessage WebSocketRoute.onMessage()} in both directions, there is no automatic
* forwarding at all.
* <pre>{@code
* page.routeWebSocket("/ws", ws -> {
* WebSocketRoute server = ws.connectToServer();
* ws.onMessage(frame -> {
* if (!"blocked-from-the-page".equals(frame.text()))
* server.send(frame.text());
* });
* server.onMessage(frame -> {
* if (!"blocked-from-the-server".equals(frame.text()))
* ws.send(frame.text());
* });
* });
* }</pre>
*/
public interface WebSocketRoute {
class CloseOptions {
/**
* Optional <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code">close code</a>.
*/
public Integer code;
/**
* Optional <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason">close reason</a>.
*/
public String reason;
/**
* Optional <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code">close code</a>.
*/
public CloseOptions setCode(int code) {
this.code = code;
return this;
}
/**
* Optional <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason">close reason</a>.
*/
public CloseOptions setReason(String reason) {
this.reason = reason;
return this;
}
}
/**
* Closes one side of the WebSocket connection.
*
* @since v1.48
*/
default void close() {
close(null);
}
/**
* Closes one side of the WebSocket connection.
*
* @since v1.48
*/
void close(CloseOptions options);
/**
* By default, routed WebSocket does not connect to the server, so you can mock entire WebSocket communication. This method
* connects to the actual WebSocket server, and returns the server-side {@code WebSocketRoute} instance, giving the ability
* to send and receive messages from the server.
*
* <p> Once connected to the server:
* <ul>
* <li> Messages received from the server will be **automatically forwarded** to the WebSocket in the page, unless {@link
* com.microsoft.playwright.WebSocketRoute#onMessage WebSocketRoute.onMessage()} is called on the server-side {@code
* WebSocketRoute}.</li>
* <li> Messages sent by the <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send">{@code
* WebSocket.send()}</a> call in the page will be **automatically forwarded** to the server, unless {@link
* com.microsoft.playwright.WebSocketRoute#onMessage WebSocketRoute.onMessage()} is called on the original {@code
* WebSocketRoute}.</li>
* </ul>
*
* <p> See examples at the top for more details.
*
* @since v1.48
*/
WebSocketRoute connectToServer();
/**
* Allows to handle <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close">{@code WebSocket.close}</a>.
*
* <p> By default, closing one side of the connection, either in the page or on the server, will close the other side. However,
* when {@link com.microsoft.playwright.WebSocketRoute#onClose WebSocketRoute.onClose()} handler is set up, the default
* forwarding of closure is disabled, and handler should take care of it.
*
* @param handler Function that will handle WebSocket closure. Received an optional <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code">close code</a> and an optional <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason">close reason</a>.
* @since v1.48
*/
void onClose(BiConsumer<Integer, String> handler);
/**
* This method allows to handle messages that are sent by the WebSocket, either from the page or from the server.
*
* <p> When called on the original WebSocket route, this method handles messages sent from the page. You can handle this
* messages by responding to them with {@link com.microsoft.playwright.WebSocketRoute#send WebSocketRoute.send()},
* forwarding them to the server-side connection returned by {@link com.microsoft.playwright.WebSocketRoute#connectToServer
* WebSocketRoute.connectToServer()} or do something else.
*
* <p> Once this method is called, messages are not automatically forwarded to the server or to the page - you should do that
* manually by calling {@link com.microsoft.playwright.WebSocketRoute#send WebSocketRoute.send()}. See examples at the top
* for more details.
*
* <p> Calling this method again will override the handler with a new one.
*
* @param handler Function that will handle messages.
* @since v1.48
*/
void onMessage(Consumer<WebSocketFrame> handler);
/**
* Sends a message to the WebSocket. When called on the original WebSocket, sends the message to the page. When called on
* the result of {@link com.microsoft.playwright.WebSocketRoute#connectToServer WebSocketRoute.connectToServer()}, sends
* the message to the server. See examples at the top for more details.
*
* @param message Message to send.
* @since v1.48
*/
void send(String message);
/**
* Sends a message to the WebSocket. When called on the original WebSocket, sends the message to the page. When called on
* the result of {@link com.microsoft.playwright.WebSocketRoute#connectToServer WebSocketRoute.connectToServer()}, sends
* the message to the server. See examples at the top for more details.
*
* @param message Message to send.
* @since v1.48
*/
void send(byte[] message);
/**
* URL of the WebSocket created in the page.
*
* @since v1.48
*/
String url();
}
@@ -47,13 +47,15 @@ public interface Worker {
class WaitForCloseOptions {
/**
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The
* default value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
* default value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()}.
*/
public Double timeout;
/**
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The
* default value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
* default value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()}.
*/
public WaitForCloseOptions setTimeout(double timeout) {
this.timeout = timeout;
@@ -63,13 +65,14 @@ public interface Worker {
/**
* Returns the return value of {@code expression}.
*
* <p> If the function passed to the {@link Worker#evaluate Worker.evaluate()} returns a <a
* <p> If the function passed to the {@link com.microsoft.playwright.Worker#evaluate Worker.evaluate()} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@link
* Worker#evaluate Worker.evaluate()} would wait for the promise to resolve and return its value.
* com.microsoft.playwright.Worker#evaluate Worker.evaluate()} would wait for the promise to resolve and return its value.
*
* <p> If the function passed to the {@link Worker#evaluate Worker.evaluate()} returns a non-[Serializable] value, then {@link
* Worker#evaluate Worker.evaluate()} returns {@code undefined}. Playwright also supports transferring some additional
* values that are not serializable by {@code JSON}: {@code -0}, {@code NaN}, {@code Infinity}, {@code -Infinity}.
* <p> If the function passed to the {@link com.microsoft.playwright.Worker#evaluate Worker.evaluate()} returns a
* non-[Serializable] value, then {@link com.microsoft.playwright.Worker#evaluate Worker.evaluate()} returns {@code
* undefined}. Playwright also supports transferring some additional values that are not serializable by {@code JSON}:
* {@code -0}, {@code NaN}, {@code Infinity}, {@code -Infinity}.
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
@@ -81,13 +84,14 @@ public interface Worker {
/**
* Returns the return value of {@code expression}.
*
* <p> If the function passed to the {@link Worker#evaluate Worker.evaluate()} returns a <a
* <p> If the function passed to the {@link com.microsoft.playwright.Worker#evaluate Worker.evaluate()} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@link
* Worker#evaluate Worker.evaluate()} would wait for the promise to resolve and return its value.
* com.microsoft.playwright.Worker#evaluate Worker.evaluate()} would wait for the promise to resolve and return its value.
*
* <p> If the function passed to the {@link Worker#evaluate Worker.evaluate()} returns a non-[Serializable] value, then {@link
* Worker#evaluate Worker.evaluate()} returns {@code undefined}. Playwright also supports transferring some additional
* values that are not serializable by {@code JSON}: {@code -0}, {@code NaN}, {@code Infinity}, {@code -Infinity}.
* <p> If the function passed to the {@link com.microsoft.playwright.Worker#evaluate Worker.evaluate()} returns a
* non-[Serializable] value, then {@link com.microsoft.playwright.Worker#evaluate Worker.evaluate()} returns {@code
* undefined}. Playwright also supports transferring some additional values that are not serializable by {@code JSON}:
* {@code -0}, {@code NaN}, {@code Infinity}, {@code -Infinity}.
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
@@ -98,12 +102,14 @@ public interface Worker {
/**
* Returns the return value of {@code expression} as a {@code JSHandle}.
*
* <p> The only difference between {@link Worker#evaluate Worker.evaluate()} and {@link Worker#evaluateHandle
* Worker.evaluateHandle()} is that {@link Worker#evaluateHandle Worker.evaluateHandle()} returns {@code JSHandle}.
* <p> The only difference between {@link com.microsoft.playwright.Worker#evaluate Worker.evaluate()} and {@link
* com.microsoft.playwright.Worker#evaluateHandle Worker.evaluateHandle()} is that {@link
* com.microsoft.playwright.Worker#evaluateHandle Worker.evaluateHandle()} returns {@code JSHandle}.
*
* <p> If the function passed to the {@link Worker#evaluateHandle Worker.evaluateHandle()} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@link
* Worker#evaluateHandle Worker.evaluateHandle()} would wait for the promise to resolve and return its value.
* <p> If the function passed to the {@link com.microsoft.playwright.Worker#evaluateHandle Worker.evaluateHandle()} returns a
* <a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then
* {@link com.microsoft.playwright.Worker#evaluateHandle Worker.evaluateHandle()} would wait for the promise to resolve and
* return its value.
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
@@ -115,12 +121,14 @@ public interface Worker {
/**
* Returns the return value of {@code expression} as a {@code JSHandle}.
*
* <p> The only difference between {@link Worker#evaluate Worker.evaluate()} and {@link Worker#evaluateHandle
* Worker.evaluateHandle()} is that {@link Worker#evaluateHandle Worker.evaluateHandle()} returns {@code JSHandle}.
* <p> The only difference between {@link com.microsoft.playwright.Worker#evaluate Worker.evaluate()} and {@link
* com.microsoft.playwright.Worker#evaluateHandle Worker.evaluateHandle()} is that {@link
* com.microsoft.playwright.Worker#evaluateHandle Worker.evaluateHandle()} returns {@code JSHandle}.
*
* <p> If the function passed to the {@link Worker#evaluateHandle Worker.evaluateHandle()} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@link
* Worker#evaluateHandle Worker.evaluateHandle()} would wait for the promise to resolve and return its value.
* <p> If the function passed to the {@link com.microsoft.playwright.Worker#evaluateHandle Worker.evaluateHandle()} returns a
* <a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then
* {@link com.microsoft.playwright.Worker#evaluateHandle Worker.evaluateHandle()} would wait for the promise to resolve and
* return its value.
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
@@ -21,15 +21,15 @@ package com.microsoft.playwright.assertions;
* The {@code APIResponseAssertions} class provides assertion methods that can be used to make assertions about the {@code
* APIResponse} in the tests.
* <pre>{@code
* ...
* // ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
*
* public class TestPage {
* ...
* // ...
* @Test
* void navigatesToLoginPage() {
* ...
* APIResponse response = page.request().get('https://playwright.dev');
* // ...
* APIResponse response = page.request().get("https://playwright.dev");
* assertThat(response).isOK();
* }
* }
@@ -49,7 +49,7 @@ public interface APIResponseAssertions {
/**
* Ensures the response status code is within {@code 200..299} range.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(response).isOK();
* }</pre>
File diff suppressed because it is too large Load Diff
@@ -22,14 +22,14 @@ import java.util.regex.Pattern;
* The {@code PageAssertions} class provides assertion methods that can be used to make assertions about the {@code Page}
* state in the tests.
* <pre>{@code
* ...
* // ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
*
* public class TestPage {
* ...
* // ...
* @Test
* void navigatesToLoginPage() {
* ...
* // ...
* page.getByText("Sign in").click();
* assertThat(page).hasURL(Pattern.compile(".*\/login"));
* }
@@ -52,11 +52,24 @@ public interface PageAssertions {
}
}
class HasURLOptions {
/**
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
* expression parameter if specified. A provided predicate ignores this flag.
*/
public Boolean ignoreCase;
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
public Double timeout;
/**
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
* expression parameter if specified. A provided predicate ignores this flag.
*/
public HasURLOptions setIgnoreCase(boolean ignoreCase) {
this.ignoreCase = ignoreCase;
return this;
}
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
@@ -78,7 +91,7 @@ public interface PageAssertions {
/**
* Ensures the page has the given title.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page).hasTitle("Playwright");
* }</pre>
@@ -92,7 +105,7 @@ public interface PageAssertions {
/**
* Ensures the page has the given title.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page).hasTitle("Playwright");
* }</pre>
@@ -104,7 +117,7 @@ public interface PageAssertions {
/**
* Ensures the page has the given title.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page).hasTitle("Playwright");
* }</pre>
@@ -118,7 +131,7 @@ public interface PageAssertions {
/**
* Ensures the page has the given title.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page).hasTitle("Playwright");
* }</pre>
@@ -130,7 +143,7 @@ public interface PageAssertions {
/**
* Ensures the page is navigated to the given URL.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page).hasURL(".com");
* }</pre>
@@ -144,7 +157,7 @@ public interface PageAssertions {
/**
* Ensures the page is navigated to the given URL.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page).hasURL(".com");
* }</pre>
@@ -156,7 +169,7 @@ public interface PageAssertions {
/**
* Ensures the page is navigated to the given URL.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page).hasURL(".com");
* }</pre>
@@ -170,7 +183,7 @@ public interface PageAssertions {
/**
* Ensures the page is navigated to the given URL.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page).hasURL(".com");
* }</pre>
@@ -30,14 +30,13 @@ import com.microsoft.playwright.impl.PageAssertionsImpl;
*
* <p> Consider the following example:
* <pre>{@code
* ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
*
* public class TestExample {
* ...
* // ...
* @Test
* void statusBecomesSubmitted() {
* ...
* // ...
* page.locator("#submit-button").click();
* assertThat(page.locator(".status")).hasText("Submitted");
* }
@@ -54,7 +53,7 @@ public interface PlaywrightAssertions {
/**
* Creates a {@code APIResponseAssertions} object for the given {@code APIResponse}.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* PlaywrightAssertions.assertThat(response).isOK();
* }</pre>
@@ -69,7 +68,7 @@ public interface PlaywrightAssertions {
/**
* Creates a {@code LocatorAssertions} object for the given {@code Locator}.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* PlaywrightAssertions.assertThat(locator).isVisible();
* }</pre>
@@ -84,7 +83,7 @@ public interface PlaywrightAssertions {
/**
* Creates a {@code PageAssertions} object for the given {@code Page}.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* PlaywrightAssertions.assertThat(page).hasTitle("News");
* }</pre>
@@ -99,7 +98,7 @@ public interface PlaywrightAssertions {
/**
* Changes default timeout for Playwright assertions from 5 seconds to the specified value.
*
* <p> **Usage**
* <p> <strong>Usage</strong>
* <pre>{@code
* PlaywrightAssertions.setDefaultAssertionTimeout(30_000);
* }</pre>
@@ -107,8 +106,8 @@ public interface PlaywrightAssertions {
* @param timeout Timeout in milliseconds.
* @since v1.25
*/
static void setDefaultAssertionTimeout(double milliseconds) {
AssertionsTimeout.setDefaultTimeout(milliseconds);
static void setDefaultAssertionTimeout(double timeout) {
AssertionsTimeout.setDefaultTimeout(timeout);
}
}
@@ -1,7 +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.impl;
import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import com.microsoft.playwright.APIRequestContext;
import com.microsoft.playwright.APIResponse;
import com.microsoft.playwright.PlaywrightException;
@@ -10,11 +25,11 @@ import com.microsoft.playwright.options.FilePayload;
import com.microsoft.playwright.options.RequestOptions;
import java.io.File;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static com.microsoft.playwright.impl.Serialization.*;
@@ -22,6 +37,7 @@ import static com.microsoft.playwright.impl.Utils.toFilePayload;
class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
private final TracingImpl tracing;
private String disposeReason;
APIRequestContextImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
@@ -34,8 +50,17 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
}
@Override
public void dispose() {
withLogging("APIRequestContext.dispose", () -> sendMessage("dispose"));
public void dispose(DisposeOptions options) {
withLogging("APIRequestContext.dispose", () -> disposeImpl(options));
}
private void disposeImpl(DisposeOptions options) {
if (options == null) {
options = new DisposeOptions();
}
disposeReason = options.reason;
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
sendMessage("dispose", params);
}
@Override
@@ -62,6 +87,9 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
}
private APIResponse fetchImpl(String url, RequestOptionsImpl options) {
if (disposeReason != null) {
throw new PlaywrightException(disposeReason);
}
if (options == null) {
options = new RequestOptionsImpl();
}
@@ -72,7 +100,7 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
for (Map.Entry<String, ?> e : options.params.entrySet()) {
queryParams.put(e.getKey(), "" + e.getValue());
}
params.add("params", toNameValueArray(queryParams));
params.add("params", toNameValueArray(queryParams.entrySet()));
}
if (options.method != null) {
params.addProperty("method", options.method);
@@ -92,7 +120,7 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
}
}
if (bytes == null) {
params.addProperty("jsonData", gson().toJson(options.data));
params.addProperty("jsonData", jsonDataSerializer.toJson(options.data));
} else {
String base64 = Base64.getEncoder().encodeToString(bytes);
params.addProperty("postData", base64);
@@ -119,6 +147,12 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
}
params.addProperty("maxRedirects", options.maxRedirects);
}
if (options.maxRetries != null) {
if (options.maxRetries < 0) {
throw new PlaywrightException("'maxRetries' must be greater than or equal to '0'");
}
params.addProperty("maxRetries", options.maxRetries);
}
JsonObject json = sendMessage("fetch", params).getAsJsonObject();
return new APIResponseImpl(this, json.getAsJsonObject("response"));
}
@@ -135,9 +169,9 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
return false;
}
private static JsonArray serializeMultipartData(Map<String, Object> data) {
private static JsonArray serializeMultipartData(List<? extends Map.Entry<String, Object>> data) {
JsonArray result = new JsonArray();
for (Map.Entry<String, Object> e : data.entrySet()) {
for (Map.Entry<String, ?> e : data) {
FilePayload filePayload = null;
if (e.getValue() instanceof FilePayload) {
filePayload = (FilePayload) e.getValue();
@@ -1,15 +1,37 @@
/*
* 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;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.microsoft.playwright.APIRequest;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.options.ClientCertificate;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Base64;
import java.util.List;
import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Utils.addToProtocol;
import static java.nio.file.Files.readAllBytes;
class APIRequestImpl implements APIRequest {
private final PlaywrightImpl playwright;
@@ -26,6 +48,8 @@ class APIRequestImpl implements APIRequest {
private APIRequestContextImpl newContextImpl(NewContextOptions options) {
if (options == null) {
options = new NewContextOptions();
} else {
options = Utils.clone(options);
}
if (options.storageStatePath != null) {
try {
@@ -41,11 +65,13 @@ class APIRequestImpl implements APIRequest {
storageState = new Gson().fromJson(options.storageState, JsonObject.class);
options.storageState = null;
}
List<ClientCertificate> clientCertificateList = options.clientCertificates;
options.clientCertificates = null;
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
if (storageState != null) {
params.add("storageState", storageState);
}
addToProtocol(params, clientCertificateList);
JsonObject result = playwright.sendMessage("newRequest", params).getAsJsonObject();
APIRequestContextImpl context = playwright.connection.getExistingObject(result.getAsJsonObject("request").get("guid").getAsString());
return context;
@@ -46,22 +46,20 @@ class APIResponseImpl implements APIResponse {
@Override
public byte[] body() {
return context.withLogging("APIResponse.body", () -> {
try {
JsonObject params = new JsonObject();
params.addProperty("fetchUid", fetchUid());
JsonObject json = context.sendMessage("fetchResponseBody", params).getAsJsonObject();
if (!json.has("binary")) {
throw new PlaywrightException("Response has been disposed");
}
return Base64.getDecoder().decode(json.get("binary").getAsString());
} catch (PlaywrightException e) {
if (isSafeCloseError(e)) {
throw new PlaywrightException("Response has been disposed");
}
throw e;
try {
JsonObject params = new JsonObject();
params.addProperty("fetchUid", fetchUid());
JsonObject json = context.sendMessage("fetchResponseBody", params).getAsJsonObject();
if (!json.has("binary")) {
throw new PlaywrightException("Response has been disposed");
}
});
return Base64.getDecoder().decode(json.get("binary").getAsString());
} catch (PlaywrightException e) {
if (isSafeCloseError(e)) {
throw new PlaywrightException("Response has been disposed");
}
throw e;
}
}
@Override
@@ -20,6 +20,7 @@ import com.microsoft.playwright.PlaywrightException;
import org.opentest4j.AssertionFailedError;
import org.opentest4j.ValueWrapper;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
@@ -46,7 +47,6 @@ class AssertionsBase {
options = new FrameExpectOptions();
}
options.expectedText = expectedText;
options.isNot = isNot;
expectImpl(expression, options, expected, message);
}
@@ -54,13 +54,14 @@ class AssertionsBase {
if (expectOptions.timeout == null) {
expectOptions.timeout = AssertionsTimeout.defaultTimeout;
}
if (expectOptions.isNot) {
expectOptions.isNot = isNot;
if (isNot) {
message = message.replace("expected to", "expected not to");
}
FrameExpectResult result = actualLocator.expect(expression, expectOptions);
if (result.matches == isNot) {
Object actual = result.received == null ? null : Serialization.deserialize(result.received);
String log = String.join("\n", result.log);
String log = (result.log == null) ? "" : String.join("\n", result.log);
if (!log.isEmpty()) {
log = "\nCall log:\n" + log;
}
@@ -91,4 +92,17 @@ class AssertionsBase {
}
return expected;
}
static Boolean shouldIgnoreCase(Object options) {
if (options == null) {
return null;
}
try {
Field fromField = options.getClass().getDeclaredField("ignoreCase");
Object value = fromField.get(options);
return (Boolean) value;
} catch (NoSuchFieldException | IllegalAccessException e) {
return null;
}
}
}
@@ -36,7 +36,7 @@ import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
import static com.microsoft.playwright.impl.Utils.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.readAllBytes;
import static java.util.Arrays.asList;
@@ -45,8 +45,12 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
private final BrowserImpl browser;
private final TracingImpl tracing;
private final APIRequestContextImpl request;
private final ClockImpl clock;
final List<PageImpl> pages = new ArrayList<>();
final List<PageImpl> backgroundPages = new ArrayList<>();
final Router routes = new Router();
final WebSocketRouter webSocketRoutes = new WebSocketRouter();
private boolean closeWasCalled;
private final WaitableEvent<EventType, ?> closePromise;
final Map<String, BindingCallback> bindings = new HashMap<>();
@@ -80,6 +84,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
}
enum EventType {
BACKGROUNDPAGE,
CLOSE,
CONSOLE,
DIALOG,
@@ -100,6 +105,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
}
tracing = connection.getExistingObject(initializer.getAsJsonObject("tracing").get("guid").getAsString());
request = connection.getExistingObject(initializer.getAsJsonObject("requestContext").get("guid").getAsString());
clock = new ClockImpl(this);
closePromise = new WaitableEvent<>(listeners, EventType.CLOSE);
}
@@ -127,6 +133,16 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
return null;
}
@Override
public void onBackgroundPage(Consumer<Page> handler) {
listeners.add(EventType.BACKGROUNDPAGE, handler);
}
@Override
public void offBackgroundPage(Consumer<Page> handler) {
listeners.remove(EventType.BACKGROUNDPAGE, handler);
}
@Override
public void onClose(Consumer<BrowserContext> handler) {
listeners.add(EventType.CLOSE, handler);
@@ -217,6 +233,11 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
listeners.remove(EventType.RESPONSE, handler);
}
@Override
public ClockImpl clock() {
return clock;
}
private <T> T waitForEventWithTimeout(EventType eventType, Runnable code, Predicate<T> predicate, Double timeout) {
List<Waitable<T>> waitables = new ArrayList<>();
waitables.add(new WaitableEvent<>(listeners, eventType, predicate));
@@ -270,6 +291,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
options = new CloseOptions();
}
closeReason = options.reason;
request.dispose(convertType(options, APIRequestContext.DisposeOptions.class));
for (Map.Entry<String, HarRecorder> entry : harRecorders.entrySet()) {
JsonObject params = new JsonObject();
params.addProperty("harId", entry.getKey());
@@ -323,6 +345,11 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
});
}
@Override
public List<Page> backgroundPages() {
return new ArrayList<>(backgroundPages);
}
private void addInitScriptImpl(String script) {
JsonObject params = new JsonObject();
params.addProperty("source", script);
@@ -335,8 +362,29 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
}
@Override
public void clearCookies() {
withLogging("BrowserContext.clearCookies", () -> sendMessage("clearCookies"));
public void clearCookies(ClearCookiesOptions options) {
withLogging("BrowserContext.clearCookies", () -> clearCookiesImpl(options));
}
private void clearCookiesImpl(ClearCookiesOptions options) {
if (options == null) {
options = new ClearCookiesOptions();
}
JsonObject params = new JsonObject();
setStringOrRegex(params, "name", options.name);
setStringOrRegex(params, "domain", options.domain);
setStringOrRegex(params, "path", options.path);
sendMessage("clearCookies", params);
}
private static void setStringOrRegex(JsonObject params, String name, Object value) {
if (value instanceof String) {
params.addProperty(name, (String) value);
} else if (value instanceof Pattern) {
Pattern pattern = (Pattern) value;
params.addProperty(name + "RegexSource", pattern.pattern());
params.addProperty(name + "RegexFlags", toJsRegexFlags(pattern));
}
}
@Override
@@ -432,7 +480,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
@Override
public void route(String url, Consumer<Route> handler, RouteOptions options) {
route(new UrlMatcher(baseUrl, url), handler, options);
route(UrlMatcher.forGlob(baseUrl, url, this.connection.localUtils, false), handler, options);
}
@Override
@@ -454,7 +502,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
recordIntoHar(null, har, options);
return;
}
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl, options.url);
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl, options.url, this.connection.localUtils, false);
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
onClose(context -> harRouter.dispose());
route(matcher, route -> harRouter.handle(route), null);
@@ -467,6 +515,28 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
});
}
@Override
public void routeWebSocket(String url, Consumer<WebSocketRoute> handler) {
routeWebSocketImpl(UrlMatcher.forGlob(baseUrl, url, this.connection.localUtils, true), handler);
}
@Override
public void routeWebSocket(Pattern pattern, Consumer<WebSocketRoute> handler) {
routeWebSocketImpl(new UrlMatcher(pattern), handler);
}
@Override
public void routeWebSocket(Predicate<String> predicate, Consumer<WebSocketRoute> handler) {
routeWebSocketImpl(new UrlMatcher(predicate), handler);
}
private void routeWebSocketImpl(UrlMatcher matcher, Consumer<WebSocketRoute> handler) {
withLogging("BrowserContext.routeWebSocket", () -> {
webSocketRoutes.add(matcher, handler);
updateWebSocketInterceptionPatterns();
});
}
void recordIntoHar(PageImpl page, Path har, RouteFromHAROptions options) {
JsonObject params = new JsonObject();
if (page != null) {
@@ -553,14 +623,22 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
@Override
public String storageState(StorageStateOptions options) {
return withLogging("BrowserContext.storageState", () -> {
JsonElement json = sendMessage("storageState");
String storageState = json.toString();
if (options != null && options.path != null) {
Utils.writeToFile(storageState.getBytes(StandardCharsets.UTF_8), options.path);
}
return storageState;
});
return withLogging("BrowserContext.storageState", () -> storageStateImpl(options));
}
private String storageStateImpl(StorageStateOptions options) {
if (options == null) {
options = new StorageStateOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.remove("path");
JsonElement json = sendMessage("storageState", params);
String storageState = json.toString();
if (options.path != null) {
Utils.writeToFile(storageState.getBytes(StandardCharsets.UTF_8), options.path);
}
return storageState;
}
@Override
@@ -568,9 +646,17 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
return tracing;
}
@Override
public void unrouteAll() {
withLogging("BrowserContext.unrouteAll", () -> {
routes.removeAll();
updateInterceptionPatterns();
});
}
@Override
public void unroute(String url, Consumer<Route> handler) {
unroute(new UrlMatcher(this.baseUrl, url), handler);
unroute(UrlMatcher.forGlob(this.baseUrl, url, this.connection.localUtils, false), handler);
}
@Override
@@ -626,6 +712,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
}
private void updateWebSocketInterceptionPatterns() {
sendMessage("setWebSocketInterceptionPatterns", webSocketRoutes.interceptionPatterns());
}
void handleRoute(RouteImpl route) {
Router.HandleResult handled = routes.handle(route);
if (handled != Router.HandleResult.NoMatchingHandler) {
@@ -636,6 +726,12 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
}
}
void handleWebSocketRoute(WebSocketRouteImpl route) {
if (!webSocketRoutes.handle(route)) {
route.connectToServer();
}
}
WaitableResult<JsonElement> pause() {
return sendMessageAsync("pause", new JsonObject());
}
@@ -675,6 +771,9 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
RouteImpl route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
route.browserContext = this;
handleRoute(route);
} else if ("webSocketRoute".equals(event)) {
WebSocketRouteImpl route = connection.getExistingObject(params.getAsJsonObject("webSocketRoute").get("guid").getAsString());
handleWebSocketRoute(route);
} else if ("page".equals(event)) {
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
pages.add(page);
@@ -682,6 +781,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
if (page.opener() != null && !page.opener().isClosed()) {
page.opener().notifyPopup(page);
}
} else if ("backgroundPage".equals(event)) {
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
backgroundPages.add(page);
listeners.notify(EventType.BACKGROUNDPAGE, page);
} else if ("bindingCall".equals(event)) {
BindingCall bindingCall = connection.getExistingObject(params.getAsJsonObject("binding").get("guid").getAsString());
BindingCallback binding = bindings.get(bindingCall.name());
@@ -208,6 +208,7 @@ class BrowserImpl extends ChannelOwner implements Browser {
params.addProperty("noDefaultViewport", true);
}
}
addToProtocol(params, options.clientCertificates);
params.remove("acceptDownloads");
if (options.acceptDownloads != null) {
params.addProperty("acceptDownloads", options.acceptDownloads ? "accept" : "deny");
@@ -26,10 +26,12 @@ import com.microsoft.playwright.options.HarContentPolicy;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Utils.addToProtocol;
import static com.microsoft.playwright.impl.Utils.convertType;
class BrowserTypeImpl extends ChannelOwner implements BrowserType {
@@ -195,6 +197,10 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
if (!userDataDir.isAbsolute() && !userDataDir.toString().isEmpty()) {
Path cwd = Paths.get("").toAbsolutePath();
userDataDir = cwd.resolve(userDataDir);
}
params.addProperty("userDataDir", userDataDir.toString());
if (recordHar != null) {
params.add("recordHar", recordHar);
@@ -221,6 +227,7 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
params.addProperty("noDefaultViewport", true);
}
}
addToProtocol(params, options.clientCertificates);
params.remove("acceptDownloads");
if (options.acceptDownloads != null) {
params.addProperty("acceptDownloads", options.acceptDownloads ? "accept" : "deny");
@@ -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;
@@ -19,7 +35,10 @@ public class CDPSessionImpl extends ChannelOwner implements CDPSession {
super.handleEvent(event, parameters);
if ("event".equals(event)) {
String method = parameters.get("method").getAsString();
JsonObject params = parameters.get("params").getAsJsonObject();
JsonObject params = null;
if (parameters.has("params")) {
params = parameters.get("params").getAsJsonObject();
}
listeners.notify(method, params);
}
}
@@ -35,6 +35,7 @@ class ChannelOwner extends LoggingSupport {
final String guid;
final JsonObject initializer;
private boolean wasCollected;
private boolean isInternalType;
protected ChannelOwner(ChannelOwner parent, String type, String guid, JsonObject initializer) {
this(parent.connection, parent, type, guid, initializer);
@@ -58,6 +59,10 @@ class ChannelOwner extends LoggingSupport {
}
}
void markAsInternalType() {
isInternalType = true;
}
void disposeChannelOwner(boolean wasGarbageCollected) {
// Clean up from parent and connection.
if (parent != null) {
@@ -84,6 +89,9 @@ class ChannelOwner extends LoggingSupport {
@Override
<T> T withLogging(String apiName, Supplier<T> code) {
if (isInternalType) {
apiName = null;
}
String previousApiName = connection.setApiName(apiName);
try {
return super.withLogging(apiName, code);
@@ -92,6 +100,10 @@ class ChannelOwner extends LoggingSupport {
}
}
WaitableResult<JsonElement> sendMessageAsync(String method) {
return sendMessageAsync(method, new JsonObject());
}
WaitableResult<JsonElement> sendMessageAsync(String method, JsonObject params) {
checkNotCollected();
return connection.sendMessageAsync(guid, method, params);
@@ -0,0 +1,135 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.Clock;
import java.util.Date;
class ClockImpl implements Clock {
private final ChannelOwner browserContext;
ClockImpl(BrowserContextImpl browserContext) {
this.browserContext = browserContext;
}
private void sendMessageWithLogging(String method, JsonObject params) {
String capitalizedMethod = method.substring(0, 1).toUpperCase() + method.substring(1);
browserContext.withLogging("Clock." + method,
() -> browserContext.sendMessage("clock" + capitalizedMethod, params));
}
@Override
public void fastForward(long ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksNumber", ticks);
sendMessageWithLogging("fastForward", params);
}
@Override
public void fastForward(String ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksString", ticks);
sendMessageWithLogging("fastForward", params);
}
@Override
public void install(InstallOptions options) {
JsonObject params = new JsonObject();
if (options != null) {
parseTime(options.time, params);
}
sendMessageWithLogging("install", params);
}
@Override
public void runFor(long ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksNumber", ticks);
sendMessageWithLogging("runFor", params);
}
@Override
public void runFor(String ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksString", ticks);
sendMessageWithLogging("runFor", params);
}
@Override
public void pauseAt(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
sendMessageWithLogging("pauseAt", params);
}
@Override
public void pauseAt(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
sendMessageWithLogging("pauseAt", params);
}
@Override
public void pauseAt(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
sendMessageWithLogging("pauseAt", params);
}
@Override
public void resume() {
sendMessageWithLogging("resume", new JsonObject());
}
@Override
public void setFixedTime(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
sendMessageWithLogging("setFixedTime", params);
}
@Override
public void setFixedTime(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
sendMessageWithLogging("setFixedTime", params);
}
@Override
public void setFixedTime(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
sendMessageWithLogging("setFixedTime", params);
}
@Override
public void setSystemTime(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
sendMessageWithLogging("setSystemTime", params);
}
@Override
public void setSystemTime(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
sendMessageWithLogging("setSystemTime", params);
}
@Override
public void setSystemTime(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
sendMessageWithLogging("setSystemTime", params);
}
private static void parseTime(Object time, JsonObject params) {
if (time instanceof Long) {
params.addProperty("timeNumber", (Long) time);
} else if (time instanceof Date) {
params.addProperty("timeNumber", ((Date) time).getTime());
} else if (time instanceof String) {
params.addProperty("timeString", (String) time);
}
}
}
@@ -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() {
@@ -382,6 +384,9 @@ public class Connection {
case "WebSocket":
result = new WebSocketImpl(parent, type, guid, initializer);
break;
case "WebSocketRoute":
result = new WebSocketRouteImpl(parent, type, guid, initializer);
break;
case "Worker":
result = new WorkerImpl(parent, type, guid, initializer);
break;
@@ -21,11 +21,13 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.ElementHandle;
import com.microsoft.playwright.Frame;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.options.BoundingBox;
import com.microsoft.playwright.options.ElementState;
import com.microsoft.playwright.options.FilePayload;
import com.microsoft.playwright.options.SelectOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Base64;
@@ -490,7 +492,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);
}
@@ -20,39 +20,93 @@ import com.microsoft.playwright.options.FilePayload;
import com.microsoft.playwright.options.FormData;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
public class FormDataImpl implements FormData {
Map<String, Object> fields = new LinkedHashMap<>();
static class Field implements Map.Entry<String, Object> {
final String name;
final Object value;
private Field(String name, Object value) {
this.name = name;
this.value = value;
}
@Override
public String getKey() {
return name;
}
@Override
public Object getValue() {
return value;
}
@Override
public Object setValue(Object value) {
throw new UnsupportedOperationException();
}
}
List<Field> fields = new ArrayList();
@Override
public FormData append(String name, String value) {
return appendImpl(name, value);
}
@Override
public FormData append(String name, boolean value) {
return appendImpl(name, value);
}
@Override
public FormData append(String name, int value) {
return appendImpl(name, value);
}
@Override
public FormData append(String name, Path value) {
return appendImpl(name, value);
}
@Override
public FormData append(String name, FilePayload value) {
return appendImpl(name, value);
}
@Override
public FormData set(String name, String value) {
fields.put(name, value);
return this;
return setImpl(name, value);
}
@Override
public FormData set(String name, boolean value) {
fields.put(name, value);
return this;
return setImpl(name, value);
}
@Override
public FormData set(String name, int value) {
fields.put(name, value);
return this;
return setImpl(name, value);
}
@Override
public FormData set(String name, Path value) {
fields.put(name, value);
return this;
return setImpl(name, value);
}
@Override
public FormData set(String name, FilePayload value) {
fields.put(name, value);
return setImpl(name, value);
}
private FormData setImpl(String name, Object value) {
fields = fields.stream().filter(f -> !name.equals(f.name)).collect(Collectors.toList());
return appendImpl(name, value);
}
private FormData appendImpl(String name, Object value) {
fields.add(new Field(name, value));
return this;
}
}
@@ -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
@@ -1031,7 +1031,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
List<Waitable<Response>> waitables = new ArrayList<>();
if (matcher == null) {
matcher = UrlMatcher.forOneOf(page.context().baseUrl, options.url);
matcher = UrlMatcher.forOneOf(page.context().baseUrl, options.url, this.connection.localUtils, false);
}
logger.log("waiting for navigation " + matcher);
waitables.add(new WaitForNavigationHelper(matcher, options.waitUntil, logger));
@@ -1078,7 +1078,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
@Override
public void waitForURL(String url, WaitForURLOptions options) {
waitForURL(new UrlMatcher(page.context().baseUrl, url), options);
waitForURL(UrlMatcher.forGlob(page.context().baseUrl, url, this.connection.localUtils, false), options);
}
@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
@@ -133,4 +133,9 @@ class FrameLocatorImpl implements FrameLocator {
public FrameLocator nth(int index) {
return new FrameLocatorImpl(frame, frameSelector + " >> nth=" + index);
}
@Override
public Locator owner() {
return new LocatorImpl(frame, frameSelector, null);
}
}
@@ -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()
@@ -41,7 +41,12 @@ public class JSHandleImpl extends ChannelOwner implements JSHandle {
@Override
public void dispose() {
withLogging("JSHandle.dispose", () -> sendMessage("dispose"));
withLogging("JSHandle.dispose", () -> {
try {
sendMessage("dispose");
} catch (TargetClosedError e) {
}
});
}
@Override
@@ -33,6 +33,7 @@ class JsonPipe extends ChannelOwner implements Transport {
private ListenerCollection<EventType> listeners = new ListenerCollection<>();
private enum EventType { CLOSE }
private boolean isClosed;
private String closeReason = "Browser has been closed";
JsonPipe(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
@@ -97,13 +98,19 @@ class JsonPipe extends ChannelOwner implements Transport {
incoming.add(params.get("message").getAsJsonObject());
} else if ("closed".equals(event)) {
isClosed = true;
if (params.has("reason")) {
String reason = params.get("reason").getAsString();
if (reason.trim().length() > 0) {
closeReason = reason;
}
}
listeners.notify(EventType.CLOSE, this);
}
}
private void checkIfClosed() {
if (isClosed) {
throw new PlaywrightException("Browser has been closed");
throw new PlaywrightException(closeReason);
}
}
}
@@ -17,6 +17,7 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import java.util.*;
import java.util.function.Consumer;
@@ -46,6 +47,9 @@ class ListenerCollection <EventType> {
}
void add(EventType type, Consumer<?> listener) {
if (listener == null) {
throw new PlaywrightException("Can't add a null listener");
}
List<Consumer<?>> list = listeners.get(type);
if (list == null) {
list = new ArrayList<>();
@@ -21,12 +21,18 @@ import com.google.gson.JsonObject;
import java.nio.file.Path;
import java.util.List;
import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Serialization.gson;
class LocalUtils extends ChannelOwner {
public class LocalUtils extends ChannelOwner {
LocalUtils(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
markAsInternalType();
}
JsonArray deviceDescriptors() {
return initializer.getAsJsonArray("deviceDescriptors");
}
void zip(Path zipFile, JsonArray entries, String stacksId, boolean appendMode, boolean includeSources) {
@@ -54,4 +60,16 @@ class LocalUtils extends ChannelOwner {
JsonObject json = connection.localUtils().sendMessage("tracingStarted", params).getAsJsonObject();
return json.get("stacksId").getAsString();
}
public Pattern globToRegex(String glob, String baseURL, boolean webSocketUrl) {
JsonObject params = new JsonObject();
params.addProperty("glob", glob);
if (baseURL != null) {
params.addProperty("baseURL", baseURL);
}
params.addProperty("webSocketUrl", webSocketUrl);
JsonObject json = connection.localUtils().sendMessage("globToRegex", params).getAsJsonObject();
String regex = json.get("regex").getAsString();
return Pattern.compile(regex);
}
}
@@ -18,10 +18,13 @@ package com.microsoft.playwright.impl;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.assertions.LocatorAssertions;
import com.microsoft.playwright.options.AriaRole;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Serialization.serializeArgument;
@@ -36,6 +39,25 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
super((LocatorImpl) locator, isNot);
}
@Override
public void containsClass(String classname, ContainsClassOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = classname;
expectImpl("to.contain.class", expected, classname, "Locator expected to contain class", convertType(options, FrameExpectOptions.class));
}
@Override
public void containsClass(List<String> classnames, ContainsClassOptions options) {
List<ExpectedTextValue> list = new ArrayList<>();
for (String text : classnames) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = text;
list.add(expected);
}
expectImpl("to.contain.class.array", list, classnames, "Locator expected to contain classes", convertType(options, FrameExpectOptions.class));
}
@Override
public void containsText(String text, ContainsTextOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
@@ -82,6 +104,57 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
expectImpl("to.contain.text.array", list, patterns, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleDescription(String description, HasAccessibleDescriptionOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = description;
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.description", expected, description, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleDescription(Pattern pattern, HasAccessibleDescriptionOptions options) {
ExpectedTextValue expected = expectedRegex(pattern);
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.description", expected, pattern, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleErrorMessage(String errorMessage, HasAccessibleErrorMessageOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = errorMessage;
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.error.message", expected, errorMessage, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleErrorMessage(Pattern pattern, HasAccessibleErrorMessageOptions options) {
ExpectedTextValue expected = expectedRegex(pattern);
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.error.message", expected, pattern, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleName(String name, HasAccessibleNameOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = name;
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.name", expected, name, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleName(Pattern pattern, HasAccessibleNameOptions options) {
ExpectedTextValue expected = expectedRegex(pattern);
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.name", expected, pattern, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAttribute(String name, String text, HasAttributeOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
@@ -206,6 +279,13 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
expectImpl("to.have.property", list, value, "Locator expected to have JavaScript property '" + name + "'", commonOptions);
}
@Override
public void hasRole(AriaRole role, HasRoleOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = role.toString().toLowerCase();
expectImpl("to.have.role", expected, expected.string, "Locator expected to have role", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasText(String text, HasTextOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
@@ -288,12 +368,42 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
expectImpl("to.have.values", list, patterns, "Locator expected to have values matching regex", convertType(options, FrameExpectOptions.class));
}
@Override
public void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions snapshotOptions) {
if (snapshotOptions == null) {
snapshotOptions = new MatchesAriaSnapshotOptions();
}
FrameExpectOptions options = convertType(snapshotOptions, FrameExpectOptions.class);
options.expectedValue = serializeArgument(expected);
expectImpl("to.match.aria", options, expected,"Locator expected to match Aria snapshot");
}
@Override
public void isChecked(IsCheckedOptions options) {
boolean unchecked = options != null && options.checked != null && !options.checked;
String expression = unchecked ? "to.be.unchecked" : "to.be.checked";
String message = "Locator expected to be " + (unchecked ? "un" : "") + "checked";
expectTrue(expression, message, convertType(options, FrameExpectOptions.class));
if (options == null) {
options = new IsCheckedOptions();
}
Map<String, Boolean> expectedValue = new HashMap<>();
if (options.indeterminate != null) {
expectedValue.put("indeterminate", options.indeterminate);
}
if (options.checked != null) {
expectedValue.put("checked", options.checked);
}
String expected;
if (options.indeterminate != null && options.indeterminate) {
expected = "indeterminate";
} else {
boolean unchecked = options.checked != null && !options.checked;
expected = unchecked ? "unchecked" : "checked";
}
String message = "Locator expected to be";
FrameExpectOptions expectOptions = convertType(options, FrameExpectOptions.class);
expectOptions.expectedValue = serializeArgument(expectedValue);
expectImpl("to.be.checked", expectOptions, expected, message);
}
@Override
@@ -366,17 +476,4 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
String message = "Locator expected to be " + (attached ? "attached" : "detached");
expectTrue(attached ? "to.be.attached" : "to.be.detached", message, frameOptions);
}
private static Boolean shouldIgnoreCase(Object options) {
if (options == null) {
return null;
}
try {
Field fromField = options.getClass().getDeclaredField("ignoreCase");
Object value = fromField.get(options);
return (Boolean) value;
} catch (NoSuchFieldException | IllegalAccessException e) {
return null;
}
}
}
@@ -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;
@@ -5,25 +21,25 @@ import com.google.gson.JsonObject;
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.*;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.LocatorUtils.*;
import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Utils.convertType;
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
class LocatorImpl implements Locator {
final FrameImpl frame;
final String selector;
public LocatorImpl(FrameImpl frame, String selector, LocatorOptions options) {
LocatorImpl(FrameImpl frame, String selector, LocatorOptions options) {
this(frame, selector, options, null);
}
private LocatorImpl(FrameImpl frame, String selector, LocatorOptions options, Boolean visible) {
this.frame = frame;
if (options != null) {
if (options.hasText != null) {
@@ -45,6 +61,9 @@ class LocatorImpl implements Locator {
selector += " >> internal:has-not=" + gson().toJson(locator.selector);
}
}
if (visible != null) {
selector += " >> visible=" + visible;
}
this.selector = selector;
}
@@ -99,6 +118,21 @@ class LocatorImpl implements Locator {
return new LocatorImpl(frame, selector + " >> internal:and=" + gson().toJson(other.selector), null);
}
@Override
public String ariaSnapshot(AriaSnapshotOptions options) {
return frame.withLogging("Locator.ariaSnapshot", () -> ariaSnapshotImpl(options));
}
private String ariaSnapshotImpl(AriaSnapshotOptions options) {
if (options == null) {
options = new AriaSnapshotOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
JsonObject result = frame.sendMessage("ariaSnapshot", params).getAsJsonObject();
return result.get("snapshot").getAsString();
}
@Override
public void blur(BlurOptions options) {
frame.withLogging("Locator.blur", () -> blurImpl(options));
@@ -187,6 +221,11 @@ class LocatorImpl implements Locator {
return frame.querySelectorAll(selector);
}
@Override
public FrameLocator contentFrame() {
return new FrameLocatorImpl(frame, selector);
}
@Override
public Object evaluate(String expression, Object arg, EvaluateOptions options) {
return withElement((h, o) -> h.evaluate(expression, arg), options);
@@ -212,7 +251,8 @@ class LocatorImpl implements Locator {
@Override
public Locator filter(FilterOptions options) {
return new LocatorImpl(frame, selector, convertType(options,LocatorOptions.class));
Boolean visible = (options == null) ? null : options.visible;
return new LocatorImpl(frame, selector, convertType(options, LocatorOptions.class), visible);
}
@Override
@@ -278,12 +318,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
@@ -599,6 +639,20 @@ class LocatorImpl implements Locator {
return "Locator@" + selector;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof LocatorImpl)) {
return false;
}
LocatorImpl locator = (LocatorImpl) obj;
return frame.equals(locator.frame) && selector.equals(locator.selector);
}
@Override
public int hashCode() {
return frame.hashCode() ^ selector.hashCode();
}
FrameExpectResult expect(String expression, FrameExpectOptions options) {
return frame.withLogging("Locator.expect", () -> expectImpl(expression, options));
}
@@ -611,9 +665,6 @@ class LocatorImpl implements Locator {
}
private FrameExpectResult expectImpl(String expression, FrameExpectOptions options) {
if (options == null) {
options = new FrameExpectOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
params.addProperty("expression", expression);
@@ -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);
}
@@ -21,6 +21,7 @@ import com.microsoft.playwright.assertions.PageAssertions;
import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.LocatorAssertionsImpl.shouldIgnoreCase;
import static com.microsoft.playwright.impl.UrlMatcher.resolveUrl;
import static com.microsoft.playwright.impl.Utils.convertType;
@@ -57,6 +58,7 @@ public class PageAssertionsImpl extends AssertionsBase implements PageAssertions
url = resolveUrl(actualPage.context().baseUrl, url);
}
expected.string = url;
expected.ignoreCase = shouldIgnoreCase(options);
expectImpl("to.have.url", expected, url, "Page URL expected to be", convertType(options, FrameExpectOptions.class));
}
@@ -48,7 +48,37 @@ public class PageImpl extends ChannelOwner implements Page {
final Waitable<?> waitableClosedOrCrashed;
private ViewportSize viewport;
private final Router routes = new Router();
private final WebSocketRouter webSocketRoutes = new WebSocketRouter();
private final Set<FrameImpl> frames = new LinkedHashSet<>();
private final Map<Integer, LocatorHandler> locatorHandlers = new HashMap<>();
private static class LocatorHandler {
private final Locator locator;
private final Consumer<Locator> handler;
private Integer times;
LocatorHandler(Locator locator, Consumer<Locator> handler, Integer times) {
this.locator = locator;
this.handler = handler;
this.times = times;
}
boolean call() {
if (shouldRemove()) {
return true;
}
if (times != null) {
--times;
}
handler.accept(locator);
return shouldRemove();
}
private boolean shouldRemove() {
return times != null && times == 0;
}
}
private static final Map<EventType, String> eventSubscriptions() {
Map<EventType, String> result = new HashMap<>();
result.put(EventType.CONSOLE, "console");
@@ -170,6 +200,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;
@@ -180,6 +213,11 @@ public class PageImpl extends ChannelOwner implements Page {
if (handled == Router.HandleResult.NoMatchingHandler || handled == Router.HandleResult.Fallback) {
browserContext.handleRoute(route);
}
} else if ("webSocketRoute".equals(event)) {
WebSocketRouteImpl route = connection.getExistingObject(params.getAsJsonObject("webSocketRoute").get("guid").getAsString());
if (!webSocketRoutes.handle(route)) {
browserContext.handleWebSocketRoute(route);
}
} else if ("video".equals(event)) {
String artifactGuid = params.getAsJsonObject("artifact").get("guid").getAsString();
ArtifactImpl artifact = connection.getExistingObject(artifactGuid);
@@ -198,6 +236,7 @@ public class PageImpl extends ChannelOwner implements Page {
void didClose() {
isClosed = true;
browserContext.pages.remove(this);
browserContext.backgroundPages.remove(this);
listeners.notify(EventType.CLOSE, this);
}
@@ -398,6 +437,11 @@ public class PageImpl extends ChannelOwner implements Page {
listeners.remove(EventType.WORKER, handler);
}
@Override
public ClockImpl clock() {
return browserContext.clock();
}
@Override
public Page waitForClose(WaitForCloseOptions options, Runnable code) {
return withWaitLogging("Page.waitForClose", logger -> waitForCloseImpl(options, code));
@@ -523,6 +567,63 @@ public class PageImpl extends ChannelOwner implements Page {
return withLogging("Page.querySelectorAll", () -> mainFrame.querySelectorAllImpl(selector));
}
@Override
public void addLocatorHandler(Locator locator, Consumer<Locator> handler, AddLocatorHandlerOptions options) {
LocatorImpl locatorImpl = (LocatorImpl) locator;
if (locatorImpl.frame != mainFrame) {
throw new PlaywrightException("Locator must belong to the main frame of this page");
}
if (options == null) {
options = new AddLocatorHandlerOptions();
}
if (options.times != null && options.times == 0) {
return;
}
AddLocatorHandlerOptions finalOptions = options;
withLogging("Page.addLocatorHandler", () -> {
JsonObject params = new JsonObject();
params.addProperty("selector", locatorImpl.selector);
if (finalOptions.noWaitAfter != null && finalOptions.noWaitAfter) {
params.addProperty("noWaitAfter", true);
}
params.addProperty("selector", locatorImpl.selector);
JsonObject json = (JsonObject) sendMessage("registerLocatorHandler", params);
int uid = json.get("uid").getAsInt();
locatorHandlers.put(uid, new LocatorHandler(locator, handler, finalOptions.times));
});
}
@Override
public void removeLocatorHandler(Locator locator) {
for (Map.Entry<Integer, LocatorHandler> entry: locatorHandlers.entrySet()) {
if (entry.getValue().locator.equals(locator)) {
locatorHandlers.remove(locator);
JsonObject params = new JsonObject();
params.addProperty("uid", entry.getKey());
try {
sendMessage("unregisterLocatorHandler", params);
} catch (PlaywrightException e) {
}
}
}
}
private void onLocatorHandlerTriggered(int uid) {
boolean remove = false;
try {
LocatorHandler handler = locatorHandlers.get(uid);
remove = handler != null && handler.call();
} finally {
if (remove) {
locatorHandlers.remove(uid);
}
JsonObject params = new JsonObject();
params.addProperty("uid", uid);
params.addProperty("remove", remove);
sendMessageAsync("resolveLocatorHandlerNoReply", params);
}
}
@Override
public Object evalOnSelector(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
return withLogging("Page.evalOnSelector", () -> mainFrame.evalOnSelectorImpl(
@@ -544,7 +645,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);
}
@@ -683,7 +785,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override
public Frame frameByUrl(String glob) {
return frameFor(new UrlMatcher(browserContext.baseUrl, glob));
return frameFor(UrlMatcher.forGlob(browserContext.baseUrl, glob, this.connection.localUtils, false));
}
@Override
@@ -831,6 +933,11 @@ public class PageImpl extends ChannelOwner implements Page {
return null;
}
@Override
public void requestGC() {
withLogging("Page.requestGC", () -> sendMessage("requestGC"));
}
@Override
public ResponseImpl navigate(String url, NavigateOptions options) {
return withLogging("Page.navigate", () -> mainFrame.navigateImpl(url, convertType(options, Frame.NavigateOptions.class)));
@@ -998,7 +1105,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override
public void route(String url, Consumer<Route> handler, RouteOptions options) {
route(new UrlMatcher(browserContext.baseUrl, url), handler, options);
route(UrlMatcher.forGlob(browserContext.baseUrl, url, this.connection.localUtils, false), handler, options);
}
@Override
@@ -1020,7 +1127,7 @@ public class PageImpl extends ChannelOwner implements Page {
browserContext.recordIntoHar(this, har, convertType(options, BrowserContext.RouteFromHAROptions.class));
return;
}
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.baseUrl, options.url);
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.baseUrl, options.url, this.connection.localUtils, false);
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
onClose(context -> harRouter.dispose());
route(matcher, route -> harRouter.handle(route), null);
@@ -1033,6 +1140,28 @@ public class PageImpl extends ChannelOwner implements Page {
});
}
@Override
public void routeWebSocket(String url, Consumer<WebSocketRoute> handler) {
routeWebSocketImpl(UrlMatcher.forGlob(browserContext.baseUrl, url, this.connection.localUtils, true), handler);
}
@Override
public void routeWebSocket(Pattern pattern, Consumer<WebSocketRoute> handler) {
routeWebSocketImpl(new UrlMatcher(pattern), handler);
}
@Override
public void routeWebSocket(Predicate<String> predicate, Consumer<WebSocketRoute> handler) {
routeWebSocketImpl(new UrlMatcher(predicate), handler);
}
private void routeWebSocketImpl(UrlMatcher matcher, Consumer<WebSocketRoute> handler) {
withLogging("Page.routeWebSocket", () -> {
webSocketRoutes.add(matcher, handler);
updateWebSocketInterceptionPatterns();
});
}
@Override
public byte[] screenshot(ScreenshotOptions options) {
return withLogging("Page.screenshot", () -> screenshotImpl(options));
@@ -1226,9 +1355,17 @@ public class PageImpl extends ChannelOwner implements Page {
() -> mainFrame.uncheckImpl(selector, convertType(options, Frame.UncheckOptions.class)));
}
@Override
public void unrouteAll() {
withLogging("Page.unrouteAll", () -> {
routes.removeAll();
updateInterceptionPatterns();
});
}
@Override
public void unroute(String url, Consumer<Route> handler) {
unroute(new UrlMatcher(browserContext.baseUrl, url), handler);
unroute(UrlMatcher.forGlob(browserContext.baseUrl, url, this.connection.localUtils, false), handler);
}
@Override
@@ -1252,6 +1389,10 @@ public class PageImpl extends ChannelOwner implements Page {
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
}
private void updateWebSocketInterceptionPatterns() {
sendMessage("setWebSocketInterceptionPatterns", webSocketRoutes.interceptionPatterns());
}
@Override
public String url() {
return mainFrame.url();
@@ -1367,7 +1508,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override
public Request waitForRequest(String urlGlob, WaitForRequestOptions options, Runnable code) {
return waitForRequest(new UrlMatcher(browserContext.baseUrl, urlGlob), null, options, code);
return waitForRequest(UrlMatcher.forGlob(browserContext.baseUrl, urlGlob, this.connection.localUtils, false), null, options, code);
}
@Override
@@ -1412,7 +1553,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override
public Response waitForResponse(String urlGlob, WaitForResponseOptions options, Runnable code) {
return waitForResponse(new UrlMatcher(browserContext.baseUrl, urlGlob), null, options, code);
return waitForResponse(UrlMatcher.forGlob(browserContext.baseUrl, urlGlob, this.connection.localUtils, false), null, options, code);
}
@Override
@@ -1465,7 +1606,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override
public void waitForURL(String url, WaitForURLOptions options) {
waitForURL(new UrlMatcher(browserContext.baseUrl, url), options);
waitForURL(UrlMatcher.forGlob(browserContext.baseUrl, url, this.connection.localUtils, false), options);
}
@Override
@@ -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,14 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
sharedSelectors.removeChannel(selectors);
}
public LocalUtils localUtils() {
return connection.localUtils;
}
public JsonArray deviceDescriptors() {
return localUtils().deviceDescriptors();
}
@Override
public BrowserTypeImpl chromium() {
return chromium;
@@ -33,6 +33,12 @@ class SerializedValue{
String d;
String u;
String bi;
public static class E {
String m;
String n;
String s;
}
E e;
public static class R {
String p;
String f;
@@ -53,6 +53,7 @@ public class RequestImpl extends ChannelOwner implements Request {
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
markAsInternalType();
if (initializer.has("redirectedFrom")) {
redirectedFrom = connection.getExistingObject(initializer.getAsJsonObject("redirectedFrom").get("guid").getAsString());
@@ -33,6 +33,7 @@ public class RequestOptionsImpl implements RequestOptions {
Boolean ignoreHTTPSErrors;
Double timeout;
Integer maxRedirects;
Integer maxRetries;
@Override
public RequestOptions setHeader(String name, String value) {
@@ -125,4 +126,10 @@ public class RequestOptionsImpl implements RequestOptions {
this.maxRedirects = maxRedirects;
return this;
}
@Override
public RequestOptions setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
return this;
}
}
@@ -40,6 +40,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
markAsInternalType();
headers = new RawHeaders(asList(gson().fromJson(initializer.getAsJsonArray("headers"), HttpHeader[].class)));
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
request.timing = gson().fromJson(initializer.get("timing"), Timing.class);
@@ -38,6 +38,7 @@ public class RouteImpl extends ChannelOwner implements Route {
public RouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
markAsInternalType();
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
}
@@ -47,7 +48,6 @@ public class RouteImpl extends ChannelOwner implements Route {
withLogging("Route.abort", () -> {
JsonObject params = new JsonObject();
params.addProperty("errorCode", errorCode);
params.addProperty("requestUrl", request.initializer.get("url").getAsString());
sendMessageAsync("abort", params);
});
}
@@ -135,7 +135,6 @@ public class RouteImpl extends ChannelOwner implements Route {
params.addProperty("postData", base64);
}
}
params.addProperty("requestUrl", request.initializer.get("url").getAsString());
params.addProperty("isFallback", isFallback);
sendMessageAsync("continue", params);
}
@@ -231,7 +230,6 @@ public class RouteImpl extends ChannelOwner implements Route {
if (fetchResponseUid != null) {
params.addProperty("fetchResponseUid", fetchResponseUid);
}
params.addProperty("requestUrl", request.initializer.get("url").getAsString());
sendMessageAsync("fulfill", params);
}
@@ -23,6 +23,7 @@ import com.microsoft.playwright.Route;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -66,8 +67,8 @@ class Router {
.collect(Collectors.toList());
}
int size() {
return routes.size();
void removeAll() {
routes.clear();
}
enum HandleResult { NoMatchingHandler, Handled, Fallback, PendingHandler }
@@ -99,27 +100,7 @@ class Router {
}
JsonObject interceptionPatterns() {
JsonArray jsonPatterns = new JsonArray();
for (RouteInfo route : routes) {
JsonObject jsonPattern = new JsonObject();
Object urlFilter = route.matcher.rawSource;
if (urlFilter instanceof String) {
jsonPattern.addProperty("glob", (String) urlFilter);
} else if (urlFilter instanceof Pattern) {
Pattern pattern = (Pattern) urlFilter;
jsonPattern.addProperty("regexSource", pattern.pattern());
jsonPattern.addProperty("regexFlags", toJsRegexFlags(pattern));
} else {
// Match all requests.
jsonPattern.addProperty("glob", "**/*");
jsonPatterns = new JsonArray();
jsonPatterns.add(jsonPattern);
break;
}
jsonPatterns.add(jsonPattern);
}
JsonObject result = new JsonObject();
result.add("patterns", jsonPatterns);
return result;
List<UrlMatcher> matchers = routes.stream().map(r -> r.matcher).collect(Collectors.toList());
return Utils.interceptionPatterns(matchers);
}
}

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