Files
yellowjacket/frontend/vitest.config.mts
T
yonluandClaude Opus 5 a4ada725a2 feat(wails): rebuild the Vitest fake on v3's transport seam
v2 installed two globals and the fake replaced both. v3 has neither —
the runtime is an npm module and the generated bindings call into it.
What it has instead is better: setTransport() is a public seam for
replacing the IPC transport, and *every* runtime call goes through it,
so the fake is smaller than v2's and covers strictly more.

The event dispatcher is no longer mirrored at all. v2's fake
reimplemented desktop/events.js — the listener list, maxCallbacks
expiry, the reverse iteration — because there was no way to reach the
real one; emit() now goes through window._wails.dispatchWailsEvent,
which is the entry point the backend's own push uses. What is mirrored
instead is one line of Go: how EventManager.Emit packs variadic data
into an event's single data field. Registration and unregistration are
the public Events API. The one non-public thing left is the listener
registry, aliased in vitest.config.mts and used only by
listenerNames() — a test asks whether importing a store subscribed it,
which nothing public can answer.

A binding carries a method ID, not a name, so the fake derives the
ID -> path map from the generated tree: FNV-1a over the FQN, with the
Go type's casing recovered from each package's index.ts, which is the
only place it survives (library/library.ts cannot tell you it is
FrontendUtil). The map has to be complete rather than lazy because 21
assertions read calls() with no argument and compare the whole list.

Two things had to move that are not the fake.

fixture() drains microtasks between two renders: a v3 binding settles
several hops later than v2's, and tests were already written as though
fixture() meant "mounted and loaded". Microtasks and not a timer,
which would hang under the suites that install fake ones.

tracklist-store keeps its defaults on an empty answer instead of
emptying the column list. GetTrackListColumns substitutes
DefaultColumns only when the whole config section is missing; a section
that exists with no columns returns nothing. Until now this was
accidental — the binding was typed Column[], an absent answer arrived
as undefined, and .map threw into the catch.

757 tests pass across all 63 files. They are run in batches: a single
browser session dies partway through the 58 it queues, which reproduces
unchanged at the pre-migration commit and is a resource limit on this
machine rather than anything here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDCbcCZQepnpSQYJ6SxxZm
2026-08-14 19:46:14 -04:00

77 lines
2.9 KiB
TypeScript

import path from 'path';
import { playwright } from '@vitest/browser-playwright';
import { defineConfig, mergeConfig } from 'vitest/config';
import viteConfig from './vite.config.mts';
// Visual regression is opt-in. `toMatchScreenshot` baselines are
// sensitive to font hinting and GPU compositing, so a baseline taken on
// one machine fails on another for reasons that have nothing to do with
// the component. Behavioural assertions run everywhere; screenshots run
// where the baselines were taken (`make ui-visual`, or CI's container).
const screenshotsEnabled = process.env['YJ_VISUAL'] === '1';
export default mergeConfig(
viteConfig,
defineConfig({
test: {
// The Playwright *runner* lives in e2e/, a separate package, so its
// specs can never be picked up here and the two Playwright
// versions cannot collide.
include: ['test/**/*.test.ts'],
setupFiles: ['./test/setup.ts'],
globals: false,
browser: {
enabled: true,
provider: playwright(),
headless: true,
// WebKit's Linux build wants Ubuntu libraries Arch does not
// have, so it is a CI-only browser. See .planning/NOTES.md.
instances: [{ browser: 'chromium' }],
viewport: { width: 1280, height: 800 },
screenshotFailures: false,
expect: {
toMatchScreenshot: {
comparatorName: 'pixelmatch',
comparatorOptions: { allowedMismatchedPixelRatio: 0.02 },
},
},
},
expect: { requireAssertions: true },
env: { YJ_VISUAL: screenshotsEnabled ? '1' : '' },
},
resolve: {
alias: {
'@test': path.resolve(__dirname, 'test'),
// The runtime's listener registry, which its package exports
// map does not expose. wails-fake.ts needs it to answer "did
// importing a store subscribe it"; see the note there.
'@wailsio/listener': path.resolve(
__dirname,
'node_modules/@wailsio/runtime/dist/listener.js',
),
},
},
// Pre-bundle what the components pull in, or Vite discovers it
// mid-run and reloads the page underneath a running test.
optimizeDeps: {
include: [
'lit',
'lit/decorators.js',
'lit/directives/repeat.js',
'@lit-labs/virtualizer',
// Web Awesome components are deep imports, one per element;
// the glob pre-bundles them all rather than discovering each
// one the first time a component under test imports it.
'@awesome.me/webawesome/dist/components/*/*.js',
'@awesome.me/webawesome/dist/webawesome.js',
// The runtime every generated binding imports. Left out, Vite
// discovers it from the first binding module a suite touches
// and reloads the page — which surfaces as several suites
// failing to import at all while each passes on its own.
'@wailsio/runtime',
],
},
}),
);