chore: lint java docs snippets (#32945)

This commit is contained in:
Max Schmitt 2024-10-04 11:34:04 +02:00 committed by GitHub
parent 0a45549533
commit 895be9f8de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 349 additions and 157 deletions

View file

@ -38,7 +38,7 @@ jobs:
run: npm audit --omit dev run: npm audit --omit dev
lint-snippets: lint-snippets:
name: "Lint snippets" name: "Lint snippets"
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
@ -50,6 +50,12 @@ jobs:
- uses: actions/setup-dotnet@v4 - uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '21'
- run: npm ci - run: npm ci
- run: pip install -r utils/doclint/linting-code-snippets/python/requirements.txt - run: pip install -r utils/doclint/linting-code-snippets/python/requirements.txt
- run: mvn package
working-directory: utils/doclint/linting-code-snippets/java
- run: node utils/doclint/linting-code-snippets/cli.js - run: node utils/doclint/linting-code-snippets/cli.js

View file

@ -70,22 +70,24 @@ For example, you can use [`AxeBuilder.include()`](https://github.com/dequelabs/a
`AxeBuilder.analyze()` will scan the page *in its current state* when you call it. To scan parts of a page that are revealed based on UI interactions, use [Locators](./locators.md) to interact with the page before invoking `analyze()`: `AxeBuilder.analyze()` will scan the page *in its current state* when you call it. To scan parts of a page that are revealed based on UI interactions, use [Locators](./locators.md) to interact with the page before invoking `analyze()`:
```java ```java
@Test public class HomepageTests {
void navigationMenuFlyoutShouldNotHaveAutomaticallyDetectableAccessibilityViolations() throws Exception { @Test
page.navigate("https://your-site.com/"); void navigationMenuFlyoutShouldNotHaveAutomaticallyDetectableAccessibilityViolations() throws Exception {
page.navigate("https://your-site.com/");
page.locator("button[aria-label=\"Navigation Menu\"]").click(); page.locator("button[aria-label=\"Navigation Menu\"]").click();
// It is important to waitFor() the page to be in the desired // It is important to waitFor() the page to be in the desired
// state *before* running analyze(). Otherwise, axe might not // state *before* running analyze(). Otherwise, axe might not
// find all the elements your test expects it to scan. // find all the elements your test expects it to scan.
page.locator("#navigation-menu-flyout").waitFor(); page.locator("#navigation-menu-flyout").waitFor();
AxeResults accessibilityScanResults = new AxeBuilder(page) AxeResults accessibilityScanResults = new AxeBuilder(page)
.include(Arrays.asList("#navigation-menu-flyout")) .include(Arrays.asList("#navigation-menu-flyout"))
.analyze(); .analyze();
assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations()); assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations());
}
} }
``` ```
@ -158,38 +160,40 @@ This approach avoids the downsides of using `AxeBuilder.exclude()` at the cost o
Here is an example of using fingerprints based on only rule IDs and "target" selectors pointing to each violation: Here is an example of using fingerprints based on only rule IDs and "target" selectors pointing to each violation:
```java ```java
@Test public class HomepageTests {
shouldOnlyHaveAccessibilityViolationsMatchingKnownFingerprints() throws Exception { @Test
page.navigate("https://your-site.com/"); shouldOnlyHaveAccessibilityViolationsMatchingKnownFingerprints() throws Exception {
page.navigate("https://your-site.com/");
AxeResults accessibilityScanResults = new AxeBuilder(page).analyze(); AxeResults accessibilityScanResults = new AxeBuilder(page).analyze();
List<ViolationFingerprint> violationFingerprints = fingerprintsFromScanResults(accessibilityScanResults); List<ViolationFingerprint> violationFingerprints = fingerprintsFromScanResults(accessibilityScanResults);
assertEquals(Arrays.asList( assertEquals(Arrays.asList(
new ViolationFingerprint("aria-roles", "[span[role=\"invalid\"]]"), new ViolationFingerprint("aria-roles", "[span[role=\"invalid\"]]"),
new ViolationFingerprint("color-contrast", "[li:nth-child(2) > span]"), new ViolationFingerprint("color-contrast", "[li:nth-child(2) > span]"),
new ViolationFingerprint("label", "[input]") new ViolationFingerprint("label", "[input]")
), violationFingerprints); ), violationFingerprints);
} }
// You can make your "fingerprint" as specific as you like. This one considers a violation to be // You can make your "fingerprint" as specific as you like. This one considers a violation to be
// "the same" if it corresponds the same Axe rule on the same element. // "the same" if it corresponds the same Axe rule on the same element.
// //
// Using a record type makes it easy to compare fingerprints with assertEquals // Using a record type makes it easy to compare fingerprints with assertEquals
public record ViolationFingerprint(String ruleId, String target) { } public record ViolationFingerprint(String ruleId, String target) { }
public List<ViolationFingerprint> fingerprintsFromScanResults(AxeResults results) { public List<ViolationFingerprint> fingerprintsFromScanResults(AxeResults results) {
return results.getViolations().stream() return results.getViolations().stream()
// Each violation refers to one rule and multiple "nodes" which violate it // Each violation refers to one rule and multiple "nodes" which violate it
.flatMap(violation -> violation.getNodes().stream() .flatMap(violation -> violation.getNodes().stream()
.map(node -> new ViolationFingerprint( .map(node -> new ViolationFingerprint(
violation.getId(), violation.getId(),
// Each node contains a "target", which is a CSS selector that uniquely identifies it // Each node contains a "target", which is a CSS selector that uniquely identifies it
// If the page involves iframes or shadow DOMs, it may be a chain of CSS selectors // If the page involves iframes or shadow DOMs, it may be a chain of CSS selectors
node.getTarget().toString() node.getTarget().toString()
))) )))
.collect(Collectors.toList()); .collect(Collectors.toList());
}
} }
``` ```
@ -208,11 +212,11 @@ This example fixture creates an `AxeBuilder` object which is pre-configured with
```java ```java
class AxeTestFixtures extends TestFixtures { class AxeTestFixtures extends TestFixtures {
AxeBuilder makeAxeBuilder() { AxeBuilder makeAxeBuilder() {
return new AxeBuilder(page) return new AxeBuilder(page)
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']) .withTags(new String[]{"wcag2a", "wcag2aa", "wcag21a", "wcag21aa"})
.exclude('#commonly-reused-element-with-known-issue'); .exclude("#commonly-reused-element-with-known-issue");
} }
} }
``` ```
@ -229,7 +233,7 @@ public class HomepageTests extends AxeTestFixtures {
AxeResults accessibilityScanResults = makeAxeBuilder() AxeResults accessibilityScanResults = makeAxeBuilder()
// Automatically uses the shared AxeBuilder configuration, // Automatically uses the shared AxeBuilder configuration,
// but supports additional test-specific configuration too // but supports additional test-specific configuration too
.include('#specific-element-under-test') .include("#specific-element-under-test")
.analyze(); .analyze();
assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations()); assertEquals(Collections.emptyList(), accessibilityScanResults.getViolations());

View file

@ -194,6 +194,7 @@ public class TestGitHubAPI {
These tests assume that repository exists. You probably want to create a new one before running tests and delete it afterwards. Use `@BeforeAll` and `@AfterAll` hooks for that. These tests assume that repository exists. You probably want to create a new one before running tests and delete it afterwards. Use `@BeforeAll` and `@AfterAll` hooks for that.
```java ```java
public class TestGitHubAPI {
// ... // ...
void createTestRepository() { void createTestRepository() {
@ -223,6 +224,7 @@ These tests assume that repository exists. You probably want to create a new one
disposeAPIRequestContext(); disposeAPIRequestContext();
closePlaywright(); closePlaywright();
} }
}
``` ```
### Complete test example ### Complete test example
@ -381,18 +383,20 @@ The following test creates a new issue via API and then navigates to the list of
project to check that it appears at the top of the list. The check is performed using [LocatorAssertions]. project to check that it appears at the top of the list. The check is performed using [LocatorAssertions].
```java ```java
@Test public class TestGitHubAPI {
void lastCreatedIssueShouldBeFirstInTheList() { @Test
Map<String, String> data = new HashMap<>(); void lastCreatedIssueShouldBeFirstInTheList() {
data.put("title", "[Feature] request 1"); Map<String, String> data = new HashMap<>();
data.put("body", "Feature description"); data.put("title", "[Feature] request 1");
APIResponse newIssue = request.post("/repos/" + USER + "/" + REPO + "/issues", data.put("body", "Feature description");
RequestOptions.create().setData(data)); APIResponse newIssue = request.post("/repos/" + USER + "/" + REPO + "/issues",
assertTrue(newIssue.ok()); RequestOptions.create().setData(data));
assertTrue(newIssue.ok());
page.navigate("https://github.com/" + USER + "/" + REPO + "/issues"); page.navigate("https://github.com/" + USER + "/" + REPO + "/issues");
Locator firstIssue = page.locator("a[data-hovercard-type='issue']").first(); Locator firstIssue = page.locator("a[data-hovercard-type='issue']").first();
assertThat(firstIssue).hasText("[Feature] request 1"); assertThat(firstIssue).hasText("[Feature] request 1");
}
} }
``` ```
@ -402,18 +406,20 @@ The following test creates a new issue via user interface in the browser and the
it was created: it was created:
```java ```java
@Test public class TestGitHubAPI {
void lastCreatedIssueShouldBeOnTheServer() { @Test
page.navigate("https://github.com/" + USER + "/" + REPO + "/issues"); void lastCreatedIssueShouldBeOnTheServer() {
page.locator("text=New Issue").click(); page.navigate("https://github.com/" + USER + "/" + REPO + "/issues");
page.locator("[aria-label='Title']").fill("Bug report 1"); page.locator("text=New Issue").click();
page.locator("[aria-label='Comment body']").fill("Bug description"); page.locator("[aria-label='Title']").fill("Bug report 1");
page.locator("text=Submit new issue").click(); page.locator("[aria-label='Comment body']").fill("Bug description");
String issueId = page.url().substring(page.url().lastIndexOf('/')); page.locator("text=Submit new issue").click();
String issueId = page.url().substring(page.url().lastIndexOf('/'));
APIResponse newIssue = request.get("https://github.com/" + USER + "/" + REPO + "/issues/" + issueId); APIResponse newIssue = request.get("https://github.com/" + USER + "/" + REPO + "/issues/" + issueId);
assertThat(newIssue).isOK(); assertThat(newIssue).isOK();
assertTrue(newIssue.text().contains("Bug report 1")); assertTrue(newIssue.text().contains("Bug report 1"));
}
} }
``` ```

View file

@ -14,15 +14,15 @@ test('navigates to login', async ({ page }) => {
``` ```
```java ```java
... // ...
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
public class TestPage { public class TestPage {
... // ...
@Test @Test
void navigatesToLoginPage() { void navigatesToLoginPage() {
... // ...
APIResponse response = page.request().get('https://playwright.dev'); APIResponse response = page.request().get("https://playwright.dev");
assertThat(response).isOK(); assertThat(response).isOK();
} }
} }

View file

@ -18,15 +18,15 @@ const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'.
import com.microsoft.playwright.*; import com.microsoft.playwright.*;
public class Example { public class Example {
public static void main(String[] args) { public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) { try (Playwright playwright = Playwright.create()) {
BrowserType firefox = playwright.firefox() BrowserType firefox = playwright.firefox();
Browser browser = firefox.launch(); Browser browser = firefox.launch();
Page page = browser.newPage(); Page page = browser.newPage();
page.navigate('https://example.com'); page.navigate("https://example.com");
browser.close(); browser.close();
} }
} }
} }
``` ```
@ -202,7 +202,7 @@ Browser browser = playwright.firefox().launch(); // Or 'chromium' or 'webkit'.
BrowserContext context = browser.newContext(); BrowserContext context = browser.newContext();
// Create a new page in a pristine context. // Create a new page in a pristine context.
Page page = context.newPage(); Page page = context.newPage();
page.navigate('https://example.com'); page.navigate("https://example.com");
// Graceful close up everything // Graceful close up everything
context.close(); context.close();
@ -331,7 +331,7 @@ await browser.stopTracing();
```java ```java
browser.startTracing(page, new Browser.StartTracingOptions() browser.startTracing(page, new Browser.StartTracingOptions()
.setPath(Paths.get("trace.json"))); .setPath(Paths.get("trace.json")));
page.goto('https://www.google.com'); page.navigate("https://www.google.com");
browser.stopTracing(); browser.stopTracing();
``` ```

View file

@ -655,7 +655,7 @@ import com.microsoft.playwright.*;
public class Example { public class Example {
public static void main(String[] args) { public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) { try (Playwright playwright = Playwright.create()) {
BrowserType webkit = playwright.webkit() BrowserType webkit = playwright.webkit();
Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
BrowserContext context = browser.newContext(); BrowserContext context = browser.newContext();
context.exposeBinding("pageURL", (source, args) -> source.page().url()); context.exposeBinding("pageURL", (source, args) -> source.page().url());
@ -813,8 +813,9 @@ import java.util.Base64;
public class Example { public class Example {
public static void main(String[] args) { public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) { try (Playwright playwright = Playwright.create()) {
BrowserType webkit = playwright.webkit() BrowserType webkit = playwright.webkit();
Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
BrowserContext context = browser.newContext();
context.exposeFunction("sha256", args -> { context.exposeFunction("sha256", args -> {
String text = (String) args[0]; String text = (String) args[0];
MessageDigest crypto; MessageDigest crypto;

View file

@ -44,8 +44,8 @@ ConsoleMessage msg = page.waitForConsoleMessage(() -> {
}); });
// Deconstruct console.log arguments // Deconstruct console.log arguments
msg.args().get(0).jsonValue() // hello msg.args().get(0).jsonValue(); // hello
msg.args().get(1).jsonValue() // 42 msg.args().get(1).jsonValue(); // 42
``` ```
```python async ```python async

View file

@ -6,7 +6,7 @@ The [FormData] is used create form data that is sent via [APIRequestContext].
```java ```java
import com.microsoft.playwright.options.FormData; import com.microsoft.playwright.options.FormData;
... // ...
FormData form = FormData.create() FormData form = FormData.create()
.set("firstName", "John") .set("firstName", "John")
.set("lastName", "Doe") .set("lastName", "Doe")
@ -28,7 +28,7 @@ the new value onto the end of the existing set of values.
```java ```java
import com.microsoft.playwright.options.FormData; import com.microsoft.playwright.options.FormData;
... // ...
FormData form = FormData.create() FormData form = FormData.create()
// Only name and value are set. // Only name and value are set.
.append("firstName", "John") .append("firstName", "John")
@ -100,7 +100,7 @@ Sets a field on the form. File values can be passed either as `Path` or as `File
```java ```java
import com.microsoft.playwright.options.FormData; import com.microsoft.playwright.options.FormData;
... // ...
FormData form = FormData.create() FormData form = FormData.create()
// Only name and value are set. // Only name and value are set.
.set("firstName", "John") .set("firstName", "John")

View file

@ -257,7 +257,7 @@ await browser.close();
Page page = browser.newPage(); Page page = browser.newPage();
page.navigate("https://keycode.info"); page.navigate("https://keycode.info");
page.keyboard().press("A"); 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.keyboard().press("ArrowLeft");
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png"))); page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
page.keyboard().press("Shift+O"); page.keyboard().press("Shift+O");

View file

@ -38,7 +38,7 @@ for li in page.get_by_role('listitem').all():
``` ```
```java ```java
for (Locator li : page.getByRole('listitem').all()) for (Locator li : page.getByRole("listitem").all())
li.click(); li.click();
``` ```

View file

@ -14,14 +14,14 @@ test('status becomes submitted', async ({ page }) => {
``` ```
```java ```java
... // ...
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
public class TestLocator { public class TestLocator {
... // ...
@Test @Test
void statusBecomesSubmitted() { void statusBecomesSubmitted() {
... // ...
page.getByRole(AriaRole.BUTTON).click(); page.getByRole(AriaRole.BUTTON).click();
assertThat(page.locator(".status")).hasText("Submitted"); assertThat(page.locator(".status")).hasText("Submitted");
} }
@ -2048,7 +2048,7 @@ await expect(locator).toHaveValues([/R/, /G/]);
``` ```
```java ```java
page.locator("id=favorite-colors").selectOption(["R", "G"]); page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") }); assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
``` ```

View file

@ -1041,9 +1041,9 @@ await page.dragAndDrop('#source', '#target', {
``` ```
```java ```java
page.dragAndDrop("#source", '#target'); page.dragAndDrop("#source", "#target");
// or specify exact positions relative to the top-left corners of the elements: // or specify exact positions relative to the top-left corners of the elements:
page.dragAndDrop("#source", '#target', new Page.DragAndDropOptions() page.dragAndDrop("#source", "#target", new Page.DragAndDropOptions()
.setSourcePosition(34, 7).setTargetPosition(10, 20)); .setSourcePosition(34, 7).setTargetPosition(10, 20));
``` ```
@ -1716,7 +1716,7 @@ public class Example {
public static void main(String[] args) { public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) { try (Playwright playwright = Playwright.create()) {
BrowserType webkit = playwright.webkit(); BrowserType webkit = playwright.webkit();
Browser browser = webkit.launch({ headless: false }); Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
BrowserContext context = browser.newContext(); BrowserContext context = browser.newContext();
Page page = context.newPage(); Page page = context.newPage();
page.exposeBinding("pageURL", (source, args) -> source.page().url()); page.exposeBinding("pageURL", (source, args) -> source.page().url());
@ -1886,26 +1886,27 @@ public class Example {
public static void main(String[] args) { public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) { try (Playwright playwright = Playwright.create()) {
BrowserType webkit = playwright.webkit(); BrowserType webkit = playwright.webkit();
Browser browser = webkit.launch({ headless: false }); Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
Page page = browser.newPage(); Page page = browser.newPage();
page.exposeFunction("sha256", args -> { page.exposeFunction("sha256", args -> {
String text = (String) args[0];
MessageDigest crypto;
try { try {
crypto = MessageDigest.getInstance("SHA-256"); String text = (String) args[0];
MessageDigest crypto = MessageDigest.getInstance("SHA-256");
byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(token);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
return null; return null;
} }
byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(token);
}); });
page.setContent("<script>\n" + page.setContent(
"<script>\n" +
" async function onClick() {\n" + " async function onClick() {\n" +
" document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');\n" + " document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');\n" +
" }\n" + " }\n" +
"</script>\n" + "</script>\n" +
"<button onclick=\"onClick()\">Click me</button>\n" + "<button onclick=\"onClick()\">Click me</button>\n" +
"<div></div>\n"); "<div></div>"
);
page.click("button"); page.click("button");
} }
} }
@ -2106,7 +2107,7 @@ const frame = page.frame({ url: /.*domain.*/ });
``` ```
```java ```java
Frame frame = page.frameByUrl(Pattern.compile(".*domain.*"); Frame frame = page.frameByUrl(Pattern.compile(".*domain.*"));
``` ```
```py ```py
@ -3161,12 +3162,12 @@ await page.getByRole('button', { name: 'Start here' }).click();
```java ```java
// Setup the handler. // Setup the handler.
page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () => { page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () -> {
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click(); page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click();
}); });
// Write the test as usual. // Write the test as usual.
page.goto("https://example.com"); page.navigate("https://example.com");
page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
``` ```
@ -3218,12 +3219,12 @@ await page.getByRole('button', { name: 'Start here' }).click();
```java ```java
// Setup the handler. // Setup the handler.
page.addLocatorHandler(page.getByText("Confirm your security details")), () => { page.addLocatorHandler(page.getByText("Confirm your security details"), () -> {
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click(); page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click();
}); });
// Write the test as usual. // Write the test as usual.
page.goto("https://example.com"); page.navigate("https://example.com");
page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
``` ```
@ -3275,12 +3276,12 @@ await page.getByRole('button', { name: 'Start here' }).click();
```java ```java
// Setup the handler. // Setup the handler.
page.addLocatorHandler(page.locator("body")), () => { page.addLocatorHandler(page.locator("body"), () -> {
page.evaluate("window.removeObstructionsForTestIfNeeded()"); page.evaluate("window.removeObstructionsForTestIfNeeded()");
}, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true)); }, new Page.AddLocatorHandlerOptions().setNoWaitAfter(true));
// Write the test as usual. // Write the test as usual.
page.goto("https://example.com"); page.navigate("https://example.com");
page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click(); page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
``` ```
@ -3326,7 +3327,7 @@ await page.addLocatorHandler(page.getByLabel('Close'), async locator => {
``` ```
```java ```java
page.addLocatorHandler(page.getByLabel("Close"), locator => { page.addLocatorHandler(page.getByLabel("Close"), locator -> {
locator.click(); locator.click();
}, new Page.AddLocatorHandlerOptions().setTimes(1)); }, new Page.AddLocatorHandlerOptions().setTimes(1));
``` ```

View file

@ -14,14 +14,14 @@ test('navigates to login', async ({ page }) => {
``` ```
```java ```java
... // ...
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
public class TestPage { public class TestPage {
... // ...
@Test @Test
void navigatesToLoginPage() { void navigatesToLoginPage() {
... // ...
page.getByText("Sign in").click(); page.getByText("Sign in").click();
assertThat(page).hasURL(Pattern.compile(".*/login")); assertThat(page).hasURL(Pattern.compile(".*/login"));
} }

View file

@ -35,14 +35,13 @@ def test_status_becomes_submitted(page: Page) -> None:
``` ```
```java ```java
...
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
public class TestExample { public class TestExample {
... // ...
@Test @Test
void statusBecomesSubmitted() { void statusBecomesSubmitted() {
... // ...
page.locator("#submit-button").click(); page.locator("#submit-button").click();
assertThat(page.locator(".status")).hasText("Submitted"); assertThat(page.locator(".status")).hasText("Submitted");
} }

View file

@ -1488,19 +1488,19 @@ page.get_by_text(re.compile("^hello$", re.IGNORECASE))
```java ```java
// Matches <span> // Matches <span>
page.getByText("world") page.getByText("world");
// Matches first <div> // Matches first <div>
page.getByText("Hello world") page.getByText("Hello world");
// Matches second <div> // Matches second <div>
page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
// Matches both <div>s // Matches both <div>s
page.getByText(Pattern.compile("Hello")) page.getByText(Pattern.compile("Hello"));
// Matches second <div> // Matches second <div>
page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
``` ```
```csharp ```csharp

View file

@ -188,7 +188,7 @@ page.setViewportSize(1600, 1200);
// Emulate high-DPI // Emulate high-DPI
BrowserContext context = browser.newContext(new Browser.NewContextOptions() BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.setViewportSize(2560, 1440) .setViewportSize(2560, 1440)
.setDeviceScaleFactor(2); .setDeviceScaleFactor(2));
``` ```
```python async ```python async
@ -378,7 +378,7 @@ const context = await browser.newContext({
```java ```java
BrowserContext context = browser.newContext(new Browser.NewContextOptions() BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.setPermissions(Arrays.asList("notifications")); .setPermissions(Arrays.asList("notifications")));
``` ```
```python async ```python async

View file

@ -122,7 +122,7 @@ await locator.click();
```java ```java
Locator locator = page.getByRole(AriaRole.BUTTON, Locator locator = page.getByRole(AriaRole.BUTTON,
new Page.GetByRoleOptions().setName("Sign in")) new Page.GetByRoleOptions().setName("Sign in"));
locator.hover(); locator.hover();
locator.click(); locator.click();
@ -946,7 +946,7 @@ page.getByRole(AriaRole.LISTITEM)
.setName("Product 2")))) .setName("Product 2"))))
.getByRole(AriaRole.BUTTON, .getByRole(AriaRole.BUTTON,
new Page.GetByRoleOptions().setName("Add to cart")) new Page.GetByRoleOptions().setName("Add to cart"))
.click() .click();
``` ```
```python async ```python async
@ -987,7 +987,7 @@ assertThat(page
.getByRole(AriaRole.LISTITEM) .getByRole(AriaRole.LISTITEM)
.filter(new Locator.FilterOptions() .filter(new Locator.FilterOptions()
.setHas(page.GetByRole(AriaRole.HEADING, .setHas(page.GetByRole(AriaRole.HEADING,
new Page.GetByRoleOptions().setName("Product 2")))) new Page.GetByRoleOptions().setName("Product 2")))))
.hasCount(1); .hasCount(1);
``` ```
@ -1033,7 +1033,7 @@ assertThat(page
.filter(new Locator.FilterOptions() .filter(new Locator.FilterOptions()
.setHas(page.GetByRole(AriaRole.LIST) .setHas(page.GetByRole(AriaRole.LIST)
.GetByRole(AriaRole.HEADING, .GetByRole(AriaRole.HEADING,
new Page.GetByRoleOptions().setName("Product 2")))) new Page.GetByRoleOptions().setName("Product 2")))))
.hasCount(1); .hasCount(1);
``` ```
@ -1079,7 +1079,7 @@ await expect(page
```java ```java
assertThat(page assertThat(page
.getByRole(AriaRole.LISTITEM) .getByRole(AriaRole.LISTITEM)
.filter(new Locator.FilterOptions().setHasNot(page.getByText("Product 2"))) .filter(new Locator.FilterOptions().setHasNot(page.getByText("Product 2"))))
.hasCount(1); .hasCount(1);
``` ```
@ -1356,7 +1356,7 @@ expect(page.get_by_role("listitem")).to_have_count(3)
``` ```
```java ```java
assertThat(page.getByRole(AriaRole.LISTITEM).hasCount(3); assertThat(page.getByRole(AriaRole.LISTITEM)).hasCount(3);
``` ```
```csharp ```csharp

View file

@ -195,15 +195,15 @@ await Expect(page.GetByTextAsync("Loquat", new () { Exact = true })).ToBeVisible
page.route("*/**/api/v1/fruits", route -> { page.route("*/**/api/v1/fruits", route -> {
Response response = route.fetch(); Response response = route.fetch();
byte[] json = response.body(); byte[] json = response.body();
parsed = new Gson().fromJson(json, JsonObject.class) JsonObject parsed = new Gson().fromJson(new String(json), JsonObject.class);
parsed.add(new JsonObject().add("name", "Loquat").add("id", 100)); parsed.add(new JsonObject().add("name", "Loquat").add("id", 100));
// Fulfill using the original response, while patching the response body // Fulfill using the original response, while patching the response body
// with the given JSON object. // with the given JSON object.
route.fulfill(new Route.FulfillOptions().setResponse(response).setBody(json.toString())); route.fulfill(new Route.FulfillOptions().setResponse(response).setBody(parsed.toString()));
}); });
// Go to the page // Go to the page
page.goto("https://demo.playwright.dev/api-mocking"); page.navigate("https://demo.playwright.dev/api-mocking");
// Assert that the Loquat fruit is visible // Assert that the Loquat fruit is visible
assertThat(page.getByText("Loquat", new Page.GetByTextOptions().setExact(true))).isVisible(); assertThat(page.getByText("Loquat", new Page.GetByTextOptions().setExact(true))).isVisible();
@ -294,7 +294,7 @@ page.routeFromHAR(Path.of("./hars/fruit.har"), new RouteFromHAROptions()
); );
// Go to the page // Go to the page
page.goto("https://demo.playwright.dev/api-mocking"); page.navigate("https://demo.playwright.dev/api-mocking");
// Assert that the fruit is visible // Assert that the fruit is visible
assertThat(page.getByText("Strawberry")).isVisible(); assertThat(page.getByText("Strawberry")).isVisible();
@ -392,10 +392,11 @@ page.routeFromHAR(Path.of("./hars/fruit.har"), new RouteFromHAROptions()
); );
// Go to the page // Go to the page
page.goto("https://demo.playwright.dev/api-mocking"); page.navigate("https://demo.playwright.dev/api-mocking");
// Assert that the Playwright fruit is visible // Assert that the Playwright fruit is visible
assertThat(page.getByText("Playwright", new Page.GetByTextOptions().setExact(true))).isVisible(); assertThat(page.getByText("Playwright", new Page.GetByTextOptions()
.setExact(true))).isVisible();
``` ```
In the trace of our test we can see that the route was fulfilled from the HAR file and the API was not called. In the trace of our test we can see that the route was fulfilled from the HAR file and the API was not called.
![trace showing the HAR file being used](https://github.com/microsoft/playwright/assets/13063165/1bd7ab66-ea4f-43c2-a4e5-ca17d4837ff1) ![trace showing the HAR file being used](https://github.com/microsoft/playwright/assets/13063165/1bd7ab66-ea4f-43c2-a4e5-ca17d4837ff1)

View file

@ -146,8 +146,8 @@ const browser = await chromium.launch({
```java ```java
Browser browser = chromium.launch(new BrowserType.LaunchOptions() Browser browser = chromium.launch(new BrowserType.LaunchOptions()
.setProxy(new Proxy("http://myproxy.com:3128") .setProxy(new Proxy("http://myproxy.com:3128")
.setUsername('usr') .setUsername("usr")
.setPassword('pwd'))); .setPassword("pwd")));
``` ```
```python async ```python async
@ -627,7 +627,7 @@ page.route("**/title.html", route -> {
String body = response.text(); String body = response.text();
body = body.replace("<title>", "<title>My prefix:"); body = body.replace("<title>", "<title>My prefix:");
Map<String, String> headers = response.headers(); Map<String, String> headers = response.headers();
headers.put("content-type": "text/html"); headers.put("content-type", "text/html");
route.fulfill(new Route.FulfillOptions() route.fulfill(new Route.FulfillOptions()
// Pass all fields from the response. // Pass all fields from the response.
.setResponse(response) .setResponse(response)

View file

@ -289,7 +289,7 @@ Page objects can then be used inside a test.
```java ```java
import models.SearchPage; import models.SearchPage;
import com.microsoft.playwright.*; import com.microsoft.playwright.*;
... // ...
// In the test // In the test
Page page = browser.newPage(); Page page = browser.newPage();

View file

@ -378,7 +378,7 @@ New method [`method: Page.addLocatorHandler`] registers a callback that will be
// Setup the handler. // Setup the handler.
page.addLocatorHandler( page.addLocatorHandler(
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Hej! You are in control of your cookies.")), page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Hej! You are in control of your cookies.")),
() - > { () -> {
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Accept all")).click(); page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Accept all")).click();
}); });
// Write the test as usual. // Write the test as usual.
@ -1187,14 +1187,12 @@ Playwright for Java 1.18 introduces [Web-First Assertions](./test-assertions).
Consider the following example: Consider the following example:
```java ```java
...
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
public class TestExample { public class TestExample {
...
@Test @Test
void statusBecomesSubmitted() { void statusBecomesSubmitted() {
... // ...
page.locator("#submit-button").click(); page.locator("#submit-button").click();
assertThat(page.locator(".status")).hasText("Submitted"); assertThat(page.locator(".status")).hasText("Submitted");
} }
@ -1471,19 +1469,19 @@ button.click("button >> visible=true");
Traces are recorded using the new [`property: BrowserContext.tracing`] API: Traces are recorded using the new [`property: BrowserContext.tracing`] API:
```java ```java
Browser browser = chromium.launch(); Browser browser = playwright.chromium().launch();
BrowserContext context = browser.newContext(); BrowserContext context = browser.newContext();
// Start tracing before creating / navigating a page. // Start tracing before creating / navigating a page.
context.tracing.start(new Tracing.StartOptions() context.tracing().start(new Tracing.StartOptions()
.setScreenshots(true) .setScreenshots(true)
.setSnapshots(true); .setSnapshots(true));
Page page = context.newPage(); Page page = context.newPage();
page.goto("https://playwright.dev"); page.navigate("https://playwright.dev");
// Stop tracing and export it into a zip archive. // Stop tracing and export it into a zip archive.
context.tracing.stop(new Tracing.StopOptions() context.tracing().stop(new Tracing.StopOptions()
.setPath(Paths.get("trace.zip"))); .setPath(Paths.get("trace.zip")));
``` ```

View file

@ -21,7 +21,7 @@ page.screenshot(path="screenshot.png")
```java ```java
page.screenshot(new Page.ScreenshotOptions() page.screenshot(new Page.ScreenshotOptions()
.setPath(Paths.get("screenshot.png"))) .setPath(Paths.get("screenshot.png")));
``` ```
```csharp ```csharp

View file

@ -202,7 +202,7 @@ You can use a Gradle build configuration script, written in Groovy or Kotlin.
}> }>
<TabItem value="gradle"> <TabItem value="gradle">
```java ```groovy
plugins { plugins {
application application
id 'java' id 'java'
@ -234,7 +234,7 @@ test {
</TabItem> </TabItem>
<TabItem value="gradle-kotlin"> <TabItem value="gradle-kotlin">
```java ```groovy
plugins { plugins {
application application
id("java") id("java")

View file

@ -209,6 +209,16 @@ class CSharpLintingService extends LintingService {
} }
} }
class JavaLintingService extends LintingService {
supports(codeLang) {
return codeLang === 'java';
}
async lint(snippets) {
return await this.spawnAsync('java', ['-jar', path.join(__dirname, 'java', 'target', 'java-syntax-checker-1.0-SNAPSHOT.jar')], snippets, path.join(__dirname, 'java'))
}
}
class LintingServiceFactory { class LintingServiceFactory {
constructor() { constructor() {
/** @type {LintingService[]} */ /** @type {LintingService[]} */
@ -219,6 +229,7 @@ class LintingServiceFactory {
this.services.push( this.services.push(
new PythonLintingService(), new PythonLintingService(),
new CSharpLintingService(), new CSharpLintingService(),
new JavaLintingService(),
); );
} }
this._metrics = {}; this._metrics = {};

View file

@ -0,0 +1,2 @@
target/
dependency-reduced-pom.xml

View file

@ -0,0 +1,53 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-syntax-checker</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.26.2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>JavaSyntaxChecker</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,110 @@
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.github.javaparser.JavaParser;
import com.github.javaparser.Problem;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.StaticJavaParser;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class JavaSyntaxChecker {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Error: Please provide the path to the JSON file");
return;
}
String codeSnippetsPath = args[args.length - 1];
List<CodeSnippet> codeSnippets = readCodeSnippets(codeSnippetsPath);
if (codeSnippets == null) {
System.out.println("Error: codeSnippets is null");
return;
}
List<Map<String, Object>> output = new ArrayList<>();
ParserConfiguration config = new ParserConfiguration();
config.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17);
for (CodeSnippet codeSnippet : codeSnippets) {
String cleanedCode = cleanSnippet(codeSnippet.code);
ParseResult<CompilationUnit> parseResult = new JavaParser(config).parse(cleanedCode);
List<Problem> syntaxErrors = parseResult.getProblems();
if (!syntaxErrors.isEmpty()) {
output.add(Map.of(
"status", "error",
"error", String.join("\n", syntaxErrors.stream()
.map(Problem::getMessage)
.collect(Collectors.toList()))
));
} else {
output.add(Map.of("status", "ok"));
}
}
System.out.println(new Gson().toJson(output));
}
private static String removeImports(String code) {
// Remove import statements
return Pattern.compile("^import.*;$", Pattern.MULTILINE)
.matcher(code)
.replaceAll("");
}
private static String cleanSnippet(String code) {
// if it contains "public class" then it's a full class, return immediately
if (code.contains("public class")) {
return code;
}
code = removeImports(code);
String wrappedCode = """
import com.microsoft.playwright.*;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.*;
public class Example {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch();
BrowserContext context = browser.newContext();
Page page = context.newPage();
%s
}
}
}
""".formatted(code);
return wrappedCode;
}
private static List<CodeSnippet> readCodeSnippets(String filePath) {
try (FileReader reader = new FileReader(filePath)) {
Type listType = new TypeToken<ArrayList<CodeSnippet>>(){}.getType();
return new Gson().fromJson(reader, listType);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
class CodeSnippet {
String filePath;
String codeLang;
String code;
public CodeSnippet(String filePath, String codeLang, String code) {
this.filePath = filePath;
this.codeLang = codeLang;
this.code = code;
}
}