Skip to content

feat(ui): add BrowserPanel chrome visual-smoke fixture + source contract (#819) - #838

Merged
Astro-Han merged 1 commit into
mainfrom
feat/819-browser-chrome-fixture
Jul 12, 2026
Merged

Astro-Han merged 1 commit into
mainfrom
feat/819-browser-chrome-fixture

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

  • Add a browser-empty visual-smoke fixture scenario that mounts BrowserPanel (via a new VisualSmokeState.liveBrowserSessionIds field wired into applyVisualSmokeFixture) so the renderer chrome can be screenshot-captured in the empty state — the fix(ui): give BrowserPanel a narrow bottom-sheet layout (#546) #818 narrow-layout defect surface.
  • Add a browser-panel-chrome source contract (symmetric to artifact-pane-layout.test.ts) locking the 5 declarative chrome invariants that don't need a screenshot: back/forward :disabled track canGoBack/canGoForward; reload/stop icon + aria-label + onClick swap with loading; address bar snaps to live URL on blur when not editing; empty state present when !hasPage.

Why

BrowserPanel (apps/desktop/src/renderer/browser-panel.tsx) is the renderer chrome over a native Electron WebContentsView; the native page can't be captured in a deterministic fixture, but the chrome is renderer DOM. The chrome had no deterministic visual or DOM/computed-style coverage, so layout/a11y regressions (e.g. the narrow-layout defect fixed in #818) could only be caught by manual real-window smoke.

Closes #819.

Scope

  • Loaded / loading / nav chrome states are intentionally not screenshot-fixture'd. Their layout is identical to empty (fix(ui): give BrowserPanel a narrow bottom-sheet layout (#546) #818 is a layout defect, not a state defect), loading/nav are transient and can't be deterministically captured, and their state invariants are locked by the source contract. Seeding BrowserState for a loaded screenshot would need a production test-seam (a main-side visual-smoke browser:state emit hook, or a visual-smoke read inside BrowserPanel) for no layout-regression value — rejected on first-principles/Occam's-razor grounds.
  • Source contract over render test. The repo has no jsdom/testing-library, and BrowserPanel is IPC-driven via window.maka.browser. The 5 invariants are declarative (disabled={expr}, cond ? <X/> : <Y/>, onBlur={...}, !hasPage && <Empty>) — the wiring IS the rendered DOM, so a source regex is a reliable proxy without a render harness + heavy mocking. This matches the cited symmetry (artifact-pane-layout.test.ts).
  • liveBrowserSessionIds is a natural seam. It's the exact state the renderer already uses to gate BrowserPanel mounting (activeId && liveBrowserSessionIds.includes(activeId)); adding it to VisualSmokeState extends the existing fixture contract, no production behavior change. Real users never receive a visual smoke state.

Verification

  • typecheck ✓ (main + renderer + storybook)
  • test ✓ — 2363/2363 (was 2354; +2 browser-empty fixture tests, +5 browser-panel-chrome contract tests)
  • browser-panel-chrome ✓ 5/5 (back/forward disabled, reload/stop swap, address blur-snap, empty-state)
  • visual-smoke-fixture ✓ — browser-empty scenario resolves, activeSessionId + liveBrowserSessionIds seeded correctly, turn session on disk (reused, no browser-specific seed)
  • 8 baseline PNGs generated at apps/desktop/tests/screenshots/browser-empty/ (gitignored generated captures): 1280/990 × light/dark × motion/reduced. Dimensions verified via sips (1280→2560×1640, 990→1980×1640 Retina; sizes 249KB light / 729KB-1MB dark, non-blank). motion == reduced-motion byte-for-byte (empty chrome has no animation).
  • Visual evidence: I can't read images, so I can't visually verify the PNGs. The chrome layout regression surface is locked by the existing browser-panel-layout CSS contract (fix(ui): give BrowserPanel a narrow bottom-sheet layout (#546) #818/fix(ui): reserve chat/composer height when both ArtifactPane and BrowserPanel are open at narrow widths #824, already merged) + the empty-state fixture; the 5 behavioral invariants are locked by the source contract. The generated PNGs are a stable-subset candidate pending visual review + promotion to screenshots-baseline/ via a separate baseline commit (the established pattern — baselines are committed separately from fixture code, e.g. e1d1344e, 12ad19aa; --update-baseline is designed for a full --all re-capture, not single-scenario promotion).

Impact

None on users — visual-smoke mode only; real users never receive a visual smoke state, so liveBrowserSessionIds never drives production browser:live wiring. Dev/test impact: a new fixture scenario + a new source contract test; the capture script's ALL_SCENARIOS includes browser-empty for baseline runs.

Reviewer notes

  • The browser-empty scenario reuses the always-seeded turn session (TURN_SESSION_ID), so it adds no on-disk seed branch — getVisualSmokeState returns liveBrowserSessionIds: [TURN_SESSION_ID] + activeSessionId: TURN_SESSION_ID; seedVisualSmokeFixture already writes the turn session unconditionally.
  • Per-capture wall time was ~60s (right at the capture script's 60s subprocess timeout) — the app doesn't self-exit after the capture marker, so the script kills it at the timeout; all 8 variants still produced valid PNGs. This is pre-existing capture-script behavior, not introduced here.

Ready for review

  • Verification is complete and the results above are current
  • Impact, compatibility, migration, documentation, and release-note needs are addressed
  • User-visible UI/UX changes include visual evidence, or Verification explains why it is not applicable

…act (#819)

BrowserPanel renderer chrome had no deterministic coverage, so layout/
a11y regressions (e.g. the #818 narrow-layout defect) could only be
caught by manual real-window smoke. Add two deterministic layers:

- VisualSmokeState.liveBrowserSessionIds + applyVisualSmokeFixture
  wiring + a `browser-empty` scenario that seeds the active turn
  session as a live browser session so BrowserPanel mounts. With no
  native WebContentsView in visual-smoke mode, browser.getState
  resolves null → EMPTY_STATE → the empty-state chrome (toolbar
  all-nav-disabled + <Empty> strip), exactly the #818 defect surface.
  Reuses the always-seeded turn session; no browser-specific on-disk
  seed.

- browser-panel-chrome source contract (symmetric to
  artifact-pane-layout.test.ts) locking the 5 declarative chrome
  invariants that don't need a screenshot: back/forward :disabled
  track canGoBack/canGoForward; reload/stop icon + aria-label +
  onClick swap with loading; address bar snaps to live URL on blur
  when not editing; empty state present when !hasPage. The wiring IS
  the rendered DOM for these declarative bindings, so a source regex
  is a reliable proxy without a render harness (the repo has no
  jsdom/testing-library; BrowserPanel is IPC-driven).

Loaded/loading/nav chrome states are intentionally not screenshot-
fixture'd: their layout is identical to empty (#818 is a layout
defect, not a state defect), loading/nav are transient, and their
state invariants are locked by the source contract. Seeding
BrowserState for a loaded screenshot would need a production test-
seam (main-side visual-smoke browser:state hook or BrowserPanel
visual-smoke read) for no layout-regression value.

8 baseline PNGs (1280/990 × light/dark × motion/reduced) generated
in apps/desktop/tests/screenshots/browser-empty/ (gitignored
generated captures) as a stable-subset candidate, pending visual
review + promotion to screenshots-baseline/ via a separate baseline
commit (the established pattern — baselines are committed separately
from fixture code).
@Astro-Han
Astro-Han force-pushed the feat/819-browser-chrome-fixture branch from 84d7e78 to 1192a04 Compare July 12, 2026 16:25
@Astro-Han
Astro-Han merged commit ace834b into main Jul 12, 2026
2 of 3 checks passed
@Astro-Han
Astro-Han deleted the feat/819-browser-chrome-fixture branch July 14, 2026 05:05
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.

feat(ui): add deterministic visual-smoke fixture + DOM contract for BrowserPanel renderer chrome

1 participant