Skip to content

Pin the home page's sponsor strip in visual baselines - #18

Merged
JakeGinnivan merged 2 commits into
mainfrom
jakeginnivan/pin-sponsor-strip-in-visual-baselines
Sep 8, 2026
Merged

JakeGinnivan merged 2 commits into
mainfrom
jakeginnivan/pin-sponsor-strip-in-visual-baselines

Conversation

@JakeGinnivan

Copy link
Copy Markdown
Contributor

Upstreams dddwa/dddperth#156.

Why

/ is the only visual baseline with no year in its path, so it renders the current conference — and its sponsor strip moves whenever that conference signs, re-tiers or re-logos a sponsor. For a fork that means a 9-baseline regeneration for a routine content edit, and an easy way to sweep an unreviewed sponsor change into a regeneration done for something else.

It also recurs on a schedule: the strip flips to its fallback state when a new year starts empty, then flips back when the first sponsor lands.

What

E2E_SPONSOR_FIXTURES (set only by e2e/start-dev-server.mjs) swaps in e2e/fixtures/sponsors.ts, joining the date and Sessionize pins already here. The flag rides on AppConfig as plain per-request data, passed explicitly to resolveSponsorsWithFallback.

visual.spec.ts asserts a fixture logo is actually on the page — same rationale as the date pin's assertion.

Notes for review

Why AppConfig rather than module state. The fork's first version kept the flag in a module-level variable set from load-context.server.ts. Vite splits that into its own SSR chunk, so the resolver read a different instance and never saw the flag — the suite reported all-pass while the fixtures never loaded. The assertion exists so that can't recur silently.

What this does and doesn't cover. Swapping the whole sponsor strip does not by itself fail the home baseline: / is a tall full-page capture against a ratio gate (maxDiffPixelRatio: 0.02), and the strip is too small a share of that area to trip it — measured in the fork by rendering fixtures against real-sponsor baselines and watching all 9 pass. So / polices layout and chrome; real sponsor rendering stays covered by the year-pinned /sponsors/<year>.

One extra fix carried along. Core's date-pin assertion matched .countdown exactly, which fails for any conference configuring more than one date window (the Important Dates card renders one pill per window). Now asserts the conference-day countdown is among them. This is why visual.spec.ts shows more change than the sponsor seam alone.

Fixture logos are inline data: URIs, not files: everything under public/ is copied verbatim into the client build, so committed logo files ship to production as dead assets (verified in the fork before changing it). A test asserts this.

Baselines are core's own — regenerated against conference-stub in the pinned container, not copied from the fork.

Verification

  • 365 unit tests, 81 visual tests, typecheck + build clean.
  • Production build greps clean for the flag and the fixture data — the guard folds to a literal useSponsorFixtures: false.
  • 9 home baselines regenerated; no other baseline moved.

Follow-up

A second PR will bring core's sponsor portal up to date with the fork (logistics form, exhibitor export, the Jira client rework and Vicki's feedback). Core's portal is currently several fork PRs behind, which is how this split came about.

Forks should /core-pull after both land.

https://claude.ai/code/session_019eNk5wET3eWEqRFFtLihTV

Upstreams dddwa/dddperth#156.

`/` is the only visual baseline with no year in its path, so it renders the
current conference and its sponsor strip moves whenever that conference signs,
re-tiers or re-logos a sponsor — a 9-baseline regeneration for a routine
content edit, and an easy way to sweep an unreviewed sponsor change into a
regeneration done for something else. For a fork this recurs on a schedule:
the strip also flips to its fallback state when a new year starts empty, then
flips back when the first sponsor lands.

`E2E_SPONSOR_FIXTURES` (set only by e2e/start-dev-server.mjs) swaps in
e2e/fixtures/sponsors.ts, joining the existing date and Sessionize pins. The
flag travels on AppConfig as plain per-request data rather than module state:
Vite splits load-context into its own SSR chunk, so a module-level flag set
there is read from a *different* instance by the resolver — the fork's first
version failed exactly that way, silently, with the suite reporting all-pass
while the fixtures never loaded.

visual.spec.ts asserts a fixture logo is actually on the page, for the same
reason the date pin is asserted: when the seam doesn't apply the page still
renders fine, just with real sponsors, and the baselines quietly resume
tracking live config.

Worth recording, since it bounds what this trades away: swapping the whole
sponsor strip does not by itself fail the home baseline. `/` is a tall
full-page capture against a ratio gate (maxDiffPixelRatio: 0.02) and the strip
is too small a share of that to trip it — measured in the fork by rendering
fixtures against real-sponsor baselines and watching all 9 pass. `/` polices
layout and chrome; real sponsor rendering stays covered by the year-pinned
/sponsors/<year>.

Also carries a fork-portability fix to the date-pin assertion that core
lacked: the Important Dates card renders one pill per configured date window,
so matching `.countdown` exactly fails for any conference with more than one.
Now asserts the conference-day countdown is among them.

Fixture logos are inline data: URIs. Everything under public/ is copied
verbatim into the client build, so committed logo files would ship to
production as dead assets (verified in the fork before this was changed).

Baselines here are core's own, regenerated against conference-stub in the
pinned container — not the fork's.

Verified: 365 unit tests, 81 visual tests, typecheck + build clean, and the
production build greps clean for the flag and the fixture data (the guard
folds to `useSponsorFixtures: false`).

Claude-Session: https://claude.ai/code/session_019eNk5wET3eWEqRFFtLihTV
`setup` fans out to four generators with no ordering, so nx runs them
concurrently — and all four write into `website/`. `react-router typegen`
clears and rewrites `.react-router/types/` as it goes, so its rmdir can hit a
directory another generator has just repopulated:

    ENOTEMPTY: directory not empty, rmdir
    '.../website/.react-router/types/app/routes/+types'

That kills the visual-regression job before it takes a single screenshot. It
hit three PRs in a row (#16, #17, #18), each going green on a plain re-run,
which is what a race looks like rather than a real failure.

`parallelism: false` on the four setup targets serialises them. Verified by
running `nx setup` from a clean tree and again over existing generated output
— the repeat run is the case CI was failing on.

This is a stopgap: the targets are still unordered relative to each other, and
the workflow additionally runs `nx setup website` explicitly even though
`e2e-visual` already dependsOn it, so setup runs twice per job. Worth tidying
properly, but serialising fixes the flake now.

Claude-Session: https://claude.ai/code/session_019eNk5wET3eWEqRFFtLihTV
@JakeGinnivan
JakeGinnivan merged commit 0a96392 into main Sep 8, 2026
3 checks passed
@JakeGinnivan
JakeGinnivan deleted the jakeginnivan/pin-sponsor-strip-in-visual-baselines branch September 8, 2026 03:53
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