Files
fusion/packages/dashboard/app/components/__tests__/DashboardLoader.test.tsx
gsxdsm bce7dbd96f chore: consolidate test files into __tests__/ dirs and clean stray engine artifacts
- Move all co-located *.test.* files into sibling __tests__/ directories so the
  layout is consistent across packages (159 renames + content-rewrite moves).
  Updates relative imports, vi.mock specifiers, and __dirname/import.meta.url
  path resolutions where tests read fixtures from disk.
- Drop tracked tsc-emit alongside engine .ts sources (auth-storage/logger/
  skill-resolver/context-limit-detector/pi.{js,d.ts,*.map}). These were
  accidentally committed in a merge and the stale pi.js was masking a real
  test-mock vs source mismatch (tests imported "../pi.js" and vite preferred
  the stale build over pi.ts).
- Add packages/engine/.gitignore to block future src/*.{js,d.ts,map}.
- Refactor plugin pi-module seams (openclaw/paperclip/hermes) to ESM-import
  createFnAgent / promptWithFallback / describeModel from @fusion/engine
  instead of require()-ing packages/engine/src/pi.js. Adds @fusion/engine to
  the two plugin package.jsons that were missing it; exports describeModel
  from the engine public API.
- Fix engine test mocks now that they run against current pi.ts: add
  ModelRegistry.create static to mocks in pi.test.ts and pi-create-fn-agent
  .test.ts; switch three boundary-result toEqual assertions to toMatchObject
  so the new content/isError fields don't trip exact-match comparison.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:39:20 -07:00

162 lines
5.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { render, screen } from "@testing-library/react";
import { DashboardLoader } from "../DashboardLoader";
function getStep(label: string): HTMLElement {
const step = screen.getByText(label).closest("li");
if (!step) {
throw new Error(`Could not find step for label: ${label}`);
}
return step;
}
describe("DashboardLoader", () => {
it("renders projects stage with active first step and pending remaining steps", () => {
render(<DashboardLoader stage="projects" />);
expect(screen.getByText("Fusion")).toBeInTheDocument();
expect(getStep("Loading projects").className).toContain("dashboard-loader__step--active");
expect(getStep("Selecting project").className).toContain("dashboard-loader__step--pending");
expect(getStep("Fetching tasks").className).toContain("dashboard-loader__step--pending");
});
it("marks previous stages done when current stage is tasks", () => {
render(<DashboardLoader stage="tasks" />);
expect(getStep("Loading projects").className).toContain("dashboard-loader__step--done");
expect(getStep("Selecting project").className).toContain("dashboard-loader__step--done");
expect(getStep("Fetching tasks").className).toContain("dashboard-loader__step--active");
});
it("marks all steps done when stage is ready", () => {
render(<DashboardLoader stage="ready" />);
expect(getStep("Loading projects").className).toContain("dashboard-loader__step--done");
expect(getStep("Selecting project").className).toContain("dashboard-loader__step--done");
expect(getStep("Fetching tasks").className).toContain("dashboard-loader__step--done");
});
it("announces loading state for assistive technologies", () => {
render(<DashboardLoader stage="project" />);
const statusRegion = screen.getByRole("status", { name: "Loading Fusion dashboard" });
expect(statusRegion).toHaveAttribute("aria-live", "polite");
expect(screen.getByLabelText("Dashboard loading progress")).toBeInTheDocument();
});
it("keeps stage visuals stable across all stages", () => {
const stages = ["projects", "project", "tasks", "ready"] as const;
const snapshots = stages.map((stage) => {
const { container, unmount } = render(<DashboardLoader stage={stage} />);
const steps = Array.from(container.querySelectorAll<HTMLElement>(".dashboard-loader__step")).map((step) => ({
className: step.className,
label: step.querySelector(".dashboard-loader__step-label")?.textContent,
iconText: step.querySelector(".dashboard-loader__step-icon")?.textContent?.trim() ?? "",
hasSpinner: Boolean(step.querySelector(".dashboard-loader__spinner")),
}));
unmount();
return { stage, steps };
});
expect(snapshots).toMatchInlineSnapshot(`
[
{
"stage": "projects",
"steps": [
{
"className": "dashboard-loader__step dashboard-loader__step--active",
"hasSpinner": true,
"iconText": "",
"label": "Loading projects",
},
{
"className": "dashboard-loader__step dashboard-loader__step--pending",
"hasSpinner": false,
"iconText": "•",
"label": "Selecting project",
},
{
"className": "dashboard-loader__step dashboard-loader__step--pending",
"hasSpinner": false,
"iconText": "•",
"label": "Fetching tasks",
},
],
},
{
"stage": "project",
"steps": [
{
"className": "dashboard-loader__step dashboard-loader__step--done",
"hasSpinner": false,
"iconText": "✓",
"label": "Loading projects",
},
{
"className": "dashboard-loader__step dashboard-loader__step--active",
"hasSpinner": true,
"iconText": "",
"label": "Selecting project",
},
{
"className": "dashboard-loader__step dashboard-loader__step--pending",
"hasSpinner": false,
"iconText": "•",
"label": "Fetching tasks",
},
],
},
{
"stage": "tasks",
"steps": [
{
"className": "dashboard-loader__step dashboard-loader__step--done",
"hasSpinner": false,
"iconText": "✓",
"label": "Loading projects",
},
{
"className": "dashboard-loader__step dashboard-loader__step--done",
"hasSpinner": false,
"iconText": "✓",
"label": "Selecting project",
},
{
"className": "dashboard-loader__step dashboard-loader__step--active",
"hasSpinner": true,
"iconText": "",
"label": "Fetching tasks",
},
],
},
{
"stage": "ready",
"steps": [
{
"className": "dashboard-loader__step dashboard-loader__step--done",
"hasSpinner": false,
"iconText": "✓",
"label": "Loading projects",
},
{
"className": "dashboard-loader__step dashboard-loader__step--done",
"hasSpinner": false,
"iconText": "✓",
"label": "Selecting project",
},
{
"className": "dashboard-loader__step dashboard-loader__step--done",
"hasSpinner": false,
"iconText": "✓",
"label": "Fetching tasks",
},
],
},
]
`);
});
});