Skip to content

test(strategy): stand up the testing trophy and rework the interface - #18

Merged
arnaud-zg merged 2 commits into
refactor/object-calisthenics-domainfrom
feat/ui-ux-rework
Sep 2, 2026
Merged

arnaud-zg merged 2 commits into
refactor/object-calisthenics-domainfrom
feat/ui-ux-rework

Conversation

@arnaud-zg

@arnaud-zg arnaud-zg commented Sep 2, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #7.

Summary

  • a real interface pass: design tokens replace the leftover CRA colors, a dark mode
    toggle (light/dark/system, persisted), proper <main>/<footer> landmarks, a cart
    sheet on mobile with a sticky summary on desktop, 44px touch targets
  • feedback and state that was missing: aria-live cart totals, empty and focus states, a
    focus trap in the welcome modal, lazy images with explicit dimensions to kill layout
    shift, reduced motion respected everywhere, reportWebVitals actually wired up
  • a testing trophy under all of it: Vitest split into unit (domain, node) and
    integration (UI, jsdom) projects, coverage thresholds enforced in CI, two Playwright
    smoke specs against a built preview, CONTRIBUTING.md documenting the shape and the
    TDD workflow
flowchart TD
  A["Static: typecheck, Biome"] --> B["Unit: src/domain"]
  B --> C["Integration: src/ui, jsdom"]
  C --> D["E2E: 2 Playwright specs"]
Loading

Test plan

  • pnpm typecheck
  • pnpm check
  • pnpm lint:md
  • pnpm test:coverage, thresholds pass
  • pnpm build
  • pnpm test:e2e
  • manually checked light/dark/system theme, mobile cart sheet, keyboard focus trap

🤖 Generated with Claude Code

https://claude.ai/code/session_014BcGPRc3sgUJYKihGRXh5g

arnaud-zg and others added 2 commits September 2, 2026 00:13
- vitest.config.ts: two projects, unit (node, src/domain/**) and integration (jsdom,
  src/ui/** and src/app/**), replacing the vite.config.ts that never had a test block
- coverage thresholds (v8 provider): 90% on src/domain, 70% on src/ui, enforced in CI
- integration tests for the flows that matter: list products, add to cart, change
  quantity, remove an item, the free-shipping banner and the "Free" badge, the welcome
  survey's auto-open/save/close cycle
- add the ZustandWelcomeSurveyRepository spec it never had, the third storage
  implementation now has the same coverage as the other two
- Playwright, 2 smoke specs against a built vite preview: home to shop, and add to cart
  through to the checkout modal. Runs as its own CI job with the report uploaded on
  failure
- CONTRIBUTING.md documents TDD (with two real examples from this repo's own history)
  and the trophy shape

Found and fixed along the way, surfaced by writing real tests against real components:
- every umami?.track(...) call site threw ReferenceError whenever the global umami
  script hadn't loaded (blocked, offline, or simply not present, which is exactly what
  jsdom is). Switched to window.umami?.track(...), and typed it as a Window property
  instead of a bare ambient global
- tsconfig.json (the app build) was type-checking spec files with none of the vitest/
  jest-dom types available, and tsconfig.vitest.json was missing globals.d.ts and
  vite-env.d.ts, so window and import.meta.env typings weren't visible to it either
- every real catalog product costs far more than COMMERCE_CONFIG.freeShippingThreshold,
  so the "add X more to earn free shipping" banner can never appear against real data.
  Covered with a mocked, deliberately cheap catalog instead of leaving it untested

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BcGPRc3sgUJYKihGRXh5g
Correctness:
- gate TanStackRouterDevtools behind import.meta.env.DEV and lazy-load it, it was
  shipping to production unconditionally
- reportWebVitals() was called with no callback, so its own guard clause made it a
  no-op forever. Wired it to actually forward Core Web Vitals to Umami
- moved the lore video iframe out of ModalDescription (a <p>, invalid HTML for an
  iframe, and it made the dialog's accessible description empty), gave it a real
  description, and dropped ?autoplay=1 (WCAG 1.4.2)
- AnimatePresence wrapped a single stable-keyed container instead of the cart items
  themselves, so item exit animations never fired

Accessibility:
- --ring was too light to pass 3:1 non-text contrast in either theme, darkened both
- the modal close button had a ~16px hit area, now 44px with a visible hover state
- "Read Lore" was a button that called window.open(), now a real link
- the mobile cart toggle gets aria-expanded/aria-controls, the cart is a landmark
  (<aside aria-labelledby>), the product grid and cart items are real lists
- cart items promoted from h4 to h3 (Your Inventory was h2 straight to h4), and their
  images and the product images go alt="" since the adjacent text already names them
- the cart total is aria-live, the free-shipping Free badge got real color contrast

Design and layout:
- a working light/dark/system theme toggle, using tokens that already existed in
  styles.css but nothing ever applied
- replaced the CRA-era hardcoded hex colors on the header and home page with the
  site's own tokens
- fixed the product grid's premature 3-column squeeze between 1024 and 1280px
  (xl: instead of lg:), gave the sticky cart a max-height with its own scroll
  region instead of growing past the viewport, dropped the mt-14 alignment hack
- extracted CartSummaryRow, removing ~80 lines of duplicated subtotal/shipping/tax
  markup from ShoppingCart.tsx
- added a footer landmark, first one in the app
- deleted dead files: styles/index.css (unimported Vite scaffolding that would have
  broken the layout if anyone ever did import it), sheet.tsx, input.tsx, react.svg,
  vite.svg
- fixed public/manifest.json, still named "TanStack App", and linked it from
  index.html

Performance:
- the first 3 product cards load eagerly with fetchpriority high instead of lazy,
  they're the ones competing for LCP
- preconnect to the image host and to Umami

Coverage-worthy new code got its own tests: theme.ts and ThemeToggle.

Not done here, called out for later: full i18n extraction (next PR), and the OG image /
canonical / hreflang pass (the SEO PR after that).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BcGPRc3sgUJYKihGRXh5g
@arnaud-zg arnaud-zg changed the title TEMP test(strategy): stand up the testing trophy and rework the interface Sep 2, 2026
@arnaud-zg arnaud-zg self-assigned this Sep 2, 2026
@arnaud-zg arnaud-zg added the enhancement New feature or request label Sep 2, 2026
@arnaud-zg arnaud-zg closed this Sep 2, 2026
@arnaud-zg
arnaud-zg deleted the feat/ui-ux-rework branch September 2, 2026 19:01
@arnaud-zg
arnaud-zg restored the feat/ui-ux-rework branch September 2, 2026 19:13
@arnaud-zg arnaud-zg reopened this Sep 2, 2026
@arnaud-zg
arnaud-zg merged commit e23410e into main Sep 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant