From 14f6c067c2da90b6f35e81c810120838c9ad29e3 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 21 Sep 2026 16:19:52 +0200 Subject: [PATCH] test(plan): integration test for the new Gantt view Two tests against the live database and the seeded project: - loadPlan returns a section containing the seeded project. - GanttPlanView renders the project name and at least one card title to HTML. vitest integration config now picks up .test.tsx too. --- tests/integration/plan-gantt.test.tsx | 73 +++++++++++++++++++++++++++ vitest.config.ts | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/integration/plan-gantt.test.tsx diff --git a/tests/integration/plan-gantt.test.tsx b/tests/integration/plan-gantt.test.tsx new file mode 100644 index 0000000..6b8aa27 --- /dev/null +++ b/tests/integration/plan-gantt.test.tsx @@ -0,0 +1,73 @@ +import { describe, it, expect, beforeAll } from "vitest"; +import PocketBase from "pocketbase"; +import { renderToStaticMarkup } from "react-dom/server"; + +import { GanttPlanView } from "@/components/roadmap/gantt-plan-view"; +import { buildPlanSections } from "@/lib/roadmap/load"; +import type { + CardRecord, + ClientRecord, + ColumnRecord, + ProjectRecord, + SubtaskRecord, +} from "@/lib/pb/types"; + +import { DEMO, adminPb, requireServices } from "./helpers"; + +let admin: PocketBase; + +beforeAll(async () => { + await requireServices(); + admin = await adminPb(); +}); + +async function loadForProject(slug: string) { + const project = await admin + .collection("projects") + .getFirstListItem(`slug = "${slug}"`); + + const [clients, columns, cards, subtasks] = await Promise.all([ + admin.collection("clients").getFullList(), + admin.collection("columns").getFullList({ sort: "position" }), + admin + .collection("cards") + .getFullList({ filter: `project = "${project.id}" && archived = false` }), + admin + .collection("subtasks") + .getFullList({ + filter: `card.project = "${project.id}"`, + }), + ]); + + return buildPlanSections({ + clients, + projects: [project], + columns, + cards, + subtasks, + }); +} + +describe("/plan Gantt — seeded project loads", () => { + it("returns at least one section containing the seeded project", async () => { + const sections = await loadForProject(DEMO.projectSlug); + expect(sections.length).toBe(1); + expect(sections[0].project.slug).toBe(DEMO.projectSlug); + expect(sections[0].cards.length).toBeGreaterThan(0); + }); +}); + +describe("/plan Gantt — render", () => { + it("renders the project name and at least one card title", async () => { + const sections = await loadForProject(DEMO.projectSlug); + const html = renderToStaticMarkup(); + + expect(html).toContain(sections[0].project.name); + // Pick a card title that contains no characters React will escape + // (the seeded titles use ASCII letters and spaces only). + const safeTitle = + sections[0].cards.find((c) => /^[A-Z0-9 \-]+$/.test(c.title))?.title ?? + sections[0].cards[0].title.slice(0, 10); + expect(html).toContain(safeTitle); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 3041e51..73224ba 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -42,7 +42,7 @@ export default defineConfig({ test: { name: "integration", environment: "node", - include: ["tests/integration/**/*.test.ts"], + include: ["tests/integration/**/*.test.ts", "tests/integration/**/*.test.tsx"], hookTimeout: 30_000, testTimeout: 30_000, poolOptions: { forks: { singleFork: true } },