Skip to content

test(e2e): drive the onboarding chain in a real browser - #48

Merged
basb7 merged 1 commit into
mainfrom
test/e2e-onboarding-chain
Sep 1, 2026
Merged

test(e2e): drive the onboarding chain in a real browser#48
basb7 merged 1 commit into
mainfrom
test/e2e-onboarding-chain

Conversation

@basb7

@basb7 basb7 commented Sep 1, 2026

Copy link
Copy Markdown
Owner

The first end-to-end test, and the first test in this repository that exercises a screen at all.

What it does

One test, the whole chain: register → create an organization → create a project → create an environment → load /dashboard/environments cold and find it there.

Deliberately not four independent tests. The chain working is the thing that broke; four tests that each build their own state would stop testing it.

Proven to fail, not just to pass

Reintroducing the original defect — dropping refreshCapabilities() from the tenant refresh() — turns it red on exactly the step that asserts a fresh admin is offered a project rather than told they lack access:

✘ a new account can register and walk the whole chain
  Error: expect(locator).toBeVisible() failed — element(s) not found

Restored, green again.

A test whose failure has never been observed is a claim, not a check.

Worth knowing for anyone doing this next: the frontend container has no source mount. A source change needs docker compose -f compose.dev.yml up -d --build frontend before the test sees it. The first mutation attempt appeared to prove the test didn't catch the bug — it had simply never reached the running app.

No data-testid anywhere

Every selector is getByRole / getByLabel / getByText. That discipline paid immediately: the first run hit a strict-mode violation because an environment name appears both as a badge and as an option in the environment filter. A CSS-class selector would have silently picked one.

Two accessibility gaps surfaced the same way, by the test having nothing to grab: the copy-API-key button has no accessible name, and the registration error banner has no role="alert". Both are fixed in the follow-up, not here.

CI

A third job, E2E (onboarding chain). The two existing jobs are untouched, and that is not stylistic: main's ruleset requires Backend (lint + tests) and Frontend (lint + build) by exact name, so renaming either makes it unsatisfiable and blocks every merge in the repository. npm test still runs node --test.

The new job is not in the ruleset, which is intentional — an end-to-end test should earn the right to block merges before it does.

Running the real stack

docker compose -f compose.dev.yml up -d from the repo root. Global setup polls the API first and fails naming that command, rather than timing out on a selector twenty seconds later.

Leftover data — stated, not solved

Each run leaves an account, organization, project and environment in the development database. No teardown, on purpose: tearing down means driving the delete-confirmation dialog, doubling the test's surface for no coverage, and a failed run's leftovers are exactly what you want to inspect. Everything a run creates shares one id, so a run's debris greps as a unit. CI ends with docker compose down -v.

Verification

npm run test:e2e     1 passed
npm test             16 passed (unchanged)
npm run lint         clean
npm run build        compiled
pytest               592 passed, zero .py files touched

🤖 Generated with Claude Code

The first end-to-end test, and the first test in this repository that
exercises a screen at all.

It walks the whole chain in one test -- register, create an organization,
create a project, create an environment, then load /dashboard/environments
cold and find it there. Deliberately not four independent tests: the chain
working is the thing that broke, and four tests that each build their own
state would stop testing it.

Proven to fail, not just to pass. Reintroducing the original defect --
dropping refreshCapabilities() from the tenant refresh -- turns it red on
the step that asserts a fresh admin is offered a project rather than told
they lack access. Restored, it is green again. A test whose failure has
never been observed is a claim, not a check.

No data-testid anywhere; every selector is getByRole/getByLabel/getByText.
That discipline paid immediately: the first run hit a strict-mode violation
because an environment name appears both as a badge and as an option in the
environment filter. A CSS-class selector would have silently picked one.

CI gets a third job rather than changes to the two existing ones. main's
ruleset requires 'Backend (lint + tests)' and 'Frontend (lint + build)' by
exact name, and renaming either makes it unsatisfiable and blocks every
merge in the repository. npm test still runs node --test, untouched.

The suite drives the real stack, so global setup polls the API first and
fails naming the compose command rather than timing out on a selector.

Each run leaves an account, organization, project and environment in the
development database. No teardown on purpose: tearing down means driving the
delete-confirmation dialog, doubling the surface for no coverage, and a
failed run's leftovers are exactly what you want to inspect. Everything a
run creates shares one id so its debris greps as a unit.
@basb7
basb7 merged commit 6cb1e04 into main Sep 1, 2026
3 checks passed
@basb7
basb7 deleted the test/e2e-onboarding-chain branch September 1, 2026 17:33
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.

1 participant