test: add comprehensive frontend tests for components, stores, and utils

Full test coverage for the frontend implementation using Vitest and
Testing Library. Tests are organized by concern with shared fixtures.

Component tests:
- AppShell.test.tsx: Navigation tabs, view switching, batch mode overlay
- FocusCard.test.tsx: Rendering, action buttons, keyboard shortcuts, empty state
- QueueView.test.tsx: Item display, focus promotion, empty state
- QueueItem.test.tsx: Type badges, click handling
- QueueSummary.test.tsx: Count display by type
- QuickCapture.test.tsx: Modal behavior, form submission, error states
- BatchMode.test.tsx: Progress tracking, item advancement, completion
- App.test.tsx: Updated for AppShell integration

Store tests:
- focus-store.test.ts: Item management, act(), setFocus(), reorderQueue()
- nav-store.test.ts: View switching
- capture-store.test.ts: Open/close, submission states
- batch-store.test.ts: Batch lifecycle, status tracking, derived counts

Library tests:
- types.test.ts: Type guards, staleness computation
- transform.test.ts: Lore data transformation, priority ordering
- format.test.ts: IID formatting for MRs vs issues

E2E tests (app.spec.ts):
- Navigation flow
- Focus card interactions
- Queue management
- Quick capture flow

Test infrastructure:
- fixtures.ts: makeFocusItem() factory
- tauri-plugin-shell.ts: Mock for @tauri-apps/plugin-shell
- Updated tauri-api.ts mock with new commands
- vitest.config.ts: Path aliases, jsdom environment
- playwright.config.ts: Removed webServer (run separately)
- package.json: Added @tauri-apps/plugin-shell dependency

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
teernisse
2026-02-26 09:54:46 -05:00
parent df53096aa8
commit 480d0817d9
23 changed files with 1952 additions and 37 deletions

View File

@@ -24,7 +24,21 @@ export const invoke = vi.fn(async (cmd: string, _args?: unknown) => {
last_sync: null,
is_healthy: true,
message: "Mock lore status",
summary: null,
};
case "get_bridge_status":
return {
mapping_count: 0,
pending_count: 0,
suspect_count: 0,
last_sync: null,
last_reconciliation: null,
};
case "sync_now":
case "reconcile":
return { created: 0, closed: 0, skipped: 0, healed: 0, errors: [] };
case "quick_capture":
return { bead_id: "bd-mock-capture" };
default:
throw new Error(`Mock not implemented for command: ${cmd}`);
}
@@ -39,16 +53,40 @@ export function setMockResponse(cmd: string, response: unknown): void {
export function resetMocks(): void {
invoke.mockClear();
Object.keys(mockResponses).forEach((key) => delete mockResponses[key]);
eventHandlers.clear();
listen.mockClear();
}
// Event listener registry -- tests can trigger events via simulateEvent()
type EventHandler = (payload: { payload: unknown }) => void;
const eventHandlers: Map<string, Set<EventHandler>> = new Map();
// Mock event listener
export const listen = vi.fn(
async (_event: string, _handler: (payload: unknown) => void) => {
async (event: string, handler: EventHandler) => {
if (!eventHandlers.has(event)) {
eventHandlers.set(event, new Set());
}
eventHandlers.get(event)!.add(handler);
// Return unlisten function
return vi.fn();
const unlisten = vi.fn(() => {
eventHandlers.get(event)?.delete(handler);
});
return unlisten;
}
);
/** Simulate a Tauri event being emitted (for test use). */
export function simulateEvent(event: string, payload: unknown): void {
const handlers = eventHandlers.get(event);
if (handlers) {
for (const handler of handlers) {
handler({ payload });
}
}
}
// Mock event emitter
export const emit = vi.fn(async (_event: string, _payload?: unknown) => {});