/** * Home page mount test — the discovery dashboard over native data * (`useDiscovery` + `useManifest` + `useTelemetry` + `useProject`). * * Covers: the discovered-projects grid renders from the catalog + live status; * a card shows its health + composition stats; the active-project quick-stats * strip derives counts from the manifest; the empty state shows when the * catalog is empty. */ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { mount, flushPromises } from "@vue/test-utils"; import { createRouter, createMemoryHistory } from "vue-router"; import { VueQueryPlugin } from "@tanstack/vue-query"; import Home from "../Home.vue"; const CWD = "/repo/orders"; const manifest = { version: 4, model: { nodes: [ { id: "action:orders.place", kind: "action", name: "orders.place" }, { id: "action:orders.cancel", kind: "action", name: "orders.cancel" }, { id: "event:orders.placed", kind: "event", name: "orders.placed" }, { id: "projection:orders.summary", kind: "projection", name: "orders.summary" }, { id: "app:orders", kind: "app", name: "orders" }, ], edges: [], }, }; const status = { [CWD]: { hasManifest: true, processes: [{ id: "p1", pid: 123, port: 4000, status: "running", startedAt: "x" }], }, }; class FakeEventSource { onopen: (() => void) | null = null; onerror: (() => void) | null = null; onmessage: ((m: { data: string }) => void) | null = null; constructor(public readonly url: string) { queueMicrotask(() => this.onerror?.()); } close(): void {} } function seedCatalog(empty = false): void { if (empty) { localStorage.clear(); return; } localStorage.setItem( "nwire.projects", JSON.stringify({ [CWD]: { cwd: CWD, name: "orders", lastVisited: "2026-01-01T00:00:00.000Z", composition: { apps: 1, plugins: 2, actions: 2, events: 1, resolvers: 0, workflows: 1 }, }, }), ); localStorage.setItem("nwire.activeProject", CWD); } beforeEach(() => { (globalThis as unknown as { EventSource: typeof FakeEventSource }).EventSource = FakeEventSource; globalThis.fetch = vi.fn((url: string | URL) => { const u = String(url); if (u.includes("/__nwire/projects/status")) { return Promise.resolve(new Response(JSON.stringify(status), { status: 200 })); } if (u.includes("/__nwire/manifest.json")) { return Promise.resolve(new Response(JSON.stringify(manifest), { status: 200 })); } if (u.includes("/_nwire/telemetry/recent")) { return Promise.resolve(new Response("[]", { status: 200 })); } return Promise.resolve(new Response("", { status: 404 })); }) as typeof fetch; }); afterEach(() => { localStorage.clear(); }); function makeRouter() { return createRouter({ history: createMemoryHistory(), routes: [ { path: "/", name: "home", component: Home }, { path: "/trace", name: "trace", component: { template: "
" } }, { path: "/streams", name: "streams", component: { template: "" } }, { path: "/projects", name: "projects", component: { template: "" } }, { path: "/projects/:slug/:page", name: "page", component: { template: "" } }, ], }); } async function mountHome() { const router = makeRouter(); const wrapper = mount(Home, { global: { plugins: [router, VueQueryPlugin] } }); await flushPromises(); await flushPromises(); return { wrapper, router }; } describe("Home", () => { it("renders the discovered-projects grid from the catalog + live status", async () => { seedCatalog(); const { wrapper } = await mountHome(); expect(wrapper.find("[data-testid=home-page]").exists()).toBe(true); const cards = wrapper.findAll("[data-testid=home-project-card]"); expect(cards).toHaveLength(1); expect(wrapper.find("[data-testid=home-project-name]").text()).toBe("orders"); // Running health (a live process is registered). expect(wrapper.find("[data-testid=home-project-health]").text()).toContain("Running"); // Composition stats from the snapshot are present (not pending). expect(wrapper.find("[data-testid=home-project-stats]").exists()).toBe(true); }); it("derives the active-project quick stats from the manifest", async () => { seedCatalog(); const { wrapper } = await mountHome(); const strip = wrapper.find("[data-testid=home-quickstats]"); expect(strip.exists()).toBe(true); const values = strip.findAll("[data-testid=kpi-value]").map((n) => n.text()); // Nodes=5, Handlers=0, Actions=2, Events=1, Projections=1, Errors=0 (telemetry empty). expect(values).toEqual(["5", "0", "2", "1", "1", "0"]); }); it("jumps into a project's map, switching the active project without reload", async () => { seedCatalog(); const { wrapper, router } = await mountHome(); await wrapper.find("[data-testid=home-jump-map]").trigger("click"); await flushPromises(); expect(router.currentRoute.value.fullPath).toBe("/projects/orders/map"); }); it("shows the empty state when no projects are discovered", async () => { seedCatalog(true); const { wrapper } = await mountHome(); expect(wrapper.find("[data-testid=home-projects-empty]").exists()).toBe(true); expect(wrapper.find("[data-testid=home-project-card]").exists()).toBe(false); }); });