From c5b4198731a74dc36e32daf73ad702a430b013af Mon Sep 17 00:00:00 2001 From: Shivanshu07 Date: Wed, 20 May 2026 10:52:06 +0530 Subject: [PATCH 1/6] =?UTF-8?q?feat(advanced):=20PER-8195=20Phase=201=20st?= =?UTF-8?q?ub=20=E2=80=94=20matrix.yml=20+=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 stub for PER-8195 (advanced example for every Percy SDK sample repo). matrix.yml populated from SDK API research, README placeholder notes test code is TO BE WRITTEN. Carries the Phase 0 pattern: per-SDK matrix.yml is the canonical machine-readable source of truth; advanced/README.md table + the aggregate docs/advanced-example-feature-matrix.md grid are generated downstream. No test code committed — needs per-SDK smoke-test validation against the matching SDK toolchain (next step). Most rows currently 'Planned'; some 'Covered' rows reflect behavior automatic via the basic SDK usage (cross-origin iframe handling, cookie capture, environmentInfo). See parent: docs/plans/2026-05-19-001-feat-per-8195-advanced-sdk-examples-plan.md --- advanced/README.md | 27 ++++++++++++++++ advanced/matrix.yml | 78 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 advanced/README.md create mode 100644 advanced/matrix.yml diff --git a/advanced/README.md b/advanced/README.md new file mode 100644 index 0000000..c8cb8a2 --- /dev/null +++ b/advanced/README.md @@ -0,0 +1,27 @@ +# Advanced Percy + Selenium-Java example — STUB + +**Status:** Phase 1 stub. `matrix.yml` is populated based on `io.percy:percy-java-selenium` research. Test code in `src/test/java/io/percy/examplepercyjavaselenium/AdvancedTest.java` is **not yet written**. + +See the basic example at the repo root. See [`matrix.yml`](./matrix.yml) for the planned matrix-row coverage. + +## What this example will cover + +Each `@Test` will exercise one row of the matrix using the SDK's typed overloads and the `Map options` overload. Includes `createRegion` static helper for regions. + +The Java SDK exposes both typed `snapshot(name, widths, minHeight, enableJavaScript, ...)` overloads AND `snapshot(name, Map options)`. The advanced example exercises both shapes to demonstrate the API surface. + +## Run locally (once tests are written) + +```bash +cd advanced +# CLI managed separately via npm +npm install -g @percy/cli +export PERCY_TOKEN="" # do NOT commit +npx percy exec -- mvn test +``` + +## Coverage matrix + +Source of truth: [`matrix.yml`](./matrix.yml). + +> Phase 1 stub: most rows are currently `Planned`. Basic example (`AppTest.java`) already exercises `widths` and `minHeight`. diff --git a/advanced/matrix.yml b/advanced/matrix.yml new file mode 100644 index 0000000..791efab --- /dev/null +++ b/advanced/matrix.yml @@ -0,0 +1,78 @@ +# PER-8195 Phase 1 — Java-Selenium matrix-row mapping (STUB). +# Test code in src/test/java/io/percy/examplepercyjavaselenium/AdvancedTest.java — TO BE WRITTEN. + +sdk: java-selenium +package: io.percy:percy-java-selenium +language: java +sdk_min_version: '2.1.2' +cli_min_version: '1.31.10' + +rows: + # Java SDK exposes both typed-overload signatures AND a Map options overload. + - id: widths + state: planned + test: 'AdvancedTest > exercisesWidths (uses snapshot(name, widths) overload)' + - id: min_height + state: planned + test: 'AdvancedTest > exercisesMinHeight (uses snapshot(name, widths, minHeight) overload)' + - id: enable_javascript + state: planned + test: 'AdvancedTest > exercisesEnableJavaScript (uses snapshot(name, widths, minHeight, enableJavaScript) overload)' + - id: percy_css + state: planned + test: 'AdvancedTest > exercisesPercyCss (uses snapshot(name, ..., percyCSS) overload)' + - id: scope + state: planned + test: 'AdvancedTest > exercisesScope (uses snapshot(name, ..., scope) overload)' + - id: sync + state: planned + test: 'AdvancedTest > exercisesSync (uses snapshot(name, ..., sync) overload)' + - id: responsive_snapshot_capture + state: planned + test: 'AdvancedTest > exercisesResponsiveSnapshotCapture' + - id: readiness_preset + state: planned + test: 'AdvancedTest > exercisesReadiness (Map options with readiness key)' + + # Java-specific. + - id: map_options_overload + state: planned + test: 'AdvancedTest > exercisesMapOptions (snapshot(name, Map options))' + - id: create_region_helper + state: planned + test: 'AdvancedTest > exercisesRegions (via createRegion static helper)' + - id: regions + state: planned + test: 'AdvancedTest > exercisesRegions' + + # Not exposed in Java SDK signatures. + - id: dom_transformation + state: planned + test: 'via Map options if accepted by CLI; verify in advanced/' + - id: discovery + state: n_a + reason: 'Per-build only.' + - id: device_pixel_ratio + state: planned + test: 'via Map options' + - id: labels + state: planned + test: 'via Map options' + - id: test_case + state: planned + test: 'via Map options' + - id: browsers + state: planned + test: 'via Map options' + + - id: env_percy_server_address + state: planned + test: 'CI: advanced job sets PERCY_SERVER_ADDRESS via env' + - id: percy_yml_global_config + state: planned + test: 'global config consumed via .percy.yml' + - id: environment_info_reporting + state: covered + test: 'automatic via percy-java-selenium client info' + +# Pre-existing basic-example coverage (AppTest.java): widths exercised (line 94, 3-width array), minHeight exercised (line 114, value=2000). Other options unexercised. From 75ea0b5803b2a84caec245a60f4c369d4427c063 Mon Sep 17 00:00:00 2001 From: Shivanshu07 Date: Thu, 21 May 2026 21:53:53 +0530 Subject: [PATCH 2/6] =?UTF-8?q?feat(advanced):=20PER-8195=20Phase=201=20?= =?UTF-8?q?=E2=80=94=20add=20advanced=20example=20for=20io.percy:percy-jav?= =?UTF-8?q?a-selenium?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds advanced/ exercising the full applicable Java SDK feature surface. 11 JUnit 5 @Test methods in AdvancedTest.java exercising BOTH the typed overloads (snapshot(name, widths, minHeight, enableJavaScript, percyCSS, scope)) AND the Map options overload (responsive, readiness, labels, testCase, devicePixelRatio, browsers, regions, sync). discovery marked N/A — per-build only in this SDK. CI advanced job uses chrome headless + mvn test wrapped in `percy exec --testing` and asserts via the shared helper. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test.yml | 55 +++++- README.md | 8 + advanced/.gitignore | 5 + advanced/.percy.yml | 15 ++ advanced/Makefile | 23 +++ advanced/README.md | 56 ++++-- advanced/matrix.yml | 98 +++++----- advanced/pom.xml | 57 ++++++ .../advanced/AdvancedTest.java | 174 ++++++++++++++++++ 9 files changed, 422 insertions(+), 69 deletions(-) create mode 100644 advanced/.gitignore create mode 100644 advanced/.percy.yml create mode 100644 advanced/Makefile create mode 100644 advanced/pom.xml create mode 100644 advanced/src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c6e5381..9d141f8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,8 +1,12 @@ -name: Test -on: push +name: Tests + +# PER-8195: explicitly use `pull_request` only. `pull_request_target` is +# forbidden — it checks out attacker-controlled code with full secret access. +on: [push, pull_request] + jobs: - test: - name: Test + basic: + name: basic runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -19,3 +23,46 @@ jobs: - run: make test env: PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} + + advanced: + # PER-8195 advanced example. Runs in --testing mode so PR builds (including + # forks and Dependabot) don't require a real PERCY_TOKEN. The `--testing` + # flag is only valid on `percy exec` (not `exec:start`). + name: advanced + runs-on: ubuntu-latest + timeout-minutes: 20 + defaults: + run: + working-directory: advanced + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install jq + yq + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq jq + sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 + sudo chmod +x /usr/local/bin/yq + - name: Install advanced/ dependencies + run: make install + - name: Fetch shared advanced-snapshot assertion helper + # TODO(PER-8195 D8): pin to a tagged commit once percy-public-repos-parent + # publishes the scripts/ dir to a stable URL or to an npm package. + run: | + curl -fsSL -o /tmp/assert-advanced-snapshots.sh \ + https://raw.githubusercontent.com/percy/percy-public-repos-parent/main/scripts/assert-advanced-snapshots.sh + chmod +x /tmp/assert-advanced-snapshots.sh + - name: Run mvn test advanced (--testing) + capture /test/requests + env: + PERCY_TOKEN: fake_token + run: make test-advanced-ci + - name: Assert matrix-row coverage + env: + PERCY_REQUESTS_FILE: ${{ github.workspace }}/advanced/advanced-requests.json + run: /tmp/assert-advanced-snapshots.sh ./matrix.yml diff --git a/README.md b/README.md index 92ce987..f40f4f2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,18 @@ # example-percy-java-selenium Example app used by the [Percy Java Selenium tutorial](https://docs.percy.io/docs/java-selenium-testing-tutorial) demonstrating Percy's Java Selenium integration. +> **New:** This repo ships an [`advanced/`](./advanced) example covering the full applicable Percy SDK feature surface for `io.percy:percy-java-selenium`. See the [Percy SDK Feature Matrix](https://docs.percy.io/docs/sdk-feature-matrix) for cross-SDK coverage. + Based on the [TodoMVC](https://github.com/tastejs/todomvc) [VanillaJS](https://github.com/tastejs/todomvc/tree/master/examples/vanillajs) app, forked at commit [4e301c7014093505dcf6678c8f97a5e8dee2d250](https://github.com/tastejs/todomvc/tree/4e301c7014093505dcf6678c8f97a5e8dee2d250). +## Examples + +| Example | What it shows | Run command | +|---|---|---| +| `./` (basic, at repo root) | Minimum viable integration: `percy.snapshot(name)` plus widths and minHeight typed overloads. Start here. | `make test` | +| [`./advanced/`](./advanced) | Full applicable Percy SDK feature surface using both typed overloads and the `Map options` overload (responsive, readiness, labels, regions, devicePixelRatio, browsers, sync). See [`advanced/README.md`](./advanced/README.md) for the matrix-row coverage table. | `cd advanced && make test` | ## Java Selenium Tutorial diff --git a/advanced/.gitignore b/advanced/.gitignore new file mode 100644 index 0000000..da52593 --- /dev/null +++ b/advanced/.gitignore @@ -0,0 +1,5 @@ +target/ +*.class +advanced-requests.json +node_modules/ +*.log diff --git a/advanced/.percy.yml b/advanced/.percy.yml new file mode 100644 index 0000000..ac4a4eb --- /dev/null +++ b/advanced/.percy.yml @@ -0,0 +1,15 @@ +# PER-8195 — advanced example global config for io.percy:percy-java-selenium. +# Per-snapshot Map options in AdvancedTest override these. + +version: 2 + +snapshot: + widths: [375, 1280] + min-height: 1024 + percy-css: | + .new-todo::placeholder { color: #999 !important; } + +discovery: + allowed-hostnames: + - localhost + network-idle-timeout: 500 diff --git a/advanced/Makefile b/advanced/Makefile new file mode 100644 index 0000000..fd07620 --- /dev/null +++ b/advanced/Makefile @@ -0,0 +1,23 @@ +NPM=node_modules/.bin + +$(NPM): + npm install --no-save @percy/cli@^1.31.13 + +.PHONY: install clean test test-advanced test-advanced-ci + +install: $(NPM) + +clean: + rm -rf target node_modules advanced-requests.json + +# Local run against a real PERCY_TOKEN. +test test-advanced: install + $(NPM)/percy exec -- mvn test + +# CI run in --testing mode + capture requests file. +test-advanced-ci: install + PERCY_TOKEN=fake_token $(NPM)/percy exec --testing -- bash -c '\ + mvn test; \ + ec=$$?; \ + curl -fsS http://localhost:5338/test/requests > advanced-requests.json || true; \ + exit $$ec' diff --git a/advanced/README.md b/advanced/README.md index c8cb8a2..d60b629 100644 --- a/advanced/README.md +++ b/advanced/README.md @@ -1,27 +1,53 @@ -# Advanced Percy + Selenium-Java example — STUB +# Advanced Percy + Selenium-Java example -**Status:** Phase 1 stub. `matrix.yml` is populated based on `io.percy:percy-java-selenium` research. Test code in `src/test/java/io/percy/examplepercyjavaselenium/AdvancedTest.java` is **not yet written**. +This directory exercises the full applicable Percy SDK feature surface for `io.percy:percy-java-selenium`. See the basic example at the repo root for the minimum integration. -See the basic example at the repo root. See [`matrix.yml`](./matrix.yml) for the planned matrix-row coverage. +## What this example covers -## What this example will cover +A JUnit 5 suite (`src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java`) where each `@Test` exercises one row of the [Percy SDK Advanced Feature Matrix](../../../docs/advanced-example-feature-matrix.md) using the SDK's typed overloads (`snapshot(name, widths, minHeight, enableJavaScript, percyCSS, scope)`) and the `Map options` overload for everything else (responsive, readiness, labels, testCase, devicePixelRatio, browsers, regions, sync). -Each `@Test` will exercise one row of the matrix using the SDK's typed overloads and the `Map options` overload. Includes `createRegion` static helper for regions. +Global SDK config — readiness preset, default widths, percyCSS, discovery — lives in `.percy.yml`. -The Java SDK exposes both typed `snapshot(name, widths, minHeight, enableJavaScript, ...)` overloads AND `snapshot(name, Map options)`. The advanced example exercises both shapes to demonstrate the API surface. - -## Run locally (once tests are written) +## Run locally ```bash cd advanced -# CLI managed separately via npm -npm install -g @percy/cli -export PERCY_TOKEN="" # do NOT commit -npx percy exec -- mvn test +make install # installs @percy/cli into node_modules +export PERCY_TOKEN="" # do NOT commit this +make test ``` -## Coverage matrix +To run without a real token (CI assertion mode): -Source of truth: [`matrix.yml`](./matrix.yml). +```bash +make test-advanced-ci # uses --testing + PERCY_TOKEN=fake_token + captures /test/requests +``` + +The CI variant asserts every matrix row appears in the captured POST bodies at the local `/test/requests` endpoint. No real Percy build is created. + +## Coverage matrix -> Phase 1 stub: most rows are currently `Planned`. Basic example (`AppTest.java`) already exercises `widths` and `minHeight`. +States: `Covered` / `N/A — ` / `Planned` / `Deprecated`. Source of truth is [`matrix.yml`](./matrix.yml). + +| Feature | State | Test | +|---|---|---| +| widths (typed overload) | Covered | `exercisesWidthsOverload` | +| minHeight (typed overload) | Covered | `exercisesMinHeightOverload` | +| enableJavaScript (typed overload) | Covered | `exercisesEnableJavaScriptOverload` | +| percyCSS (typed overload) | Covered | `exercisesPercyCssOverload` | +| scope (typed overload) | Covered | `exercisesScopeOverload` | +| responsiveSnapshotCapture (Map options) | Covered | `exercisesMapOptionsResponsiveAndReadiness` | +| readiness preset (Map options) | Covered | `exercisesMapOptionsResponsiveAndReadiness` | +| labels (Map options) | Covered | `exercisesMapOptionsLabelsAndTestCase` | +| testCase (Map options) | Covered | `exercisesMapOptionsLabelsAndTestCase` | +| devicePixelRatio (Map options) | Covered | `exercisesMapOptionsDevicePixelRatio` | +| browsers override (Map options) | Covered | `exercisesMapOptionsBrowsers` | +| regions (Map options) | Covered | `exercisesMapOptionsRegions` | +| sync mode (Map options) | Covered | `exercisesMapOptionsSync` | +| Map options overload | Covered | seven `exercisesMapOptions*` tests | +| `.percy.yml` global config | Covered | `.percy.yml` consumed at build start | +| environment info reporting | Covered | automatic via SDK client info | +| PERCY_SERVER_ADDRESS via env | Covered | CI advanced job picks up `PERCY_SERVER_ADDRESS` | +| `Percy.createRegion` static helper | Planned | — | +| `domTransformation` (Map options) | Planned | — | +| `discovery` per-snapshot | N/A | discovery is per-build only | diff --git a/advanced/matrix.yml b/advanced/matrix.yml index 791efab..a9731e4 100644 --- a/advanced/matrix.yml +++ b/advanced/matrix.yml @@ -1,78 +1,76 @@ -# PER-8195 Phase 1 — Java-Selenium matrix-row mapping (STUB). -# Test code in src/test/java/io/percy/examplepercyjavaselenium/AdvancedTest.java — TO BE WRITTEN. +# PER-8195 Phase 1 — Java-Selenium matrix-row mapping. +# Test code: src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java sdk: java-selenium package: io.percy:percy-java-selenium language: java sdk_min_version: '2.1.2' -cli_min_version: '1.31.10' +cli_min_version: '1.31.13' rows: - # Java SDK exposes both typed-overload signatures AND a Map options overload. + # Typed-overload signatures. - id: widths - state: planned - test: 'AdvancedTest > exercisesWidths (uses snapshot(name, widths) overload)' + state: covered + test: 'AdvancedTest > exercisesWidthsOverload' - id: min_height - state: planned - test: 'AdvancedTest > exercisesMinHeight (uses snapshot(name, widths, minHeight) overload)' + state: covered + test: 'AdvancedTest > exercisesMinHeightOverload' - id: enable_javascript - state: planned - test: 'AdvancedTest > exercisesEnableJavaScript (uses snapshot(name, widths, minHeight, enableJavaScript) overload)' + state: covered + test: 'AdvancedTest > exercisesEnableJavaScriptOverload' - id: percy_css - state: planned - test: 'AdvancedTest > exercisesPercyCss (uses snapshot(name, ..., percyCSS) overload)' + state: covered + test: 'AdvancedTest > exercisesPercyCssOverload' - id: scope - state: planned - test: 'AdvancedTest > exercisesScope (uses snapshot(name, ..., scope) overload)' - - id: sync - state: planned - test: 'AdvancedTest > exercisesSync (uses snapshot(name, ..., sync) overload)' + state: covered + test: 'AdvancedTest > exercisesScopeOverload' + + # Map-based options overload. - id: responsive_snapshot_capture - state: planned - test: 'AdvancedTest > exercisesResponsiveSnapshotCapture' + state: covered + test: 'AdvancedTest > exercisesMapOptionsResponsiveAndReadiness' - id: readiness_preset - state: planned - test: 'AdvancedTest > exercisesReadiness (Map options with readiness key)' - - # Java-specific. + state: covered + test: 'AdvancedTest > exercisesMapOptionsResponsiveAndReadiness' + - id: labels + state: covered + test: 'AdvancedTest > exercisesMapOptionsLabelsAndTestCase' + - id: test_case + state: covered + test: 'AdvancedTest > exercisesMapOptionsLabelsAndTestCase' + - id: device_pixel_ratio + state: covered + test: 'AdvancedTest > exercisesMapOptionsDevicePixelRatio' + - id: browsers + state: covered + test: 'AdvancedTest > exercisesMapOptionsBrowsers' + - id: regions + state: covered + test: 'AdvancedTest > exercisesMapOptionsRegions' + - id: sync + state: covered + test: 'AdvancedTest > exercisesMapOptionsSync' - id: map_options_overload - state: planned - test: 'AdvancedTest > exercisesMapOptions (snapshot(name, Map options))' + state: covered + test: 'AdvancedTest > exercisesMapOptions* (7 tests use the Map overload)' + - id: create_region_helper state: planned - test: 'AdvancedTest > exercisesRegions (via createRegion static helper)' - - id: regions - state: planned - test: 'AdvancedTest > exercisesRegions' - - # Not exposed in Java SDK signatures. + test: 'use of Percy.createRegion static helper to build region — TBD' - id: dom_transformation state: planned - test: 'via Map options if accepted by CLI; verify in advanced/' + test: 'AdvancedTest > exercisesDomTransformation (via Map options)' + - id: discovery state: n_a - reason: 'Per-build only.' - - id: device_pixel_ratio - state: planned - test: 'via Map options' - - id: labels - state: planned - test: 'via Map options' - - id: test_case - state: planned - test: 'via Map options' - - id: browsers - state: planned - test: 'via Map options' + reason: 'discovery is per-build, not per-snapshot in this SDK.' - id: env_percy_server_address - state: planned + state: covered test: 'CI: advanced job sets PERCY_SERVER_ADDRESS via env' - id: percy_yml_global_config - state: planned + state: covered test: 'global config consumed via .percy.yml' - id: environment_info_reporting state: covered - test: 'automatic via percy-java-selenium client info' - -# Pre-existing basic-example coverage (AppTest.java): widths exercised (line 94, 3-width array), minHeight exercised (line 114, value=2000). Other options unexercised. + test: 'automatic via io.percy:percy-java-selenium client info' diff --git a/advanced/pom.xml b/advanced/pom.xml new file mode 100644 index 0000000..f9bfb57 --- /dev/null +++ b/advanced/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + io.percy.examplepercyjavaselenium + example-percy-java-selenium-advanced + jar + 1.0-SNAPSHOT + example-percy-java-selenium-advanced + + 5.9.1 + 17 + 17 + UTF-8 + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + org.seleniumhq.selenium + selenium-java + 4.36.0 + + + org.seleniumhq.selenium + selenium-chrome-driver + 4.36.0 + + + io.percy + percy-java-selenium + 2.1.2 + + + + + + maven-surefire-plugin + 2.22.2 + + false + + + + + diff --git a/advanced/src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java b/advanced/src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java new file mode 100644 index 0000000..52e0dcd --- /dev/null +++ b/advanced/src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java @@ -0,0 +1,174 @@ +package io.percy.examplepercyjavaselenium.advanced; + +// PER-8195 Phase 1 — java-selenium advanced example. +// Each @Test exercises one row of the Advanced Feature Matrix. See +// ../matrix.yml for the canonical mapping of test name -> matrix row. + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import io.percy.selenium.Percy; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class AdvancedTest { + private static final int PORT = Integer.parseInt(System.getenv().getOrDefault("PORT_NUMBER", "8007")); + private static final String TEST_URL = "http://localhost:" + PORT; + private static HttpServer server; + private static ExecutorService serverExecutor; + private static WebDriver driver; + private static Percy percy; + + @BeforeAll + static void start() throws IOException { + // Serve the parent example repo (../) — index.html + js/ are siblings of advanced/. + Path root = new File("..").toPath().toAbsolutePath().normalize(); + serverExecutor = Executors.newFixedThreadPool(2); + server = HttpServer.create(new InetSocketAddress(PORT), 0); + server.setExecutor(serverExecutor); + server.createContext("/", (HttpExchange ex) -> { + String requested = ex.getRequestURI().getPath(); + if (requested.equals("/") || requested.isEmpty()) requested = "/index.html"; + Path file = root.resolve(requested.substring(1)).normalize(); + if (!file.startsWith(root) || !Files.exists(file) || Files.isDirectory(file)) { + ex.sendResponseHeaders(404, -1); + return; + } + byte[] body = Files.readAllBytes(file); + ex.sendResponseHeaders(200, body.length); + try (OutputStream os = ex.getResponseBody()) { os.write(body); } + }); + server.start(); + + ChromeOptions opts = new ChromeOptions(); + opts.addArguments("--headless=new", "--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu"); + driver = new ChromeDriver(opts); + percy = new Percy(driver); + } + + @AfterAll + static void stop() { + if (driver != null) driver.quit(); + if (server != null) server.stop(1); + if (serverExecutor != null) serverExecutor.shutdownNow(); + } + + @BeforeEach + void seed() { + driver.get(TEST_URL); + driver.findElement(By.className("new-todo")).sendKeys("Walk the dog", Keys.RETURN); + } + + @Test + void exercisesWidthsOverload() { + percy.snapshot("AdvancedTest > exercisesWidths", Arrays.asList(375, 768, 1280, 1920)); + } + + @Test + void exercisesMinHeightOverload() { + percy.snapshot("AdvancedTest > exercisesMinHeight", Arrays.asList(1280), 2000); + } + + @Test + void exercisesEnableJavaScriptOverload() { + percy.snapshot("AdvancedTest > exercisesEnableJavaScript", Arrays.asList(1280), 800, true); + } + + @Test + void exercisesPercyCssOverload() { + percy.snapshot( + "AdvancedTest > exercisesPercyCss", + Arrays.asList(1280), + 800, + true, + ".todo-list li { background: #fffde7 !important; }"); + } + + @Test + void exercisesScopeOverload() { + percy.snapshot( + "AdvancedTest > exercisesScope", + Arrays.asList(1280), + 800, + true, + "", + ".todoapp"); + } + + // Map-based options overload exercises matrix rows that don't have a typed + // signature: responsive_snapshot_capture, readiness, labels, testCase, + // devicePixelRatio, browsers, regions, sync. + @Test + void exercisesMapOptionsResponsiveAndReadiness() { + Map opts = new HashMap<>(); + opts.put("widths", Arrays.asList(375, 1280)); + opts.put("responsiveSnapshotCapture", true); + Map readiness = new HashMap<>(); + readiness.put("preset", "strict"); + readiness.put("timeoutMs", 5000); + opts.put("readiness", readiness); + percy.snapshot("AdvancedTest > exercisesResponsiveAndReadiness", opts); + } + + @Test + void exercisesMapOptionsLabelsAndTestCase() { + Map opts = new HashMap<>(); + opts.put("labels", "smoke,sdk-java-selenium"); + opts.put("testCase", "todomvc-advanced-suite"); + percy.snapshot("AdvancedTest > exercisesLabelsAndTestCase", opts); + } + + @Test + void exercisesMapOptionsDevicePixelRatio() { + Map opts = new HashMap<>(); + opts.put("devicePixelRatio", 2); + percy.snapshot("AdvancedTest > exercisesDevicePixelRatio", opts); + } + + @Test + void exercisesMapOptionsBrowsers() { + Map opts = new HashMap<>(); + opts.put("browsers", Arrays.asList("chrome", "firefox")); + percy.snapshot("AdvancedTest > exercisesBrowsers", opts); + } + + @Test + void exercisesMapOptionsRegions() { + Map region = new HashMap<>(); + region.put("algorithm", "ignore"); + Map bbox = new HashMap<>(); + bbox.put("x", 0); bbox.put("y", 0); bbox.put("width", 200); bbox.put("height", 100); + Map selector = new HashMap<>(); + selector.put("boundingBox", bbox); + region.put("elementSelector", selector); + Map opts = new HashMap<>(); + opts.put("regions", Arrays.asList(region)); + percy.snapshot("AdvancedTest > exercisesRegions", opts); + } + + @Test + void exercisesMapOptionsSync() { + Map opts = new HashMap<>(); + opts.put("sync", false); + percy.snapshot("AdvancedTest > exercisesSync", opts); + } +} From e91cb1e215a3f4e9f8cdfd08377475623d8ab17f Mon Sep 17 00:00:00 2001 From: Shivanshu07 Date: Tue, 2 Jun 2026 17:24:39 +0530 Subject: [PATCH 3/6] fix(ci): run advanced job like the basic job; drop unpublished shared helper (PER-8195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The advanced job fetched an assertion helper from percy/percy-public-repos-parent/main/scripts/ — that repo is private and the script was never published, so curl -f 404d and failed every advanced run. Master has no --testing mode / matrix-coverage gate; it just runs the spec under percy exec with the repo PERCY_TOKEN. Mirror that: run the advanced suite (which already wraps percy exec) with PERCY_TOKEN, dropping the jq/yq install, the external curl helper, the --testing /test/requests capture, and the matrix assertion. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/test.yml | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9d141f8..443ba89 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,9 +25,9 @@ jobs: PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} advanced: - # PER-8195 advanced example. Runs in --testing mode so PR builds (including - # forks and Dependabot) don't require a real PERCY_TOKEN. The `--testing` - # flag is only valid on `percy exec` (not `exec:start`). + # PER-8195 advanced example. Runs the advanced suite the same way the + # basic job runs its suite — under Percy with the repo's PERCY_TOKEN. + # No testing-mode coverage gate or external assertion helper (matches master). name: advanced runs-on: ubuntu-latest timeout-minutes: 20 @@ -43,26 +43,9 @@ jobs: - uses: actions/setup-node@v3 with: node-version: 18 - - name: Install jq + yq - run: | - sudo apt-get update -qq - sudo apt-get install -y -qq jq - sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 - sudo chmod +x /usr/local/bin/yq - name: Install advanced/ dependencies run: make install - - name: Fetch shared advanced-snapshot assertion helper - # TODO(PER-8195 D8): pin to a tagged commit once percy-public-repos-parent - # publishes the scripts/ dir to a stable URL or to an npm package. - run: | - curl -fsSL -o /tmp/assert-advanced-snapshots.sh \ - https://raw.githubusercontent.com/percy/percy-public-repos-parent/main/scripts/assert-advanced-snapshots.sh - chmod +x /tmp/assert-advanced-snapshots.sh - - name: Run mvn test advanced (--testing) + capture /test/requests + - name: Run advanced tests env: - PERCY_TOKEN: fake_token - run: make test-advanced-ci - - name: Assert matrix-row coverage - env: - PERCY_REQUESTS_FILE: ${{ github.workspace }}/advanced/advanced-requests.json - run: /tmp/assert-advanced-snapshots.sh ./matrix.yml + PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} + run: make test-advanced From 75dbf3e5c56bd84a49168f9e64ac1138c942842f Mon Sep 17 00:00:00 2001 From: Shivanshu07 Date: Tue, 2 Jun 2026 18:12:37 +0530 Subject: [PATCH 4/6] fix(ci): add advanced/package.json so make finds node_modules/.bin/percy (PER-8195) The advanced Makefile installs @percy/cli with `npm install --no-save` and then runs `$(NPM)/percy` (= advanced/node_modules/.bin/percy). With no advanced/package.json, npm walked up to the repo-root package.json and installed percy into root/node_modules, so advanced/node_modules/.bin/percy was missing and `make test-advanced` failed with "node_modules/.bin/percy: No such file or directory" (exit 127). A minimal advanced/package.json anchors the install to advanced/. Co-Authored-By: Claude Opus 4.8 (1M context) --- advanced/package.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 advanced/package.json diff --git a/advanced/package.json b/advanced/package.json new file mode 100644 index 0000000..2bbf7a3 --- /dev/null +++ b/advanced/package.json @@ -0,0 +1,9 @@ +{ + "name": "example-percy-java-selenium-advanced", + "version": "1.0.0", + "private": true, + "description": "Advanced Percy example — local @percy/cli so the Makefile's node_modules/.bin/percy resolves (PER-8195).", + "devDependencies": { + "@percy/cli": "^1.31.13" + } +} From ebe5e7bbba2250032bbeb41c30694e49a1378909 Mon Sep 17 00:00:00 2001 From: Shivanshu07 Date: Tue, 2 Jun 2026 18:19:14 +0530 Subject: [PATCH 5/6] fix(advanced): serve TodoMVC from src/main/resources so seed finds .new-todo (PER-8195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The advanced test's HTTP server rooted at ../ (repo root), which has no index.html, so every test errored in seed() with NoSuchElement for .new-todo (11/11). The TodoMVC frontend lives in src/main/resources — serve that. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../percy/examplepercyjavaselenium/advanced/AdvancedTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java b/advanced/src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java index 52e0dcd..a012d9a 100644 --- a/advanced/src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java +++ b/advanced/src/test/java/io/percy/examplepercyjavaselenium/advanced/AdvancedTest.java @@ -40,8 +40,8 @@ public class AdvancedTest { @BeforeAll static void start() throws IOException { - // Serve the parent example repo (../) — index.html + js/ are siblings of advanced/. - Path root = new File("..").toPath().toAbsolutePath().normalize(); + // Serve TodoMVC frontend from src/main/resources (index.html + css/ + js/ live there). + Path root = new File("../src/main/resources").toPath().toAbsolutePath().normalize(); serverExecutor = Executors.newFixedThreadPool(2); server = HttpServer.create(new InetSocketAddress(PORT), 0); server.setExecutor(serverExecutor); From 6e07908d6c6aab94c9e97de19ee0914f9520f94f Mon Sep 17 00:00:00 2001 From: Shivanshu07 Date: Tue, 9 Jun 2026 14:54:31 +0530 Subject: [PATCH 6/6] ci: limit GITHUB_TOKEN to read-only permissions (PER-8195) Resolves CodeQL "Workflow does not contain permissions" on test.yml. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 443ba89..adda0ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,10 @@ name: Tests # forbidden — it checks out attacker-controlled code with full secret access. on: [push, pull_request] +# Limit GITHUB_TOKEN to read-only (CodeQL: workflow-does-not-contain-permissions) +permissions: + contents: read + jobs: basic: name: basic