Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/playwright-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ jobs:

standalone:
runs-on: ubuntu-latest
permissions:
checks: write
contents: read
env:
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
Expand All @@ -51,13 +54,23 @@ jobs:
timeout_minutes: 30
max_attempts: 3
command: cd ./playwright-examples && mvn test -Dtest=StandaloneTest -Dsurefire.parallel=1
- name: Test Report
uses: dorny/test-reporter@v2
if: success() || failure()
with:
name: Standalone Test Report
path: playwright-examples/target/surefire-reports/*.xml
reporter: java-junit

suite:
runs-on: ubuntu-latest
permissions:
checks: write
contents: read
strategy:
fail-fast: false
matrix:
browser: [ Chrome, MicrosoftEdge ]
browser: [ chromium, firefox, webkit ]
env:
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
Expand All @@ -74,3 +87,10 @@ jobs:
timeout_minutes: 30
max_attempts: 3
command: cd ./playwright-examples && mvn test -Dsauce.browser.name=${{ matrix.browser }}
- name: Test Report
uses: dorny/test-reporter@v2
if: success() || failure()
with:
name: Suite Test Report (${{ matrix.browser }})
path: playwright-examples/target/surefire-reports/*.xml
reporter: java-junit
24 changes: 14 additions & 10 deletions playwright-examples/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Playwright Examples

There is only one way to execute Playwright on Sauce Labs with Java.
It works with any production version of Chrome or Edge.
Tests connect to Sauce Labs through Playwright's native WebSocket endpoint, so any of
Playwright's engines - Chromium, Firefox or WebKit - works the same way.

[Setup Instructions](https://github.com/saucelabs-training/demo-java/blob/main/README.md#%EF%B8%8Fsetupprerequisites)
and
Expand Down Expand Up @@ -34,22 +34,26 @@ the [Main README](https://github.com/saucelabs-training/demo-java/blob/main/READ

## Configurations

This code allows toggling which browser the tests will run on.
### Browser

### Chrome (default)

Tests will execute on the latest version of Chrome:
This code allows toggling which browser engine the tests will run on: `chromium` (default),
`firefox`, or `webkit`.

```
$ mvn clean test -Dsauce.browser=Chrome
$ mvn clean test -Dsauce.browser.name=firefox
```

### Microsoft Edge
### Session grouping / parallelism

By default, all the test methods in a test class share a single Sauce session (one Sauce job per
test class), and different test classes run in parallel with each other. A session that sees a
test failure is closed and reported immediately rather than reused by the next test.

Tests will execute on the latest version of Edge:
To give every test method its own independent Sauce session instead, switch JUnit5's own
method-execution mode to `concurrent`:

```
$ mvn clean test -Dsauce.browser=MicrosoftEdge
$ mvn clean test -Djunit.parallel.mode.default=concurrent
```

## Disclaimer
Expand Down
21 changes: 3 additions & 18 deletions playwright-examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<properties>
<surefire.parallel>10</surefire.parallel>
<junit.parallel.mode.default>same_thread</junit.parallel.mode.default>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
Expand All @@ -29,28 +30,11 @@
<version>6.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.saucelabs</groupId>
<artifactId>sauce_bindings</artifactId>
<version>2.0.0-beta.1</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20250517</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.46</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.saucelabs</groupId>
<artifactId>saucerest</artifactId>
<version>2.5.3</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand All @@ -66,7 +50,8 @@
<properties>
<configurationParameters>
junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent
junit.jupiter.execution.parallel.mode.default = ${junit.parallel.mode.default}
junit.jupiter.execution.parallel.mode.classes.default = concurrent
junit.jupiter.execution.parallel.config.strategy = fixed
junit.jupiter.execution.parallel.config.fixed.parallelism = ${surefire.parallel}
junit.jupiter.execution.parallel.config.fixed.max-pool-size = ${surefire.parallel}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.saucedemo.playwright;

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class SortingTest extends TestBase {

@Test
public void sortByNameAscending() {
login();
page.locator("[data-test='product-sort-container']").selectOption("az");

List<String> actual = itemNames();
Assertions.assertEquals(sorted(actual, Comparator.naturalOrder()), actual);
}

@Test
public void sortByNameDescending() {
login();
page.locator("[data-test='product-sort-container']").selectOption("za");

List<String> actual = itemNames();
Assertions.assertEquals(sorted(actual, Comparator.reverseOrder()), actual);
}

@Test
public void sortByPriceLowToHigh() {
login();
page.locator("[data-test='product-sort-container']").selectOption("lohi");

List<Double> actual = itemPrices();
Assertions.assertEquals(sorted(actual, Comparator.naturalOrder()), actual);
}

@Test
public void sortByPriceHighToLow() {
login();
page.locator("[data-test='product-sort-container']").selectOption("hilo");

List<Double> actual = itemPrices();
Assertions.assertEquals(sorted(actual, Comparator.reverseOrder()), actual);
}

private void login() {
page.navigate("https://www.saucedemo.com/");
page.locator("[data-test='username']").fill("standard_user");
page.locator("[data-test='password']").fill("secret_sauce");
page.locator("[data-test='login-button']").click();
}

private List<String> itemNames() {
return page.locator(".inventory_item_name").allTextContents();
}

private List<Double> itemPrices() {
return page.locator(".inventory_item_price").allTextContents().stream()
.map(price -> Double.parseDouble(price.replace("$", "")))
.collect(Collectors.toList());
}

private <T> List<T> sorted(List<T> values, Comparator<T> comparator) {
return values.stream().sorted(comparator).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
package com.saucedemo.playwright;

import com.microsoft.playwright.Page;
import com.saucelabs.bindings.SaucePlaywrightSession;
import com.saucelabs.extensions.SaucePlaywrightExtension;
import com.saucelabs.playwright.SauceExtension;
import com.saucelabs.playwright.SauceSession;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;

public class TestBase {
SaucePlaywrightSession session;
SauceSession session;
Page page;

@RegisterExtension
public SaucePlaywrightExtension sauceExtension = new SaucePlaywrightExtension();
@RegisterExtension public static SauceExtension sauceExtension = new SauceExtension();

@BeforeEach
public void setUp(SaucePlaywrightSession session, Page page) {
public void setUp(SauceSession session, Page page) {
this.session = session;
this.page = page;
}

static {
System.setProperty("sauce.build.name", "Playwright Sauce Demo");
System.setProperty("sauce.build.number", String.valueOf(System.currentTimeMillis()));
}
}
53 changes: 41 additions & 12 deletions playwright-examples/src/test/java/com/saucelabs/StandaloneTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.options.RequestOptions;
import com.saucelabs.saucerest.DataCenter;
import com.saucelabs.saucerest.SauceREST;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Base64;
import java.util.regex.Pattern;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
Expand All @@ -34,7 +39,8 @@ public class StandaloneTest {
static final String SAUCE_USERNAME = System.getenv("SAUCE_USERNAME");
static final String SAUCE_ACCESS_KEY = System.getenv("SAUCE_ACCESS_KEY");
static final String SAUCE_URL = "https://ondemand.us-west-1.saucelabs.com/wd/hub/";
static SauceREST sauceREST = new SauceREST(SAUCE_USERNAME, SAUCE_ACCESS_KEY, DataCenter.US_WEST);
static final String SAUCE_API_URL = "https://api.us-west-1.saucelabs.com";
static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
static APIRequestContext request;
static Playwright playwright;
@RegisterExtension public SauceTestWatcher watcher = new SauceTestWatcher();
Expand Down Expand Up @@ -119,7 +125,9 @@ void createSession() {
void launchBrowserAndCreatePage(TestInfo testInfo) throws IOException {
createSession();
this.testInfo = testInfo;
sauceREST.getJobsEndpoint().changeName(sessionId, testInfo.getDisplayName());
JsonObject renamePayload = new JsonObject();
renamePayload.addProperty("name", testInfo.getDisplayName());
updateJob(sessionId, renamePayload);
browser = playwright.chromium().connectOverCDP(cdpEndpoint);

// Maximize browser
Expand All @@ -136,6 +144,26 @@ void closeContextAndWindow() {
request.delete("session/" + sessionId);
}

static void updateJob(String sessionId, JsonObject payload) throws IOException {
String credentials = SAUCE_USERNAME + ":" + SAUCE_ACCESS_KEY;
String authHeader =
"Basic " + Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));

HttpRequest httpRequest =
HttpRequest.newBuilder(
URI.create(SAUCE_API_URL + "/rest/v1/" + SAUCE_USERNAME + "/jobs/" + sessionId))
.timeout(Duration.ofSeconds(30))
.header("Authorization", authHeader)
.header("Content-Type", "application/json")
.method("PUT", HttpRequest.BodyPublishers.ofString(payload.toString()))
.build();
try {
HTTP_CLIENT.send(httpRequest, HttpResponse.BodyHandlers.discarding());
} catch (InterruptedException e) {
throw new IOException(e);
}
}

@Test
void shouldClickButton() {
page.navigate(
Expand Down Expand Up @@ -172,21 +200,22 @@ public class SauceTestWatcher implements TestWatcher {
@Override
public void testSuccessful(ExtensionContext context) {
printResults();
try {
sauceREST.getJobsEndpoint().passed(sessionId);
} catch (Exception e) {
System.out.println("Problem setting job as passed: " + e);
}
reportResult(true);
}

@Override
public void testFailed(ExtensionContext context, Throwable cause) {
printResults();
reportResult(false);
}

private void reportResult(boolean passed) {
JsonObject resultPayload = new JsonObject();
resultPayload.addProperty("passed", passed);
try {
sauceREST.getJobsEndpoint().failed(sessionId);
} catch (Exception e) {
System.out.println("Problem setting job as failed: " + e);
updateJob(sessionId, resultPayload);
} catch (IOException e) {
System.out.println("Problem setting job result: " + e);
}
}

Expand Down
Loading
Loading