Skip to content

Add Playwright UI tests for the JWT build feature editor#118

Merged
matt-richardson merged 7 commits into
mainfrom
test/playwright-build-feature-ui
May 20, 2026
Merged

Add Playwright UI tests for the JWT build feature editor#118
matt-richardson merged 7 commits into
mainfrom
test/playwright-build-feature-ui

Conversation

@matt-richardson
Copy link
Copy Markdown
Contributor

Background

The JWT build feature editor has accumulated enough custom JavaScript — the subject-dimensions checkbox group, the live "Resulting sub claim" preview, the none sentinel for the empty-dimensions case, the algorithm/TTL/audience fields, server-side validation errors — that regressions in the form's persistence behaviour are easy to introduce and hard to spot without actually clicking through it. Until now there was no automated coverage of the editor as it renders in a browser; everything was either a JSP unit test or a Selenium-free Testcontainers IT that talked HTTP to the REST API.

Details

The kinds of regressions we've hit historically:

  • Unchecking all subject dimensions silently collapses back to the default on reload (this was the none sentinel fix in Make sub customisable rather than claims #112 and reviewers' "Unticking the 'trigger' claim doesn't actually stick on save" item).
  • The live sub preview drifting out of sync with the persisted form.
  • Server-side validation errors not blocking the save and instead persisting bad state.

These all live above the JSP/JavaScript boundary, so JSP-only unit tests can't catch them.

Results

New integration test class BuildFeatureUIIT under integration-tests/, run by Failsafe in the existing mvn verify phase. It:

  • Spins up jetbrains/teamcity-server:2025.11 via Testcontainers with the plugin zip mounted.
  • Creates a project and build type through the REST API so each test gets a clean editor.
  • Drives the editor in Chromium via Playwright and asserts on the rendered DOM and the persisted form state.

Eleven scenarios are covered:

Scenario What it pins down
allDimensionsUncheckedByDefaultInUi Default UI state matches the documented default — no optional dimensions in sub.
checkingDimensionPersistsAfterSave Ticking a checkbox round-trips through save/reopen.
uncheckingAllDimensionsSavesEmptyPropertyValue The none sentinel persists; doesn't collapse to default.
algorithmSelectionPersistsAfterSave Algorithm field round-trips.
ttlMinutesPersistsAfterSave TTL field round-trips.
audiencePersistsAfterSave Audience field round-trips.
liveSubjectPreviewIsMinimalByDefault Live sub preview reflects the default config.
liveSubjectPreviewReflectsCheckboxToggle Live sub preview updates as checkboxes toggle.
savedSubjectDimensionsArePreCheckedOnReopen Reopen renders the saved selection, not the default.
invalidTtlShowsValidationErrorAndDoesNotSave Server-side TTL validation blocks save.
unknownSubjectDimensionShowsValidationErrorAndDoesNotSave Server-side subject-dimension validation blocks save.

Build-side changes in integration-tests/pom.xml:

  • Adds com.microsoft.playwright:playwright:1.44.0 as a test-scope dependency.
  • Adds an exec-maven-plugin execution in pre-integration-test that runs com.microsoft.playwright.CLI install chromium in a forked JVM. The fork is required because CLI.main() calls System.exit() when finished, which would kill Surefire/Failsafe if it ran in the build JVM.

Screenshots

N/A — this PR adds tests against existing UI; no UI changes of its own.

matt-richardson and others added 5 commits May 14, 2026 15:45
Spins up a real TeamCity 2025.11 server via Testcontainers, creates a
project and build type via the REST API, then drives the build feature
editor in Chromium and asserts that form state round-trips correctly.

Covers eleven scenarios: default-uncheckedness of subject dimensions,
persistence of each editable field (subject dimensions, algorithm,
TTL, audience) across save/reopen, the live "Resulting sub claim"
preview reflecting checkbox toggles, the empty-dimensions sentinel
("none") persisting rather than silently collapsing back to the
default, and the two server-side validation errors (invalid TTL,
unknown subject dimension) blocking save.

Adds Playwright 1.44 as a test-scope dependency and an
exec-maven-plugin execution in pre-integration-test phase that runs
`com.microsoft.playwright.CLI install chromium` in a forked JVM (the
CLI calls System.exit() so it can't share the test JVM).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous maven:3.9-amazoncorretto-21 tag is built on Amazon Linux 2,
which ships glibc 2.26. Playwright 1.44 bundles a Node binary that
requires glibc 2.27/2.28, so the playwright-install-chromium Maven
goal added in the previous commit fails to start the bundled Node
before the tests can even run.

The -al2023 variant has the same JDK distribution and Maven version
on Amazon Linux 2023, which ships glibc 2.34 — enough for the
bundled Node to load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous bump to Amazon Linux 2023 cleared the glibc issue and
let the playwright-install-chromium goal complete, but Chromium itself
refuses to launch on AL2023 because the system libraries it links
against at runtime (libnss3, libatk, libgbm, libxkbcommon, etc) are
not present. Playwright's install-deps helper shells out to apt-get,
which does not exist on AL2023, and Playwright does not publish a
dnf equivalent or AL2023 package mapping.

Switch the integration-tests image to maven:3.9-eclipse-temurin-21
(Debian bookworm, glibc 2.36) and add --with-deps to the playwright
install argument list. That single flag tells Playwright to apt-get
install the dependency set it knows Chromium needs, in addition to
downloading the browser binary itself.

Two CI failures in a row caused by image-distro choice — the underlying
issue was that Playwright is built and tested against Debian/Ubuntu,
and patching around a non-supported distro keeps producing new failure
modes (first glibc, then runtime .so dependencies). Moving to a base
image Playwright already targets removes that whole class of problem.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XML forbids `--` anywhere inside a comment body except at the opening
and closing boundaries. The prose in the playwright-install-chromium
comment referred to the flag literally as `--with-deps`, which the
Maven XML parser rejected with:

  [FATAL] Non-parseable POM ... in comment after two dashes (--)
  next character must be > not w

The `<argument>--with-deps</argument>` element below the comment is
fine — element content has no such restriction. Only the comment
body needed adjusting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Playwright 1.44.0 was released before Ubuntu 24.04 (noble) shipped, so
its `install --with-deps` helper hardcodes `libasound2` in the apt-get
package list. Noble renamed that package to `libasound2t64` as part of
the 64-bit time_t transition, and the install fails with:

  E: Package 'libasound2' has no installation candidate
  Failed to install browsers
  Error: Installation process exited with code: 100

The previous image bump landed on `maven:3.9-eclipse-temurin-21`, which
turns out to be Ubuntu noble in current rolling releases (not Debian
bookworm as the prior commit message anticipated — the eclipse-temurin
base moved to noble during the 24.04 LTS rollout). Pinning the image
to jammy would fix this for our specific case, but the underlying cause
is the year-old Playwright pin.

Bumping to 1.52.0 (current latest) gives us noble support in
install-deps plus eight minor versions of bug fixes. The test only
uses core Browser/Page/Locator APIs which are stable across this
range; local `mvn test-compile -pl integration-tests` succeeds with
no source changes needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

integration-tests:
image: maven:3.9-amazoncorretto-21
image: maven:3.9-eclipse-temurin-21
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to switch this around, to get a base image that playwright could run on

@matt-richardson matt-richardson self-assigned this May 18, 2026
@matt-richardson matt-richardson marked this pull request as ready for review May 18, 2026 03:43
@matt-richardson matt-richardson requested a review from a team as a code owner May 18, 2026 03:43
Copy link
Copy Markdown

@mattburnell mattburnell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very nice! I like the break-up, the lifecycle of things, and the tests themselves are very easy to read. There are a couple of places I think the last vestiges of repetition could be removed, but overall this is really nicely factored.

Comment thread integration-tests/pom.xml Outdated
</configuration>
</plugin>
<!--
Downloads Chromium browser binaries and the system shared libraries
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While not inaccurate, it might be worth mentioning in the comment that Playwright itself is used to download / set up these things. It's definitely covered in the code / definition below, but the wording here made me think "wait, I thought Playwright could manage all this for you..." and then I saw that it's just calling it with --with-deps chromium and thought "ahh, good".


@Test
void checkingDimensionPersistsAfterSave() throws Exception {
// Uses trigger_type because the branch checkbox is only rendered when the build
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment offers more confusion than enlightenment; is it just a Claude vestige?

saveBuildFeature(page);
}

// Phase 2: uncheck every checkbox, verify empty subject_dimensions is stored
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not super-harmful repetition in this case by any means, but this could be a HoF (e.g. withAllDimensionCheckboxes) that takes an action on a checkbox, and is just called with x => x.check() and x => x.uncheck()


@Test
void algorithmSelectionPersistsAfterSave() throws Exception {
try (final var context = newLoggedInContext()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building on my comment above about a HoF... they could be stacked. Many of these tests have the same 5-line block that's "build context, navigate to page, , call save".

matt-richardson and others added 2 commits May 20, 2026 11:27
The previous wording read like the Maven plugin itself was downloading
Chromium and apt-installing its deps, which left a reviewer wondering
"wait, doesn't Playwright handle that?" Reword to lead with "Invokes
Playwright's own CLI" so the relationship is clear (and avoid literal
'--' inside an XML comment, which is illegal).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every test was spelled out as the same 5-line "open context, new page,
navigate to editor, do thing, [save]" block. Extract two stacked helpers
so each test reads as the action that matters:

* inFeatureEditor(Consumer<Page>) — opens a fresh logged-in editor and
  runs the action; used by read-only and validation-error tests.
* editFeature(Consumer<Page>) — same, plus clicks Save and waits for
  the modal to close; used by the round-trip persistence tests.

A third helper, forEachDimensionCheckbox(page, action), collapses the
two identical check/uncheck loops in uncheckingAllDimensionsSaves... to
forEachDimensionCheckbox(page, Locator::check) / Locator::uncheck.

Also drops a misleading comment on checkingDimensionPersistsAfterSave
that explained why it tested trigger_type — picking any single dimension
is fine; the comment was conversational noise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@matt-richardson matt-richardson enabled auto-merge (squash) May 20, 2026 01:29
@matt-richardson matt-richardson merged commit 8d2affe into main May 20, 2026
3 checks passed
@matt-richardson matt-richardson deleted the test/playwright-build-feature-ui branch May 20, 2026 01:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants