Files
CapaKraken/apps/web/src/components/timeline/AllocationPopover.test.tsx
T
Hartmut 8d9e26872b fix(timeline): stabilize popovers on internal scroll + expand test coverage
B-1: useViewportPopover — ignoreScrollContainers option; scroll events
originating inside the timeline canvas no longer close point-anchor popovers
B-2: AllocationPopover, DemandPopover, NewAllocationPopover — thread
scrollContainerRef through so horizontal timeline scroll is ignored
B-3: AllocationPopover — staleTime 0 so SSE reconnect triggers immediate refetch
B-4: useViewportPopover.test.ts — 6 new tests (scroll close, ignore container,
resize close, style clamping)
B-5: AllocationPopover.test.tsx — loading state + happy-path tests added

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-02 20:49:08 +02:00

168 lines
4.5 KiB
TypeScript

import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { AllocationPopover } from "./AllocationPopover.js";
const mockUseQuery = vi.fn();
const mockUseMutation = vi.fn(() => ({ mutate: vi.fn() }));
const mockUseUtils = vi.fn(() => ({
allocation: {
getAssignmentById: { invalidate: vi.fn() },
listView: { invalidate: vi.fn() },
},
}));
vi.mock("~/lib/trpc/client.js", () => ({
trpc: {
useUtils: () => mockUseUtils(),
allocation: {
getAssignmentById: {
useQuery: (...args: unknown[]) => mockUseQuery(...args),
},
},
timeline: {
updateAllocationInline: {
useMutation: () => mockUseMutation(),
},
carveAllocationRange: {
useMutation: () => mockUseMutation(),
},
},
},
}));
vi.mock("~/hooks/useInvalidatePlanningViews.js", () => ({
useInvalidateTimeline: () => vi.fn(),
}));
vi.mock("~/hooks/useViewportPopover.js", () => ({
useViewportPopover: () => ({
ref: { current: null },
style: {},
}),
}));
vi.mock("~/components/ui/DateInput.js", () => ({
DateInput: () => null,
}));
vi.mock("~/components/ui/ProjectCombobox.js", () => ({
ProjectCombobox: () => null,
}));
describe("AllocationPopover", () => {
beforeEach(() => {
mockUseQuery.mockReset();
mockUseMutation.mockClear();
mockUseUtils.mockClear();
});
it("renders an error state when the allocation lookup fails", () => {
mockUseQuery.mockReturnValue({
data: undefined,
isLoading: false,
error: new Error("Assignment not found"),
});
const html = renderToStaticMarkup(
<AllocationPopover
allocationId="assignment_missing"
projectId="project_1"
anchorX={120}
anchorY={40}
onClose={() => {}}
onOpenPanel={() => {}}
/>,
);
expect(html).toContain("data-testid=\"timeline-allocation-popover-error\"");
expect(html).toContain("The selected booking could not be loaded right now.");
expect(html).toContain("Assignment not found");
});
it("renders an unavailable state when the lookup returns no allocation", () => {
mockUseQuery.mockReturnValue({
data: undefined,
isLoading: false,
error: null,
});
const html = renderToStaticMarkup(
<AllocationPopover
allocationId="assignment_missing"
projectId="project_1"
anchorX={120}
anchorY={40}
onClose={() => {}}
onOpenPanel={() => {}}
/>,
);
expect(html).toContain("data-testid=\"timeline-allocation-popover-unavailable\"");
expect(html).toContain("The selected booking could not be resolved from the current timeline data.");
});
it("renders a loading skeleton when the allocation is being fetched", () => {
mockUseQuery.mockReturnValue({
data: undefined,
isLoading: true,
error: null,
});
const html = renderToStaticMarkup(
<AllocationPopover
allocationId="assignment_loading"
projectId="project_1"
anchorX={120}
anchorY={40}
onClose={() => {}}
onOpenPanel={() => {}}
/>,
);
expect(html).toContain("data-testid=\"timeline-allocation-popover-loading\"");
});
it("renders allocation data when provided as initialAllocation", () => {
// useQuery is always called (with enabled: false), so we need a baseline return value
mockUseQuery.mockReturnValue({
data: undefined,
isLoading: false,
error: null,
});
const allocation = {
id: "assignment_1",
entityId: "assignment_1",
projectId: "project_1",
resourceId: "resource_1",
startDate: new Date("2026-01-01"),
endDate: new Date("2026-01-31"),
hoursPerDay: 8,
role: "Developer",
metadata: null,
resource: {
displayName: "Alice Smith",
eid: "alice.smith",
lcrCents: 0,
},
};
const html = renderToStaticMarkup(
<AllocationPopover
allocationId="assignment_1"
projectId="project_1"
initialAllocation={allocation as any}
anchorX={120}
anchorY={40}
onClose={() => {}}
onOpenPanel={() => {}}
/>,
);
expect(html).not.toContain("data-testid=\"timeline-allocation-popover-error\"");
expect(html).not.toContain("data-testid=\"timeline-allocation-popover-unavailable\"");
expect(html).not.toContain("data-testid=\"timeline-allocation-popover-loading\"");
});
});