A coding agent could develop this repo's Go packages and could not develop the application: every path to running YellowJacket ended in a blocking GTK window, so 265 bound methods, 46 events, 33 component directories and 13 stores had exactly one form of verification available — `tsc --noEmit`. The unlock is that `wails dev`'s dev server on :34115 serves the real frontend with the real generated bindings against the same Go backend a desktop window attaches to, so a plain Chromium under Xvfb gets a fully functional app. Four test tiers now exist, cheapest first: - `make ui-test` — 313 Vitest tests in a real browser in ~2 s, no app, no backend, no display. Works because `frontend/wailsjs/` is a pure passthrough to `window.go`/`window.runtime`, so faking just those two globals runs the real bindings and the real store code. - `make test` — services in-process, asserting on the payload the frontend would receive, via a new `events.Emit` wrapper. - `make dev-headless` + `playwright-cli` — the real app, driven interactively, with an event bridge on `window.__yjEvents` and a dev-only control surface at `/__test/`. - `make e2e` — 19 of those flows frozen as Playwright specs. `events.Emit(ctx, …)` replaces all 35 direct `runtime.EventsEmit` call sites: wails' `getEvents` `log.Fatalf`s on any context without its runtime, so those paths could not run under test and a background worker could take the app down. Four packages had each hand-rolled the same guard; nine more guarded on `ctx != nil`, which does not help. `TestNoDirectRuntimeEmits` fails the build on a new one. Fixtures are generated, not committed (`make testdata`), and seeds are built by *running the app* — never by hand-writing config and DB rows, which would be a second description of a valid YJ_HOME. `.gitea/workflows/ci.yml` is the first workflow here that tests anything; the other three only package, so `gitea_ci` reported only packaging jobs and misled anyone asking whether a push was healthy. Both jobs were prototyped to green in a bare ubuntu:24.04 container before the YAML was written, which immediately caught `make lint` linting three configurations that nothing builds: all three passes omitted `webkit2_41`, so wails resolved webkit2gtk-4.0 — which Arch still ships and Ubuntu 24.04 dropped. Operational instructions live in `.pi/skills/yellowjacket-dev/`, measured discoveries in `.planning/NOTES.md`, and architecture in `CLAUDE.md` — split by tense, not by topic, because a topical split gives every new fact two plausible homes. `make skill-check` fails a commit if the skill cites a make target that does not exist.
75 lines
3.1 KiB
Markdown
75 lines
3.1 KiB
Markdown
# The component and store tier (`make ui-test`)
|
|
|
|
313 tests in a real Chromium in ~2 s with no Wails, no backend, no
|
|
seeded library and no virtual display. This is the cheapest coverage
|
|
available and where the bulk of UI regression belongs.
|
|
|
|
```bash
|
|
make ui-setup # once: the Vitest provider's own Chromium
|
|
make ui-test # behaviour only
|
|
make ui-watch
|
|
make ui-visual # + toMatchScreenshot baselines (YJ_VISUAL=1)
|
|
make ui-visual-update # re-record them
|
|
make ui-test UI_ARGS='store/queue' # filter
|
|
```
|
|
|
|
## How it works
|
|
|
|
`frontend/wailsjs/` is a pure passthrough — every binding is
|
|
`window.go[svc][Type][Method](args)`, every runtime call is
|
|
`window.runtime.X(...)`. So `frontend/test/support/wails-fake.ts`
|
|
replaces **those two globals and nothing else**, and the tests then
|
|
exercise the *real* generated bindings and the *real* store code. No
|
|
module mocking, and no second description of the Wails layer.
|
|
|
|
```ts
|
|
emit(Events.QueueChanged, payload); // push a backend event
|
|
stub('queue.Queue.GetState', state); // a value, or a function of the args
|
|
stubFailure('queue.Queue.SetQueue'); // reject, as a Go error does
|
|
calls('queue.Queue.SetQueue'); // what the frontend called back with
|
|
lastArgs('queue.Queue.SetQueue');
|
|
const el = await fixture('now-playing'); // mount; shadow()/text() query it
|
|
```
|
|
|
|
The dispatcher mirrors wails' own `desktop/events.js`, including
|
|
`maxCallbacks` expiry and the fact that a frontend `EventsEmit`
|
|
notifies local listeners *before* Go.
|
|
|
|
## Four things that will cost you time
|
|
|
|
- **Store singletons are constructed at module import**, before any test
|
|
can stub. `test/setup.ts` therefore carries import-time defaults for
|
|
the stores that read config in their constructor. Without one, a store
|
|
caches `undefined` where Go would have sent `[]`, and components crash
|
|
on `.length` — which reads exactly like a component bug and is not.
|
|
Adding a store that reads config on construction means adding its
|
|
default there.
|
|
- **`vitest.config.mts`, not `.ts`** — it `mergeConfig`s the repo's
|
|
`vite.config.mts` to reuse the `@go`/`@store`/`@components` aliases,
|
|
and a `.ts` sibling cannot import it.
|
|
- **Screenshots need the theme.** The setup file imports
|
|
`@store/theme-store` for its side effect (it applies the `--yj-*`
|
|
ramp to `:root`); without it a component renders white-on-white and
|
|
the baseline is blank.
|
|
- **`@lit-labs/virtualizer` never produces two identical frames**, so
|
|
`toMatchScreenshot` on `<queue-panel>` fails with "could not capture a
|
|
stable screenshot" rather than a diff. Assert on its rows instead.
|
|
|
|
Visual baselines are font-hinting and compositing sensitive, which is
|
|
why they are opt-in: they only mean anything on the machine that
|
|
recorded them.
|
|
|
|
## Bindings
|
|
|
|
`frontend/wailsjs/` is generated by `wails`, **not** by `go generate`,
|
|
so the pre-commit codegen check does not cover it — a renamed Go bound
|
|
method first shows up at runtime, as a call that never settles.
|
|
|
|
```bash
|
|
make bindings-check # ~1.5 s, also a pre-commit hook
|
|
make bindings # regenerate for real
|
|
```
|
|
|
|
The generator rewrites `wailsjs/runtime/*` as mode 755 every run; that
|
|
is churn, not drift, and the check ignores it.
|