// Shared smoke-test helpers: a fetch mock keyed by "METHOD /path" and an app // mount at a route with a signed-in admin. Fixtures are neutral acme.demo.* // data; no application names appear in framework tests. import { vi } from 'vitest' import { flushPromises, mount } from '@vue/test-utils' import { createMemoryHistory } from 'vue-router' import App from '../src/App.vue' import { createAdminRouter } from '../src/app/router' import { me } from '../src/state/useAuth' import { loadNavigation } from '../src/state/useNavigation' import navigation from './fixtures/navigation.json' export const API = '/admin-test/api/v1' export const profile = { data: { id: 7, login: 'dev', email: 'dev@example.test', first_name: 'Dana', last_name: 'Dev', is_superuser: false, role: { id: 2, code: 'developer', name: 'Developer' }, }, meta: {}, } export interface Reply { status?: number body: unknown } export type Route = Reply | ((request: Request) => Reply | Promise) export function json(status: number, body: unknown): Response { return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' } }) } export const notFound = { error: { code: 'not_found', message: 'Not found', details: {} } } /** Replaces fetch; routes are keyed by "METHOD pathname" (query ignored). */ export function mockApi(routes: Record): Request[] { const calls: Request[] = [] vi.spyOn(globalThis, 'fetch').mockImplementation(async (input: RequestInfo | URL) => { const request = input as Request calls.push(request) const route = routes[`${request.method} ${new URL(request.url).pathname}`] if (!route) { return json(404, notFound) } const reply = typeof route === 'function' ? await route(request.clone()) : route return json(reply.status ?? 200, reply.body) }) return calls } export function pathOf(request: Request): string { return new URL(request.url).pathname } export function queryOf(request: Request): URLSearchParams { return new URL(request.url).searchParams } /** Requests matching a method and pathname, in call order. */ export function requestsTo(calls: Request[], method: string, pathname: string): Request[] { return calls.filter((request) => request.method === method && pathOf(request) === pathname) } /** Mounts the whole app at a path with the profile and navigation loaded. */ export async function mountApp(path: string, routes: Record, options: { attach?: boolean } = {}) { const calls = mockApi({ [`GET ${API}/auth/me`]: { body: profile }, [`GET ${API}/navigation`]: { body: navigation }, ...routes, }) await me() await loadNavigation() const router = createAdminRouter(createMemoryHistory()) await router.push(path) const wrapper = mount(App, { global: { plugins: [router] }, attachTo: options.attach ? document.body : undefined, }) await flushPromises() return { wrapper, calls, router } }