Files
TJWaterFrontend_Refine/src/contexts/ProjectContext.test.tsx
T
jiang 29b8babd68 fix(api): align frontend with project-scoped backend
Use project-scoped endpoints and run IDs for current project, SCADA metadata, historical time series, migrated DMA results, and sensor placement runs.

The regressions recurred because tests mocked pre-interceptor and new-only payload shapes, while history queries relied on implicit global scheme state and unbounded request fan-out. Cover post-interceptor pages, migrated result_rows, explicit run_id routing, multi-element requests, request limits, and cancellation.
2026-09-01 14:37:51 +08:00

107 lines
2.9 KiB
TypeScript

import { render, waitFor } from "@testing-library/react";
import { ProjectProvider } from "./ProjectContext";
const mockApiFetch = jest.fn();
const mockUseSession = jest.fn();
const mockSetCurrentProjectId = jest.fn();
jest.mock("next-auth/react", () => ({
useSession: () => mockUseSession(),
}));
jest.mock("next/navigation", () => ({
usePathname: () => "/login",
useRouter: () => ({ push: jest.fn() }),
}));
jest.mock("@/components/project/ProjectSelector", () => ({
ProjectSelector: () => <div>project selector</div>,
}));
jest.mock("@/config/config", () => ({
config: {
BACKEND_URL: "http://backend.test",
MAP_EXTENT: [1, 2, 3, 4],
MAP_WORKSPACE: "default-workspace",
},
NETWORK_NAME: "default-network",
setMapExtent: jest.fn(),
setMapWorkspace: jest.fn(),
setNetworkName: jest.fn(),
}));
jest.mock("@/lib/apiFetch", () => ({
apiFetch: (...args: unknown[]) => mockApiFetch(...args),
}));
jest.mock("@/lib/permissions", () => ({
permissionCodes: { environmentManage: "environment:manage" },
}));
jest.mock("@/store/accessStore", () => ({
useAccessStore: (selector: (state: { permissions: string[] }) => unknown) =>
selector({ permissions: [] }),
}));
jest.mock("@/store/projectStore", () => ({
useProjectStore: (
selector: (state: { setCurrentProjectId: typeof mockSetCurrentProjectId }) => unknown,
) => selector({ setCurrentProjectId: mockSetCurrentProjectId }),
}));
const seedSavedProject = () => {
localStorage.setItem("MAP_WORKSPACE", "fengyang");
localStorage.setItem("NETWORK_NAME", "fengyang");
localStorage.setItem("MAP_EXTENT", "1,2,3,4");
localStorage.setItem(
"active_project",
"a2d67c84-fd9d-4feb-a500-c357244b2760",
);
};
describe("ProjectProvider authentication boundary", () => {
beforeEach(() => {
localStorage.clear();
mockApiFetch.mockReset();
mockSetCurrentProjectId.mockReset();
mockApiFetch.mockResolvedValue({
ok: true,
json: async () => ({}),
});
});
it("does not restore or open a saved project while unauthenticated", () => {
seedSavedProject();
mockUseSession.mockReturnValue({ status: "unauthenticated" });
render(
<ProjectProvider>
<div>login page</div>
</ProjectProvider>,
);
expect(mockSetCurrentProjectId).not.toHaveBeenCalled();
expect(mockApiFetch).not.toHaveBeenCalled();
});
it("restores a saved project and reads its current metadata", async () => {
seedSavedProject();
mockUseSession.mockReturnValue({ status: "authenticated" });
render(
<ProjectProvider>
<div>main page</div>
</ProjectProvider>,
);
await waitFor(() => expect(mockApiFetch).toHaveBeenCalledTimes(1));
expect(mockApiFetch).toHaveBeenCalledWith(
"http://backend.test/api/v1/projects/current",
);
expect(mockSetCurrentProjectId).toHaveBeenCalledWith(
"a2d67c84-fd9d-4feb-a500-c357244b2760",
);
});
});