mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 03:32:06 +00:00
149 lines
5.3 KiB
TypeScript
149 lines
5.3 KiB
TypeScript
|
|
import { act, screen, waitFor, within } from "@testing-library/react";
|
||
|
|
import userEvent from "@testing-library/user-event";
|
||
|
|
import { useLocation } from "react-router";
|
||
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||
|
|
|
||
|
|
import { SlaAtRiskBanner } from "@/components/notifications/sla-at-risk-banner";
|
||
|
|
import { NotificationSessionProvider } from "@/domain/notifications/session/notification-session-provider";
|
||
|
|
import { queryKeys } from "@/infra/query-key/query-key";
|
||
|
|
import { renderWithProviders } from "@/test/test-utils";
|
||
|
|
|
||
|
|
const apiGet = vi.hoisted(() => vi.fn());
|
||
|
|
const toastWarn = vi.hoisted(() => vi.fn());
|
||
|
|
|
||
|
|
vi.mock("@/api/api", () => ({ apiGet }));
|
||
|
|
vi.mock("react-toastify", () => ({ toast: { warn: toastWarn } }));
|
||
|
|
|
||
|
|
const BANNER = "Work orders at risk of missing their response deadline";
|
||
|
|
|
||
|
|
function atRisk(id: number, number: string | null = String(1000 + id)) {
|
||
|
|
return {
|
||
|
|
id,
|
||
|
|
number,
|
||
|
|
severity: 1,
|
||
|
|
startedAt: "2026-09-18T10:00:00Z",
|
||
|
|
deadlineAt: "2026-09-18T12:00:00Z",
|
||
|
|
percentElapsed: 60,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function feed(...slaAtRisk: ReturnType<typeof atRisk>[]) {
|
||
|
|
return { generatedAt: "2026-09-18T11:12:00Z", sections: [], slaAtRisk };
|
||
|
|
}
|
||
|
|
|
||
|
|
function LocationProbe() {
|
||
|
|
const location = useLocation();
|
||
|
|
return <output aria-label="Current location">{`${location.pathname}${location.search}`}</output>;
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderBanner() {
|
||
|
|
return renderWithProviders(
|
||
|
|
<NotificationSessionProvider>
|
||
|
|
<SlaAtRiskBanner />
|
||
|
|
<LocationProbe />
|
||
|
|
</NotificationSessionProvider>,
|
||
|
|
{ withAuth: false },
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
describe("SLA at-risk banner", () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
apiGet.mockReset();
|
||
|
|
toastWarn.mockReset();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("stays hidden while no work order is past half of its response window", async () => {
|
||
|
|
apiGet.mockResolvedValue(feed());
|
||
|
|
renderBanner();
|
||
|
|
|
||
|
|
await waitFor(() => expect(apiGet).toHaveBeenCalledWith("notifications"));
|
||
|
|
expect(screen.queryByRole("region", { name: BANNER })).not.toBeInTheDocument();
|
||
|
|
expect(toastWarn).not.toHaveBeenCalled();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("names the single work order at risk", async () => {
|
||
|
|
apiGet.mockResolvedValue(feed(atRisk(101)));
|
||
|
|
renderBanner();
|
||
|
|
|
||
|
|
const banner = await screen.findByRole("region", { name: BANNER });
|
||
|
|
expect(
|
||
|
|
within(banner).getByRole("button", {
|
||
|
|
name: "WO #1101 is at risk of missing its response deadline",
|
||
|
|
}),
|
||
|
|
).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("collapses several work orders to one line and opens Work Orders on exactly those", async () => {
|
||
|
|
apiGet.mockResolvedValue(feed(atRisk(101), atRisk(205), atRisk(318)));
|
||
|
|
const user = userEvent.setup();
|
||
|
|
renderBanner();
|
||
|
|
|
||
|
|
const banner = await screen.findByRole("region", { name: BANNER });
|
||
|
|
const open = within(banner).getByRole("button", {
|
||
|
|
name: "3 work orders are at risk of missing their response deadline",
|
||
|
|
});
|
||
|
|
expect(open).toHaveAttribute("title", "View in Work Orders");
|
||
|
|
expect(within(banner).getAllByRole("button")).toHaveLength(2);
|
||
|
|
|
||
|
|
await user.click(open);
|
||
|
|
|
||
|
|
expect(screen.getByRole("status", { name: "Current location" })).toHaveTextContent(
|
||
|
|
"/workorders?ids=101,205,318",
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("dismisses for this session only and returns when a new work order becomes at risk", async () => {
|
||
|
|
apiGet.mockResolvedValue(feed(atRisk(101), atRisk(205)));
|
||
|
|
const user = userEvent.setup();
|
||
|
|
const { queryClient, unmount } = renderBanner();
|
||
|
|
|
||
|
|
const banner = await screen.findByRole("region", { name: BANNER });
|
||
|
|
await user.click(within(banner).getByRole("button", { name: "Dismiss SLA alert" }));
|
||
|
|
expect(screen.queryByRole("region", { name: BANNER })).not.toBeInTheDocument();
|
||
|
|
|
||
|
|
apiGet.mockResolvedValue(feed(atRisk(101), atRisk(205), atRisk(318)));
|
||
|
|
await act(() => queryClient.invalidateQueries({ queryKey: queryKeys.notifications.feed() }));
|
||
|
|
|
||
|
|
const returned = await screen.findByRole("region", { name: BANNER });
|
||
|
|
expect(
|
||
|
|
within(returned).getByRole("button", {
|
||
|
|
name: "WO #1318 is at risk of missing its response deadline",
|
||
|
|
}),
|
||
|
|
).toBeInTheDocument();
|
||
|
|
|
||
|
|
unmount();
|
||
|
|
renderBanner();
|
||
|
|
expect(
|
||
|
|
await screen.findByRole("button", {
|
||
|
|
name: "3 work orders are at risk of missing their response deadline",
|
||
|
|
}),
|
||
|
|
).toBeInTheDocument();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("toasts each work order once per session, however often the feed polls", async () => {
|
||
|
|
apiGet.mockResolvedValue(feed(atRisk(101), atRisk(205, null)));
|
||
|
|
const { queryClient } = renderBanner();
|
||
|
|
|
||
|
|
await screen.findByRole("region", { name: BANNER });
|
||
|
|
expect(toastWarn).toHaveBeenCalledTimes(2);
|
||
|
|
expect(toastWarn).toHaveBeenCalledWith("WO #1101 is at 50% of its SLA response window", {
|
||
|
|
toastId: "sla-at-risk-101",
|
||
|
|
});
|
||
|
|
expect(toastWarn).toHaveBeenCalledWith("WO 205 is at 50% of its SLA response window", {
|
||
|
|
toastId: "sla-at-risk-205",
|
||
|
|
});
|
||
|
|
|
||
|
|
apiGet.mockResolvedValue(feed(atRisk(101), atRisk(205, null), atRisk(318)));
|
||
|
|
await act(() => queryClient.invalidateQueries({ queryKey: queryKeys.notifications.feed() }));
|
||
|
|
await screen.findByRole("button", {
|
||
|
|
name: "3 work orders are at risk of missing their response deadline",
|
||
|
|
});
|
||
|
|
await act(() => queryClient.invalidateQueries({ queryKey: queryKeys.notifications.feed() }));
|
||
|
|
|
||
|
|
expect(toastWarn).toHaveBeenCalledTimes(3);
|
||
|
|
expect(toastWarn).toHaveBeenLastCalledWith("WO #1318 is at 50% of its SLA response window", {
|
||
|
|
toastId: "sla-at-risk-318",
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|