UI testing: Vitest browser mode, Playwright E2E, fake TTS backend - #1015
Draft
jamiepine wants to merge 10 commits into
Draft
UI testing: Vitest browser mode, Playwright E2E, fake TTS backend#1015jamiepine wants to merge 10 commits into
jamiepine wants to merge 10 commits into
Conversation
The wav/txt/md export handlers called plugin-dialog and plugin-fs directly, which breaks in the web build. saveFile now returns the saved path (null on cancel) so callers can toast accurately, and the cross-window capture listeners only register under Tauri.
Both useAutoUpdater.ts and .tsx existed; Vite resolves .ts first, which accepts showToast but never implements it — so the update toast App.tsx asks for never rendered. The .tsx variant is the full implementation and has the same signature.
Each entry built its own QueryClient while serverStore invalidates the singleton from lib/queryClient — so switching server URL never refreshed data. Both entries now render with the singleton. i18n init was only reachable transitively through lib/utils/format.ts; import it directly in both entries.
Removes the orphaned generated OpenAPI client (superseded by the hand-written client.ts), the unrouted AudioTab, and app/'s unused standalone entry (main.tsx, index.html, vite.config) — app/ is a shared library consumed by web/ and tauri/. routeTree is exported so tests can build routers over memory history.
Two projects in one root config: 'unit' (happy-dom) for stores, hooks, and utils, and 'browser' (real Chromium via the playwright provider) for component tests — no jsdom polyfills for EventSource, AudioContext, or canvas. Harness pieces: createMockPlatform (spy-able Platform with an updater emit handle), renderWithProviders (fresh QueryClient with polling disabled + PlatformProvider), MSW worker with baseline health handler, and a store-reset teardown covering all eight zustand stores. Seed tests cover uiStore theme, serverStore invalidation, and an AboutPage render in Chromium. Requires node >= 20 (.node-version added); run with bun run test.
…te render Fixture builders match the hand-written API types with deterministic ids. Handlers are per-domain factories tests compose via worker.use; unstubbed requests fail loudly. sseController streams real text/event-stream bodies through MSW into EventSource, so generation and download progress are testable without touching the transport. renderRoute mounts the real route tree over memory history. Query clients drain on teardown so in-flight fetches can't leak past handler reset, and the browser project pre-bundles app deps so a cold dep- optimizer cache can't reload mid-run.
e2e/ drives the web build (vite preview) against a per-worker uvicorn: each Playwright worker boots its own backend on its own port with a temp cwd, so SQLite and audio files are fully isolated and parallel- safe. The page fixture seeds the persisted voicebox-server store with the worker's URL before any script runs. VOICEBOX_FAKE_TTS=1 short-circuits get_tts_backend_for_engine to a backend that synthesizes a sine tone sized to the text — the real task queue, SSE progress, history rows, and audio serving all run, only inference is fake. backend/requirements-ci.txt is the slim dependency set validated to boot the app on a CPU-only runner. Specs: startup, settings layout, seeded profile in /voices, and generate-to-completed-audio through the full pipeline.
quality keeps typecheck + web build and uploads the bundle. unit runs vitest (node + Chromium browser projects) and blocks PRs. e2e boots the CPU backend with fake TTS and runs Playwright against the preview build — informational until it has a sustained green run, then flip continue-on-error off. backend-tests runs the existing pytest suite for the first time in CI, also informational.
App: non-Tauri skips the startup gate, Tauri dev reaches the router with close-handler/updater wiring, ?view=dictate renders the pill without booting the main app (branches behind import.meta.env.PROD are unreachable under vitest and stay uncovered). ChordPicker: chord capture via raw KeyboardEvents with explicit codes, peak-set semantics, save/cancel. HistoryTable: rows, empty state, player-store autoplay intent, favorite/delete round trips, export through platform.filesystem.saveFile. FloatingGenerateBox: mounted via the real index route so submit → pending → SSE completion → history refetch runs the actual RootLayout wiring.
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
UI testing for the app: Vitest browser mode for components, Playwright E2E against a real backend, CI jobs to run it all. 29 vitest tests + 4 E2E specs, all green.
Bug fixes shaken out by making things testable
CapturesTabexports (wav/txt/md) calledplugin-dialog/plugin-fsdirectly — broken in the web build. Now routed throughplatform.filesystem.saveFile, which returns the saved path (nullon cancel) so the toast logic stays honest:useAutoUpdater.ts/.tsx: Vite resolves the.ts, which acceptsshowToastbut never implements it — the update-available toast never rendered. The.tsis gone, so this PR enables that toast for the first time.QueryClientwhileserverStoreinvalidates the singleton fromlib/queryClient— switching server URL never refreshed data. Both entries now render with the singleton.lib/utils/format.tshappens to import it; both entries now import@/i18nexplicitly.AudioTab(675 lines),app/'s standalone entry.app/is now purely a shared library.Component tests (Vitest 4, browser mode)
Two projects in one root config:
unit(happy-dom) for stores/utils,browser(real Chromium via the Playwright provider) for components. Real EventSource/AudioContext/canvas — no jsdom polyfills. Naming:*.test.tsruns in node,*.browser.test.tsxruns in Chromium.Harness under
app/src/test/:renderWithProviders/renderRoute(real route tree over memory history),createMockPlatform(every platform method a spy), typed MSW fixtures + per-domain handler factories, and an SSE controller that streams realtext/event-streambodies through MSW:Suites: App startup state machine, ChordPicker, HistoryTable row actions, FloatingGenerateBox mounted through the real root layout (submit → pending → SSE completion → history refetch is the actual wiring), uiStore/serverStore.
E2E (Playwright + real backend + fake TTS)
e2e/drives the builtweb/bundle against a per-worker uvicorn — each worker gets its own port and temp data dir, so SQLite is parallel-safe.VOICEBOX_FAKE_TTS=1short-circuitsget_tts_backend_for_engineto a backend that synthesizes a sine tone sized to the text: the real task queue, SSE progress, history rows, and audio serving all run, only inference is fake. The generate spec goes from typing text to a completed row serving a playable WAV.backend/requirements-ci.txtis the slim CPU-only set validated to boot the app on a runner.CI
quality(typecheck + web build) andunit(vitest) block PRs.e2eandbackend-tests(the 30 existing pytest files, first time in CI) run withcontinue-on-erroruntil they prove stable, then flip to blocking.Running locally
Notes: biome is not in CI — pre-existing format drift repo-wide would fail it immediately; needs a one-time
check:fixcommit first. Tauri-only surfaces (updater install, GPU switching, global shortcut dictation) stay manual — WKWebView has no WebDriver, so true in-app E2E on macOS isn't possible.