Skip to content

test(goldens): switch default locale to de + handbook gap coverage (stacked on #541)#562

Merged
TaprootFreak merged 6 commits into
feat/visual-regression-pilotfrom
feat/visual-regression-handbook-gaps
May 25, 2026
Merged

test(goldens): switch default locale to de + handbook gap coverage (stacked on #541)#562
TaprootFreak merged 6 commits into
feat/visual-regression-pilotfrom
feat/visual-regression-handbook-gaps

Conversation

@TaprootFreak
Copy link
Copy Markdown
Contributor

Summary

Stacked on #541 (which is itself the merge bus for #552). Two changes that together unblock unifying the Maestro handbook screenshots with the Golden baselines (see plan at ~/Documents/Claude/realunit-handbook-unification-plan.md):

  1. Locale switch en → de: wrapForGolden defaulted to Locale('en'), so all 59 current Goldens render in English. The Maestro handbook pins the simulator to de_CH and captures German UI — the two pipelines cannot share images while they speak different languages.
  2. Handbook gap coverage: three new Goldens for handbook pages that had no Golden equivalent:
    • create_wallet_page_revealed — handbook 05-seed-revealed (state variant of state.hideSeed=false)
    • settings_seed_page_revealed — handbook 19-settings-seed-revealed (showSeed=true)
    • settings_confirm_logout_wallet_sheet_default — handbook 24-settings-delete-wallet (modal in initial unchecked state)

Mapping audit (Phase 0)

Verified against .maestro/handbook/*.yaml:

Handbook page Golden Status
01–09, 11–16, 18, 20–23, 25 existing
05-seed-revealed new ✅ this PR
17-settings-backup-pin ⚠️ deferred (state variant of verify_pin_page, needs context-aware test setup)
19-settings-seed-revealed new ✅ this PR
24-settings-delete-wallet new ✅ this PR
26-terms legal_document_page_default ⚠️ to verify visually whether the bound content matches
10-biometric-prompt out of scope: iOS system bottom sheet from LocalAuthentication, not rendered by Flutter — Skia cannot reproduce it. Will be discussed before Phase 1 (Dockerfile.handbook switch).

BackdropFilter validation

The existing settings_seed_page_default Golden already proves that Flutter's headless Skia renders BackdropFilter correctly (the blur is visible, not the historic XCUITest-black-PNG issue). Same applies to the new revealed/hidden state variants and the create_wallet_view's SeedBlurCard.

Bootstrap workflow

.github/workflows/golden-bootstrap.yaml is re-introduced temporarily, triggered by push to this branch. It runs flutter test test/goldens --update-goldens on the realunit-app self-hosted dfx01 runner and uploads the regenerated PNGs as golden-baselines. I download the artifact, commit the baselines into test/goldens/screens/**/goldens/macos/, then delete the bootstrap workflow file in a follow-up commit — same pattern as the pilot PR.

Test plan

  • golden-bootstrap workflow run completes green on dfx01
  • Baselines downloaded + committed
  • golden-bootstrap.yaml removed
  • Visual Regression job in pull-request.yaml green on final commit
  • Spot-check sample DE Goldens visually match the handbook screenshots
  • Decide on 10-biometric-prompt and 17-settings-backup-pin before promoting to ready-for-review

Out of scope

  • Dockerfile.handbook switch from docs/handbook/screenshots/ to test/goldens/ (Phase 1 of the unification plan)
  • Maestro pipeline retirement / nightly-only mode (Phase 2)

Switches wrapForGolden's default Locale from 'en' to 'de' so the Goldens
match the German UI that the Maestro handbook captures with de_CH simulator
locale. This unblocks unifying handbook screenshots and golden baselines —
without the switch the handbook would lose German strings.

Adds three new gap-coverage tests for handbook pages without a Golden:
  - create_wallet revealed state (handbook 05-seed-revealed)
  - settings_seed revealed state (handbook 19-settings-seed-revealed)
  - SettingsConfirmLogoutWalletSheet modal (handbook 24-settings-delete-wallet)

The temporary golden-bootstrap workflow regenerates all baselines on the
dfx01 self-hosted runner and uploads them as an artifact. It will be
removed in the same PR once the new baselines are committed.
Outputs of golden-bootstrap.yaml on dfx01 run 26343749115:
  - 56 existing baselines regenerated in German (the en → de switch
    re-renders every screen the locale switch touched, which is
    effectively all of them — only icons/empty-state graphics
    survived byte-identical from before)
  - 3 new baselines for the handbook gap-coverage tests:
      create_wallet_page_revealed
      settings_seed_page_revealed
      settings_confirm_logout_wallet_sheet_default

Spot-checked vs docs/handbook/screenshots/ on de_CH Maestro flows —
the rendered German strings match (Wallet-Sicherung, Hier tippen um
anzuzeigen, …). The 12 recovery words on the revealed Golden display
in the same 2-column grid as the handbook screenshot.

Removes .github/workflows/golden-bootstrap.yaml — it was only needed
to populate this run's baseline artifact.
Material's global ThemeData(fontFamily: 'Open Sans') does NOT propagate
through WidgetStatePropertyAll<TextStyle> overrides — Material applies
the override 1:1 with no theme-merge fallback. So filledButtonTheme and
textButtonTheme's textStyle, which inherited from RealUnitTextStyle.body
(no fontFamily) plus copyWith(fontWeight: w600), resolved to Flutter's
default font.

In headless flutter_test the default is Ahem, which renders every glyph
as a filled rectangle — hence the AppFilledButton / AppTextButton labels
appearing as horizontal white boxes (one box per word, wrap-aware) in
the visual-regression baselines. On real iOS the system font fallback
hides the bug, but the buttons still don't render in Open Sans there.

Adds fontFamily: RealUnitTextStyle.fontFamily to both button-theme
textStyles. Baselines will be regenerated on dfx01 via the temporary
golden-bootstrap workflow (re-introduced in this commit, removed once
the new baselines are committed).
@TaprootFreak TaprootFreak force-pushed the feat/visual-regression-handbook-gaps branch from bd23abd to 81db267 Compare May 23, 2026 21:50
@TaprootFreak TaprootFreak marked this pull request as ready for review May 23, 2026 21:52
Outputs of golden-bootstrap.yaml on dfx01 run 26344455196.

The previous baselines (commit e738f07) rendered the
AppFilledButton/AppTextButton labels as Ahem boxes because the
button-textStyle override in lib/styles/themes.dart dropped the
Open Sans fontFamily. After the theme fix (81db267 → adds explicit
fontFamily on filledButtonTheme/textButtonTheme.textStyle) every
button label now renders correctly — verified by spot-check against
create_wallet_page_default.png ('Ich habe es gesichert' visible in
Open Sans w600 instead of 4 white rectangles).

Removes .github/workflows/golden-bootstrap.yaml — done with its
second-and-final run for this PR.
TaprootFreak added a commit that referenced this pull request May 25, 2026
…ed on #568) (#570)

## Summary

Stacked on #568. Phase 1 of the Maestro → Goldens handbook unification
plan: the 26 PNGs at `handbook.realunit.app/screenshots/` now come from
the same Golden baselines the visual-regression CI verifies on every PR.

## Why

- **Single source of truth**: one pixel-checked baseline per handbook
page. A UI regression that breaks a Golden also breaks the handbook
image before either ships.
- **Determinism**: dfx01's headless Skia/Open Sans rendering is
byte-stable across CI runs. Maestro's iOS-simulator screenshots drifted
on Apple Silicon + iOS 26 driver hangs (mobile-dev-inc/Maestro#3137).
- **Cycle time**: the handbook image rebuilds when Goldens change, in
seconds — no 30-minute Maestro suite to refresh a page.

## What changed

- **`scripts/assemble-handbook-screenshots.sh`** — explicit
`handbook-name → golden-path` mapping for all 26 pages (see PR #568 for
the audit that built this table). Copies and renames Goldens into a flat
output directory the Dockerfile then consumes. Fails loudly if a Golden
goes missing.
- **`Dockerfile.handbook`** — multi-stage build. Stage 1 (alpine + bash)
runs the assembly script. Stage 2 (nginx) copies the assembled
screenshots over the legacy `docs/handbook/screenshots/` tree,
preserving output paths so the handbook HTML `<img>` links work
unchanged.
- **`.github/workflows/handbook-build-check.yaml`** — new PR-only
validation. Runs the assembly script independently (cheap check before
docker spins up), builds the image, starts the container, hits
`/healthz`, verifies the auth gate returns 401 on `/de/`, and probes
`/screenshots/{01,11,26}.png` to prove the assembled files land on disk.
Does **not** push to Docker Hub and does **not** deploy. The
develop-push `handbook-deploy.yaml` retains sole ownership of DEV → PRD
rollout.

## Out of scope

- The legacy `docs/handbook/screenshots/` tree is left in place; the
COPY in Dockerfile.handbook overlays it with the assembled output, so
the legacy PNGs are harmless. **Phase 2** (Maestro pipeline retirement)
decides whether to delete the directory + flow YAMLs.
- DEV deploy verification — happens after merge to develop (the deploy
pipeline does that automatically).

## Test plan

- [ ] `Handbook Build Check` CI green on the PR (docker build +
container smoke)
- [ ] `Visual Regression` job green on the PR (parallel — no Golden
changes here, should pass trivially)
- [ ] After merge to develop: `handbook-deploy.yaml` builds the image,
DEV-deploy succeeds, `https://dev-handbook.realunit.app/de/` shows the
Goldens-sourced screenshots (spot-check 3 pages: welcome, dashboard,
terms)
- [ ] PRD deploy follows DEV-green, same spot-check on prod URL

## Pre-conditions for merge

The stack below this PR must merge in order: #541#562#568 → this.
Each subsequent merge re-targets the PR base automatically.
… (#568)

## Summary

Stacked on #562. Closes the 6 remaining handbook→Golden mapping gaps:

| # | Handbook page | Golden added | Notes |
|---|---|---|---|
| 03 | software-wallet-terms | `welcome_page_second_step` |
`@visibleForTesting initialShowSecondStep` ctor param |
| 09 | pin-confirm | `setup_pin_page_confirming` |
`SetupPinMode.confirm` state |
| 10 | biometric-prompt | `biometric_prompt_sheet_default` | Flutter
`EnableBiometricBottomSheet` — **NOT** an iOS system sheet (Maestro YAML
comment misleading) |
| 11 | dashboard | `home_page_loaded` | `DashboardView` with mocked
price chart |
| 17 | settings-backup-pin | `verify_pin_page_seed_backup` | i18n-driven
subtitle, no lib refactor |
| 26 | terms | `legal_document_page_terms_loaded` | `@visibleForTesting
initialMarkdownContent` ctor param |

After this PR, the handbook can switch to Golden-sourced screenshots
(Phase 1) without leaving any page unmapped.

## Lib refactors

Two minimal refactors, both `@visibleForTesting`, both with the
production default unchanged:

- `lib/screens/welcome/welcome_page.dart` — adds `initialShowSecondStep`
(default `false`)
- `lib/screens/legal/subpages/legal_document_page.dart` — adds
`initialMarkdownContent` (null → unchanged `_loadMarkdown()` path)

Driving the second-step / loaded-state via tap + pumpAndSettle was tried
first but is fragile in alchemist's hook chain.

## Bootstrap workflow

`.github/workflows/golden-bootstrap.yaml` is re-introduced temporarily,
push-triggered on this branch. Generates baselines on dfx01, removed
once committed.

## Test plan

- [ ] Bootstrap workflow completes green on dfx01
- [ ] Baselines downloaded + committed
- [ ] Bootstrap workflow removed
- [ ] `Visual Regression` job green on final commit
- [ ] Spot-check each of the 6 new baselines visually matches its
handbook counterpart
# Conflicts:
#	test/goldens/screens/settings_tax_report/goldens/macos/settings_tax_report_page_default.png
#	test/goldens/screens/transaction_history/goldens/macos/transaction_history_page_default.png
@TaprootFreak TaprootFreak merged commit 453ace2 into feat/visual-regression-pilot May 25, 2026
5 of 6 checks passed
@TaprootFreak TaprootFreak deleted the feat/visual-regression-handbook-gaps branch May 25, 2026 09:09
TaprootFreak added a commit that referenced this pull request May 25, 2026
…in de

Both PNGs were inherited from pilot's pre-#562 en-locale snapshots during
the #562 conflict merge — they were the only goldens not re-rendered in the
locale switch, so Visual Regression flagged them (1.94% / 0.40% diff) once
the screens started rendering in de.

Regenerated on dfx01 against the current de-locale + deterministic-clock
setup so both pipelines (de-locale switch + determinism fix) line up.
TaprootFreak added a commit that referenced this pull request May 26, 2026
Collection PR for follow-ups identified during the #541 review session.
Held as a **Draft** while commits accumulate; flipped to ready + merged
at the end.

### Likely contents (subject to scope decisions)
- `lib/styles/text_styles.dart` — hardcode `fontFamily` directly in
`_Body.base/.sm/.xs` + `_Header.h1/.h2/.h4` (root-cause for the latent
`appBarTheme.titleTextStyle` Ahem-rendering bug; supersedes the
per-theme `copyWith(fontFamily: …)` pin added in #562)
- `test/integration/wallet_creation_bitbox_test.dart:194-195` — drop the
tautological `appStore.wallet = created; verify(...).called(1);` or
replace with a real contract pin
- `test/integration/connect_bitbox_flow_test.dart:249,285` — align
wall-clock `Future.delayed(fastObserverInterval * 4)` with the
`fakeAsync`+`async.elapse` pattern used in
`bitbox_reconnect_recovery_test.dart`
- `test/goldens/screens/legal/legal_document_golden_test.dart` — replace
inline `_termsMarkdownStub` (1:1 copy of production terms) with a
synthetic markdown fixture under `test/fixtures/`
- Investigate why the dfx01 runner produces ±10–50 byte encoder drift on
unrelated golden PNGs during baseline refresh (Skia version pin, build
cache, font cache)

### Out of scope
- Anything that should go to a dedicated PR (e.g. a real new feature)
- #325 release develop → main

### Notes
Empty seed commit kept in history so the PR existed before the first
real change landed.

---------

Co-authored-by: Blume1977 <jana.ruettimann@dfx.swiss>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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