Answer prose round 1: 0 🔴, 4 🟡 (0 misread); document the e2e typecheck, tighten comments and changelog

This commit is contained in:
2026-10-31 08:24:18 +01:00
parent cd46c77332
commit 4e7d897516
7 changed files with 12 additions and 9 deletions
+2 -1
View File
@@ -358,6 +358,7 @@ Docker Compose. **Never run `node`, `npm`, or `tsc` on the host.**
```bash ```bash
docker compose up # dev server, live reload docker compose up # dev server, live reload
docker compose run --rm --no-deps web npm run typecheck # strict type check (--no-deps: skip Ory) docker compose run --rm --no-deps web npm run typecheck # strict type check (--no-deps: skip Ory)
docker compose -f compose.yml -f e2e-tests/compose.visual.yml run --user "$(id -u):$(id -g)" --rm --no-deps --build e2e npm run typecheck # the E2E specs
docker compose run --rm --no-deps web npm test # tests docker compose run --rm --no-deps web npm test # tests
docker compose -f compose.yml up --build -d # production docker compose -f compose.yml up --build -d # production
``` ```
@@ -391,7 +392,7 @@ one-time setup. A file-map or table row gets a clause, not a paragraph.
If a file genuinely must be plain JavaScript, use `.js`; `"type": "module"` is set in both If a file genuinely must be plain JavaScript, use `.js`; `"type": "module"` is set in both
`package.json`s, so `.js` is ESM. `package.json`s, so `.js` is ESM.
- **No build step** and no compiled artifacts — do not add a bundler or `tsc` emit. - **No build step** and no compiled artifacts — do not add a bundler or `tsc` emit.
- Before finishing a change, run the typecheck and tests above; both must pass. - Before finishing a change, run the typechecks and tests above; all must pass.
- Tests use the built-in `node --test` runner — no test framework dependency. - Tests use the built-in `node --test` runner — no test framework dependency.
- English everywhere. - English everywhere.
- Pin all dependencies and Docker images to exact, human-readable **semantic versions** — never - Pin all dependencies and Docker images to exact, human-readable **semantic versions** — never
+3 -2
View File
@@ -8,8 +8,9 @@ at boot. Entries start at 0.3.0.
### Every page carries a smaller icon sprite ### Every page carries a smaller icon sprite
The inlined sprite no longer holds `i-chart`, `i-copy` and `i-download`, which nothing in the host or `i-chart`, `i-copy` and `i-download` are gone from the inlined icon sprite, so a plugin that
the example plugins referenced. A plugin that referenced one renders a blank icon until the host registers it again. referenced one now renders a blank icon. Nothing in the host or the example plugins used them. Pick
another id from `views/partials/icons.ejs`, or inline your own SVG.
### The README defines its terms and states its intended scale ### The README defines its terms and states its intended scale
+2 -1
View File
@@ -1493,6 +1493,7 @@ Type check and unit tests run off the Ory stack — `--no-deps` keeps `web` from
```bash ```bash
docker compose run --rm --no-deps web npm run typecheck # strict tsc --noEmit docker compose run --rm --no-deps web npm run typecheck # strict tsc --noEmit
docker compose run --rm --no-deps web npm test # node --test (units) docker compose run --rm --no-deps web npm test # node --test (units)
docker compose -f compose.yml -f e2e-tests/compose.visual.yml run --user "$(id -u):$(id -g)" --rm --no-deps --build e2e npm run typecheck # the E2E specs
``` ```
### End-to-end (Playwright) ### End-to-end (Playwright)
@@ -1502,7 +1503,7 @@ no Node/browsers on the host. Five suites, each on its own stack:
| Suite | Compose overlay | Covers | | Suite | Compose overlay | Covers |
| --- | --- | --- | | --- | --- | --- |
| `visual.spec.ts` + `language.spec.ts` | `e2e-tests/compose.visual.yml` | Ory-free: the rendered design system (app shell at five widths from 1920px to 320px, theme switch, mobile off-canvas, icon sprite, CSRF-guarded sign-out, landing, 404), plugin permission-gating, and [language switching](#languages-i18n) | | `visual.spec.ts` + `language.spec.ts` | `e2e-tests/compose.visual.yml` | Ory-free: the rendered design system (app shell, theme switch, mobile off-canvas, icon sprite, CSRF-guarded sign-out, landing, 404), plugin permission-gating, and [language switching](#languages-i18n) |
| `auth-refresh.spec.ts` | `e2e-tests/compose.auth.yml` | The real Ory stack with the session→JWT TTL cut to 8s: a lapsed JWT is silently re-minted from the live Kratos session, and once revoked the stale cookie is cleared | | `auth-refresh.spec.ts` | `e2e-tests/compose.auth.yml` | The real Ory stack with the session→JWT TTL cut to 8s: a lapsed JWT is silently re-minted from the live Kratos session, and once revoked the stale cookie is cleared |
| `oauth-login.spec.ts` | `e2e-tests/compose.oauth.yml` | Another app logging in through us — register a client, start an authorization flow, `/oauth2/login` accept, `/oauth2/consent` Allow → authorization code | | `oauth-login.spec.ts` | `e2e-tests/compose.oauth.yml` | Another app logging in through us — register a client, start an authorization flow, `/oauth2/login` accept, `/oauth2/consent` Allow → authorization code |
| `full-flow.spec.ts` | `e2e-tests/compose.full.yml` | The browser UI: password + mocked-SSO login, menu filtering by permission, admin users/groups/permissions CRUD, OAuth2-clients register → one-time secret → delete, a gated plugin page, logout | | `full-flow.spec.ts` | `e2e-tests/compose.full.yml` | The browser UI: password + mocked-SSO login, menu filtering by permission, admin users/groups/permissions CRUD, OAuth2-clients register → one-time secret → delete, a gated plugin page, logout |
+2 -2
View File
@@ -53,8 +53,8 @@ docker compose build web
step "Typecheck" step "Typecheck"
docker compose run --rm --no-deps web npm run typecheck docker compose run --rm --no-deps web npm run typecheck
# The specs import @playwright/test, which only the runner image carries, so the host's tsc can't # The specs import @playwright/test, which only the runner image installs, so they typecheck there
# resolve them: typecheck them in that image. --no-deps skips web and Ory; any suite's file builds it. # rather than in the web image. --no-deps skips web; the visual file just names the e2e service.
step "Typecheck e2e-tests" step "Typecheck e2e-tests"
docker compose -f compose.yml -f e2e-tests/compose.visual.yml run --user "$(id -u):$(id -g)" --rm --no-deps --build e2e npm run typecheck docker compose -f compose.yml -f e2e-tests/compose.visual.yml run --user "$(id -u):$(id -g)" --rm --no-deps --build e2e npm run typecheck
+1 -1
View File
@@ -10,7 +10,7 @@ const SESSION_COOKIE = "plainpages_jwt"; // src/auth/login.ts — web verifies i
const shot = (page: Page, name: string): Promise<Buffer> => const shot = (page: Page, name: string): Promise<Buffer> =>
page.screenshot({ fullPage: true, path: `artifacts/screenshots/${test.info().project.name}/${name}.png` }); page.screenshot({ fullPage: true, path: `artifacts/screenshots/${test.info().project.name}/${name}.png` });
// Large desktop down to a small phone, either side of the stylesheet's 860px and 560px breakpoints. // Either side of the stylesheet's 860px and 560px breakpoints.
const VIEWPORTS = [ const VIEWPORTS = [
{ name: "desktop-xl", width: 1920, height: 1080 }, { name: "desktop-xl", width: 1920, height: 1080 },
{ name: "laptop", width: 1280, height: 800 }, { name: "laptop", width: 1280, height: 800 },
+1 -1
View File
@@ -29,7 +29,7 @@ test("the commit-hash image is pushed even when the gate no-ops", () => {
assert.doesNotMatch(step("docker push"), /^\s*if:/m); assert.doesNotMatch(step("docker push"), /^\s*if:/m);
}); });
test("every E2E suite the gate runs writes its artifacts as the invoking user", () => { test("every E2E runner container the gate starts runs as the invoking user", () => {
// The documented hand-run commands carry the same flag (src/compose.test.ts). // The documented hand-run commands carry the same flag (src/compose.test.ts).
const runs = gate.split("\n").filter((l) => /docker compose .*\brun\b.*\be2e\b/.test(l)); const runs = gate.split("\n").filter((l) => /docker compose .*\brun\b.*\be2e\b/.test(l));
assert.equal(runs.length, 3, "the e2e-tests typecheck, the suite helper and the devstack run"); assert.equal(runs.length, 3, "the e2e-tests typecheck, the suite helper and the devstack run");
+1 -1
View File
@@ -129,7 +129,7 @@ test("the E2E runner writes its artifacts as the invoking user, never as root",
// filter would otherwise leave that command silently unguarded. // filter would otherwise leave that command silently unguarded.
const documented = [read("README.md"), ...composeFiles("e2e-tests/").map(read)] const documented = [read("README.md"), ...composeFiles("e2e-tests/").map(read)]
.join("\n").split("\n").filter((l) => /docker compose .*\brun\b.*\be2e\b/.test(l)); .join("\n").split("\n").filter((l) => /docker compose .*\brun\b.*\be2e\b/.test(l));
assert.equal(documented.length, 6, "5 compose headers + 1 README block"); assert.equal(documented.length, 7, "5 compose headers + 2 README lines");
for (const l of documented) for (const l of documented)
assert.match(l, /--user "\$\(id -u\):\$\(id -g\)"/, `passes the uid: ${l.trim()}`); assert.match(l, /--user "\$\(id -u\):\$\(id -g\)"/, `passes the uid: ${l.trim()}`);
// An absent mount source is daemon-created as root, and then that uid can't write it at all. // An absent mount source is daemon-created as root, and then that uid can't write it at all.