test(FN-4677): regression tests for transient poll omission vs real removal
Fusion-Task-Id: FN-4677 Fusion-Task-Lineage: b3b9eb56-a76b-469c-9f7b-79232da29807
This commit is contained in:
committed by
gsxdsm
parent
f10eac2a18
commit
5dd75dcf0d
@@ -1,6 +1,9 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { renderHook, waitFor, act } from "@testing-library/react";
|
||||
import { useCurrentProject } from "../useCurrentProject";
|
||||
import {
|
||||
CONSECUTIVE_ABSENCE_THRESHOLD,
|
||||
useCurrentProject,
|
||||
} from "../useCurrentProject";
|
||||
import type { ProjectInfo } from "../../api";
|
||||
|
||||
// Mock the API functions
|
||||
@@ -264,4 +267,162 @@ describe("useCurrentProject", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
describe("transient omission tolerance", () => {
|
||||
const activeProjectTwo: ProjectInfo = { ...mockProjects[1], status: "active" };
|
||||
const projectsWithoutCurrent = (): ProjectInfo[] => [activeProjectTwo];
|
||||
|
||||
async function waitForProjectOneSelection(result: { current: ReturnType<typeof useCurrentProject> }) {
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.currentProject?.id).toBe("proj_1");
|
||||
});
|
||||
}
|
||||
|
||||
it("does not switch on a single transient omission", async () => {
|
||||
(fetchGlobalSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
dashboardCurrentProjectIdByNode: { local: "proj_1" },
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ projects }) => useCurrentProject(projects),
|
||||
{ initialProps: { projects: mockProjects } },
|
||||
);
|
||||
|
||||
await waitForProjectOneSelection(result);
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
|
||||
expect(result.current.currentProject?.id).toBe("proj_1");
|
||||
});
|
||||
|
||||
it("does not switch on two consecutive omissions", async () => {
|
||||
(fetchGlobalSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
dashboardCurrentProjectIdByNode: { local: "proj_1" },
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ projects }) => useCurrentProject(projects),
|
||||
{ initialProps: { projects: mockProjects } },
|
||||
);
|
||||
|
||||
await waitForProjectOneSelection(result);
|
||||
|
||||
for (let i = 0; i < CONSECUTIVE_ABSENCE_THRESHOLD - 1; i += 1) {
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
}
|
||||
|
||||
expect(result.current.currentProject?.id).toBe("proj_1");
|
||||
});
|
||||
|
||||
it("switches after sustained absence reaches threshold", async () => {
|
||||
(fetchGlobalSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
dashboardCurrentProjectIdByNode: { local: "proj_1" },
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ projects }) => useCurrentProject(projects),
|
||||
{ initialProps: { projects: mockProjects } },
|
||||
);
|
||||
|
||||
await waitForProjectOneSelection(result);
|
||||
|
||||
for (let i = 0; i < CONSECUTIVE_ABSENCE_THRESHOLD; i += 1) {
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
}
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.currentProject?.id).toBe("proj_2");
|
||||
});
|
||||
});
|
||||
|
||||
it("resets absence counter when project reappears", async () => {
|
||||
(fetchGlobalSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
dashboardCurrentProjectIdByNode: { local: "proj_1" },
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ projects }) => useCurrentProject(projects),
|
||||
{ initialProps: { projects: mockProjects } },
|
||||
);
|
||||
|
||||
await waitForProjectOneSelection(result);
|
||||
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
rerender({ projects: [...mockProjects] });
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
|
||||
expect(result.current.currentProject?.id).toBe("proj_1");
|
||||
});
|
||||
|
||||
it("does not switch when available projects is empty", async () => {
|
||||
(fetchGlobalSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
dashboardCurrentProjectIdByNode: { local: "proj_1" },
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ projects }) => useCurrentProject(projects),
|
||||
{ initialProps: { projects: mockProjects } },
|
||||
);
|
||||
|
||||
await waitForProjectOneSelection(result);
|
||||
rerender({ projects: [] });
|
||||
|
||||
expect(result.current.currentProject?.id).toBe("proj_1");
|
||||
});
|
||||
|
||||
it("persists switched project after sustained absence", async () => {
|
||||
(fetchGlobalSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
dashboardCurrentProjectIdByNode: { local: "proj_1" },
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ projects }) => useCurrentProject(projects),
|
||||
{ initialProps: { projects: mockProjects } },
|
||||
);
|
||||
|
||||
await waitForProjectOneSelection(result);
|
||||
vi.mocked(updateGlobalSettings).mockClear();
|
||||
|
||||
for (let i = 0; i < CONSECUTIVE_ABSENCE_THRESHOLD; i += 1) {
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
}
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.currentProject?.id).toBe("proj_2");
|
||||
});
|
||||
|
||||
expect(updateGlobalSettings).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
dashboardCurrentProjectIdByNode: { local: "proj_2" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("resets absence tracking after manual project selection", async () => {
|
||||
(fetchGlobalSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
dashboardCurrentProjectIdByNode: { local: "proj_1" },
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ projects }) => useCurrentProject(projects),
|
||||
{ initialProps: { projects: mockProjects } },
|
||||
);
|
||||
|
||||
await waitForProjectOneSelection(result);
|
||||
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
rerender({ projects: projectsWithoutCurrent() });
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentProject(activeProjectTwo);
|
||||
});
|
||||
|
||||
rerender({ projects: [mockProjects[0]] });
|
||||
|
||||
expect(result.current.currentProject?.id).toBe("proj_2");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user