Adds oxfmt (Rust formatter, Prettier-conformant) — the repo had no formatter, so this is a one-time normalization of the JS/TS/JSX code (257 files; purely cosmetic — full suite stays 638/638). Scope is deliberately narrowed in .oxfmtrc.json to JS/TS/JSX only: - singleQuote:true + jsxSingleQuote:false — preserve the project's existing style (single-quoted JS, double-quoted JSX attrs), not oxfmt's double-quote default. (Flipping quotes globally also broke a source-string-parsing test; preserving them keeps featureCoverage green.) - Excludes **/*.css (the CSS→Tailwind migration will rewrite those — formatting them now is wasted churn), **/*.json (avoids reformatting 20 i18n locale files + config), **/*.toml, and src-tauri/** (Rust/Tauri config — out of scope for a frontend JS formatter; oxfmt was reformatting Cargo.toml/tauri.conf.json). Tooling: - `bun run format` (write) / `bun run format:check` (verify). - ci.yml: new "Frontend format check (oxfmt)" gate after the oxlint gate. Verified: format:check clean; oxlint 0 errors; vite build; full suite 638/638; bun install --frozen-lockfile in sync. Co-authored-by: mergetest <test@local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
1.8 KiB
TypeScript
60 lines
1.8 KiB
TypeScript
import type { Page } from '@playwright/test';
|
|
|
|
/** Every routable view (the `mode` values in App.jsx). */
|
|
export const MODES = [
|
|
'launchpad',
|
|
'clone',
|
|
'design',
|
|
'gallery',
|
|
'dub',
|
|
'stories',
|
|
'projects',
|
|
'queue',
|
|
'tools',
|
|
'transcriptions',
|
|
'settings',
|
|
'donate',
|
|
] as const;
|
|
|
|
/**
|
|
* Fatal client errors that mean a view failed to LOAD — code-split chunk /
|
|
* dynamic-import failures (the "Use design → Importing a module script failed"
|
|
* regression) and uncaught exceptions. Deliberately NOT matching network/API
|
|
* noise (5xx, fetch failures) — backend health is covered elsewhere, and a
|
|
* flaky API shouldn't fail a UI-mount test.
|
|
*/
|
|
const FATAL = [
|
|
/Importing a module script failed/i,
|
|
/Failed to fetch dynamically imported module/i,
|
|
/error loading dynamically imported module/i,
|
|
/ChunkLoadError/i,
|
|
];
|
|
|
|
export type ErrorSink = { fatal: string[]; all: string[] };
|
|
|
|
/** Attach console/pageerror listeners; returns a sink you assert on later. */
|
|
export function collectErrors(page: Page): ErrorSink {
|
|
const sink: ErrorSink = { fatal: [], all: [] };
|
|
const record = (text: string) => {
|
|
sink.all.push(text);
|
|
if (FATAL.some((re) => re.test(text))) sink.fatal.push(text);
|
|
};
|
|
page.on('pageerror', (err) => record(`pageerror: ${err.message}`));
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error') record(`console.error: ${msg.text()}`);
|
|
});
|
|
return sink;
|
|
}
|
|
|
|
/**
|
|
* Land directly on a view by seeding the zustand-persist store (key
|
|
* `omnivoice.app`) before the app boots. A shallow merge over slice defaults,
|
|
* so only `mode` is forced.
|
|
*/
|
|
export async function gotoMode(page: Page, mode: string): Promise<void> {
|
|
await page.addInitScript((m) => {
|
|
localStorage.setItem('omnivoice.app', JSON.stringify({ state: { mode: m }, version: 4 }));
|
|
}, mode);
|
|
await page.goto('/');
|
|
}
|