/** * Inspect page — verifies the unified per-kind browser over the native deep * manifest: the kind rail lists present kinds with counts, the node list * filters, and selecting a node renders its detail + relationships. */ import { describe, it, expect, beforeEach, vi } from "vitest"; import { mount, flushPromises } from "@vue/test-utils"; import { createRouter, createMemoryHistory } from "vue-router"; import { VueQueryPlugin } from "@tanstack/vue-query"; import Inspect from "../Inspect.vue"; const manifest = { version: 3, actions: [ { name: "orders.place", app: "orders", public: true, description: "Place an order", inputSchema: { type: "object" }, emits: ["orders.placed"], source: { file: "orders/place.ts", line: 3 }, }, ], events: [{ name: "orders.placed", app: "orders" }], model: { nodes: [ { id: "action:orders.place", kind: "action", name: "orders.place", intent: { description: "Place an order", public: true }, data: { emits: ["orders.placed"] }, }, { id: "event:orders.placed", kind: "event", name: "orders.placed" }, ], edges: [{ from: "action:orders.place", to: "event:orders.placed", type: "emits" }], }, }; beforeEach(() => { globalThis.fetch = vi.fn((url: string | URL) => { if (String(url).includes("/__nwire/manifest.json")) { return Promise.resolve(new Response(JSON.stringify(manifest), { status: 200 })); } return Promise.resolve(new Response("", { status: 404 })); }) as typeof fetch; }); function makeRouter() { return createRouter({ history: createMemoryHistory(), routes: [ { path: "/inspect", name: "inspect", component: Inspect }, { path: "/dispatch", name: "dispatch", component: { template: "
" } }, ], }); } async function mountInspect(query = "") { const router = makeRouter(); await router.push(`/inspect${query}`); const wrapper = mount(Inspect, { global: { plugins: [router, VueQueryPlugin] } }); await flushPromises(); await flushPromises(); return { wrapper, router }; } describe("Inspect page", () => { it("lists present kinds with counts in the rail", async () => { const { wrapper } = await mountInspect(); const rail = wrapper.get('[data-testid="kind-rail"]'); expect(rail.text()).toContain("Actions"); expect(rail.text()).toContain("Events"); expect(wrapper.find('[data-testid="kind-action"]').exists()).toBe(true); }); it("auto-selects the first node and renders its detail", async () => { const { wrapper } = await mountInspect(); expect(wrapper.get('[data-testid="inspect-detail"]').text()).toContain("orders.place"); // dispatchable kind → the Dispatch link shows expect(wrapper.find('[data-testid="dispatch-link"]').exists()).toBe(true); }); it("shows relationships for the selected node", async () => { const { wrapper } = await mountInspect(); const rel = wrapper.find('[data-testid="relationships"]'); expect(rel.exists()).toBe(true); expect(rel.text()).toContain("orders.placed"); }); it("switches kinds when a rail entry is clicked", async () => { const { wrapper } = await mountInspect(); await wrapper.get('[data-testid="kind-event"]').trigger("click"); await flushPromises(); expect(wrapper.get('[data-testid="inspect-detail"]').text()).toContain("orders.placed"); }); it("honors ?kind= + ?id= deep-links", async () => { const { wrapper } = await mountInspect("?kind=event&id=event:orders.placed"); expect(wrapper.get('[data-testid="inspect-detail"]').text()).toContain("orders.placed"); }); it("filters the node list", async () => { const { wrapper } = await mountInspect(); const input = wrapper.get('[data-testid="filter-input"]'); await input.setValue("zzz"); await flushPromises(); expect(wrapper.findAll('[data-testid="inspect-row"]')).toHaveLength(0); }); });