mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 15:33:12 +00:00
test(completion-docs): give typing-heavy dialog tests the vendor-suite budget
Use user-event without per-keystroke delays and a 15 s budget per test, as the vendor create modal suite does, so the dialog tests don't time out on a loaded runner.
This commit is contained in:
parent
c1361dbbf3
commit
b6226ef1e6
2 changed files with 49 additions and 41 deletions
|
|
@ -23,6 +23,9 @@ vi.mock("@/providers/auth-context", async (importOriginal) => ({
|
|||
useAuthContext: () => ({ user: { id: "u1", userRoles: auth.userRoles } }),
|
||||
}));
|
||||
|
||||
// delay: null skips per-keystroke timer yields that only slow typing-heavy tests down.
|
||||
let user = userEvent.setup({ delay: null });
|
||||
|
||||
function renderPage() {
|
||||
return renderWithProviders(<CompletionDocsPage />, {
|
||||
route: "/completion-docs",
|
||||
|
|
@ -31,16 +34,17 @@ function renderPage() {
|
|||
}
|
||||
|
||||
async function openTemplate(name: string) {
|
||||
await userEvent.click(await screen.findByRole("row", { name: new RegExp(name) }));
|
||||
await user.click(await screen.findByRole("row", { name: new RegExp(name) }));
|
||||
return screen.findByRole("dialog");
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup({ delay: null });
|
||||
auth.userRoles = "Admin";
|
||||
serveTemplates(api, [BACKFLOW, SCRUBBER]);
|
||||
});
|
||||
|
||||
describe("Completion Docs page", () => {
|
||||
describe("Completion Docs page", { timeout: 15_000 }, () => {
|
||||
it("lists every template with its Document Title, Last Updated and Created By", async () => {
|
||||
renderPage();
|
||||
|
||||
|
|
@ -66,7 +70,7 @@ describe("Completion Docs page", () => {
|
|||
await screen.findByRole("row", { name: /Scrubber Pit Augering/ });
|
||||
const search = screen.getByRole("searchbox", { name: "Search document title" });
|
||||
|
||||
await userEvent.type(search, "backf");
|
||||
await user.type(search, "backf");
|
||||
expect(
|
||||
await screen.findByText("1 template", { selector: "span" }, { timeout: 2000 }),
|
||||
).toBeInTheDocument();
|
||||
|
|
@ -75,8 +79,8 @@ describe("Completion Docs page", () => {
|
|||
searchParams: { search: "backf" },
|
||||
});
|
||||
|
||||
await userEvent.clear(search);
|
||||
await userEvent.type(search, "no such document");
|
||||
await user.clear(search);
|
||||
await user.type(search, "no such document");
|
||||
expect(
|
||||
await screen.findByText("No templates found", {}, { timeout: 2000 }),
|
||||
).toBeInTheDocument();
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ vi.mock("@/providers/auth-context", async (importOriginal) => ({
|
|||
useAuthContext: () => ({ user: { id: "u1", userRoles: "Admin" } }),
|
||||
}));
|
||||
|
||||
// delay: null skips per-keystroke timer yields that only slow typing-heavy tests down.
|
||||
let user = userEvent.setup({ delay: null });
|
||||
|
||||
function renderPage() {
|
||||
return renderWithProviders(<CompletionDocsPage />, {
|
||||
route: "/completion-docs",
|
||||
|
|
@ -39,13 +42,13 @@ function renderPage() {
|
|||
async function openNewTemplate() {
|
||||
renderPage();
|
||||
await screen.findByRole("row", { name: /Backflow Test Report/ });
|
||||
await userEvent.click(screen.getByRole("button", { name: /Add Template/ }));
|
||||
await user.click(screen.getByRole("button", { name: /Add Template/ }));
|
||||
return screen.findByRole("dialog");
|
||||
}
|
||||
|
||||
async function openTemplate(name: string) {
|
||||
renderPage();
|
||||
await userEvent.click(await screen.findByRole("row", { name: new RegExp(name) }));
|
||||
await user.click(await screen.findByRole("row", { name: new RegExp(name) }));
|
||||
return screen.findByRole("dialog");
|
||||
}
|
||||
|
||||
|
|
@ -69,20 +72,21 @@ function unprocessable(body: { code: string; message: string }, message = body.m
|
|||
}
|
||||
|
||||
async function addProcedure(dialog: HTMLElement, index: number, name: string) {
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "+ Add procedure" }));
|
||||
await userEvent.type(within(dialog).getByLabelText(`Procedure ${index} name`), name);
|
||||
await user.click(within(dialog).getByRole("button", { name: "+ Add procedure" }));
|
||||
await user.type(within(dialog).getByLabelText(`Procedure ${index} name`), name);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup({ delay: null });
|
||||
vi.clearAllMocks();
|
||||
serveTemplates(api, [BACKFLOW, SCRUBBER], { count: 0, workOrderIds: [] });
|
||||
});
|
||||
|
||||
describe("Add Template", () => {
|
||||
describe("Add Template", { timeout: 15_000 }, () => {
|
||||
it("blocks saving without a Document Title and says why", async () => {
|
||||
const dialog = await openNewTemplate();
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Add Template/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Add Template/ }));
|
||||
|
||||
expect(await within(dialog).findByText("Document Title is required")).toBeInTheDocument();
|
||||
expect(within(dialog).getByRole("textbox", { name: /Document Title/ })).toHaveFocus();
|
||||
|
|
@ -97,14 +101,14 @@ describe("Add Template", () => {
|
|||
expect(within(dialog).getByText("WO#")).toBeInTheDocument();
|
||||
expect(within(dialog).getAllByRole("textbox")).toHaveLength(1);
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "+ Add safety note" }));
|
||||
await user.click(within(dialog).getByRole("button", { name: "+ Add safety note" }));
|
||||
expect(within(dialog).queryByRole("button", { name: "+ Add safety note" })).toBeNull();
|
||||
await userEvent.type(within(dialog).getByLabelText("Extra safety note"), "Mind the pit");
|
||||
await user.type(within(dialog).getByLabelText("Extra safety note"), "Mind the pit");
|
||||
expect(within(dialog).getByTestId("completion-template-preview")).toHaveTextContent(
|
||||
"Mind the pit",
|
||||
);
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "Remove safety note" }));
|
||||
await user.click(within(dialog).getByRole("button", { name: "Remove safety note" }));
|
||||
expect(within(dialog).queryByLabelText("Extra safety note")).toBeNull();
|
||||
expect(within(dialog).getByTestId("completion-template-preview")).not.toHaveTextContent(
|
||||
"Mind the pit",
|
||||
|
|
@ -120,28 +124,28 @@ describe("Add Template", () => {
|
|||
await addProcedure(dialog, 3, "Restore");
|
||||
expect(previewHeadings(dialog)).toEqual(["Isolate", "Inspect", "Restore"]);
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "Remove procedure 2" }));
|
||||
await user.click(within(dialog).getByRole("button", { name: "Remove procedure 2" }));
|
||||
expect(previewHeadings(dialog)).toEqual(["Isolate", "Restore"]);
|
||||
expect(within(dialog).getByLabelText("Procedure 2 name")).toHaveValue("Restore");
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "+ Add procedure" }));
|
||||
await user.click(within(dialog).getByRole("button", { name: "+ Add procedure" }));
|
||||
expect(previewHeadings(dialog)).toEqual(["Isolate", "Restore", "Procedure 3"]);
|
||||
});
|
||||
|
||||
it("saves the title, note and procedures in order, then confirms with a toast", async () => {
|
||||
const dialog = await openNewTemplate();
|
||||
await userEvent.type(
|
||||
await user.type(
|
||||
within(dialog).getByRole("textbox", { name: /Document Title/ }),
|
||||
" PRV Check ",
|
||||
);
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "+ Add safety note" }));
|
||||
await userEvent.type(within(dialog).getByLabelText("Extra safety note"), "Gloves");
|
||||
await user.click(within(dialog).getByRole("button", { name: "+ Add safety note" }));
|
||||
await user.type(within(dialog).getByLabelText("Extra safety note"), "Gloves");
|
||||
await addProcedure(dialog, 1, "Open");
|
||||
await userEvent.type(within(dialog).getByLabelText("Procedure 1 description"), "a{enter}b");
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "+ Add procedure" }));
|
||||
await user.type(within(dialog).getByLabelText("Procedure 1 description"), "a{enter}b");
|
||||
await user.click(within(dialog).getByRole("button", { name: "+ Add procedure" }));
|
||||
await addProcedure(dialog, 3, "Close");
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Add Template/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Add Template/ }));
|
||||
|
||||
await waitFor(() => expect(toast.success).toHaveBeenCalledWith("Template added"));
|
||||
expect(api.apiPost).toHaveBeenCalledWith("workorders/completion-templates", {
|
||||
|
|
@ -160,9 +164,9 @@ describe("Add Template", () => {
|
|||
unprocessable({ code: "TitleRequired", message: "Document Title is required." }),
|
||||
);
|
||||
const dialog = await openNewTemplate();
|
||||
await userEvent.type(within(dialog).getByRole("textbox", { name: /Document Title/ }), "X");
|
||||
await user.type(within(dialog).getByRole("textbox", { name: /Document Title/ }), "X");
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Add Template/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Add Template/ }));
|
||||
|
||||
expect(await within(dialog).findByText("Document Title is required")).toBeInTheDocument();
|
||||
expect(toast.error).not.toHaveBeenCalled();
|
||||
|
|
@ -172,9 +176,9 @@ describe("Add Template", () => {
|
|||
const message = "Procedure name must be 200 characters or fewer.";
|
||||
api.apiPost.mockRejectedValue(unprocessable({ code: "InvalidValue", message }, message));
|
||||
const dialog = await openNewTemplate();
|
||||
await userEvent.type(within(dialog).getByRole("textbox", { name: /Document Title/ }), "X");
|
||||
await user.type(within(dialog).getByRole("textbox", { name: /Document Title/ }), "X");
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Add Template/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Add Template/ }));
|
||||
|
||||
await waitFor(() => expect(toast.error).toHaveBeenCalledWith(message));
|
||||
expect(within(dialog).queryByText("Document Title is required")).toBeNull();
|
||||
|
|
@ -182,17 +186,17 @@ describe("Add Template", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Edit template", () => {
|
||||
describe("Edit template", { timeout: 15_000 }, () => {
|
||||
it("Cancel discards unsaved changes and returns to the original read-only values", async () => {
|
||||
const dialog = await openTemplate("Backflow Test Report");
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Edit/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Edit/ }));
|
||||
expect(within(dialog).getByText("Live preview")).toBeInTheDocument();
|
||||
const title = within(dialog).getByRole("textbox", { name: /Document Title/ });
|
||||
await userEvent.clear(title);
|
||||
await userEvent.type(title, "Renamed");
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "Remove procedure 1" }));
|
||||
await user.clear(title);
|
||||
await user.type(title, "Renamed");
|
||||
await user.click(within(dialog).getByRole("button", { name: "Remove procedure 1" }));
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: "Cancel" }));
|
||||
await user.click(within(dialog).getByRole("button", { name: "Cancel" }));
|
||||
|
||||
expect(within(dialog).queryByRole("textbox")).not.toBeInTheDocument();
|
||||
expect(within(dialog).getAllByText("Backflow Test Report").length).toBeGreaterThan(0);
|
||||
|
|
@ -200,7 +204,7 @@ describe("Edit template", () => {
|
|||
expect(previewHeadings(dialog)).toEqual(["Backflow Procedure", "Shut-off"]);
|
||||
expect(api.apiPut).not.toHaveBeenCalled();
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Edit/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Edit/ }));
|
||||
expect(within(dialog).getByRole("textbox", { name: /Document Title/ })).toHaveValue(
|
||||
"Backflow Test Report",
|
||||
);
|
||||
|
|
@ -208,10 +212,10 @@ describe("Edit template", () => {
|
|||
|
||||
it("saves an edit, keeps the legacy fields and confirms with a toast", async () => {
|
||||
const dialog = await openTemplate("Backflow Test Report");
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Edit/ }));
|
||||
await userEvent.type(within(dialog).getByRole("textbox", { name: /Document Title/ }), " v2");
|
||||
await user.click(within(dialog).getByRole("button", { name: /Edit/ }));
|
||||
await user.type(within(dialog).getByRole("textbox", { name: /Document Title/ }), " v2");
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Save changes/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Save changes/ }));
|
||||
|
||||
await waitFor(() => expect(toast.success).toHaveBeenCalledWith("Template updated"));
|
||||
expect(api.apiPut).toHaveBeenCalledWith("workorders/completion-templates/12", {
|
||||
|
|
@ -232,12 +236,12 @@ describe("Edit template", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Delete template", () => {
|
||||
describe("Delete template", { timeout: 15_000 }, () => {
|
||||
it("states how many open work orders depend on it and links to exactly those", async () => {
|
||||
serveTemplates(api, [BACKFLOW, SCRUBBER], { count: 2, workOrderIds: [101, 205] });
|
||||
const dialog = await openTemplate("Backflow Test Report");
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Delete/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Delete/ }));
|
||||
|
||||
const confirm = await screen.findByRole("dialog", { name: "Delete this template?" });
|
||||
expect(
|
||||
|
|
@ -246,14 +250,14 @@ describe("Delete template", () => {
|
|||
),
|
||||
).toBeInTheDocument();
|
||||
expect(api.apiDeleteNoContent).not.toHaveBeenCalled();
|
||||
await userEvent.click(within(confirm).getByRole("button", { name: "View linked work orders" }));
|
||||
await user.click(within(confirm).getByRole("button", { name: "View linked work orders" }));
|
||||
expect(navigate).toHaveBeenCalledWith("/workorders?ids=101,205");
|
||||
});
|
||||
|
||||
it("still asks first when nothing depends on it, then deletes with a toast", async () => {
|
||||
const dialog = await openTemplate("Scrubber Pit Augering");
|
||||
|
||||
await userEvent.click(within(dialog).getByRole("button", { name: /Delete/ }));
|
||||
await user.click(within(dialog).getByRole("button", { name: /Delete/ }));
|
||||
|
||||
const confirm = await screen.findByRole("dialog", { name: "Delete this template?" });
|
||||
expect(
|
||||
|
|
@ -263,7 +267,7 @@ describe("Delete template", () => {
|
|||
).toBeInTheDocument();
|
||||
expect(within(confirm).queryByRole("button", { name: "View linked work orders" })).toBeNull();
|
||||
|
||||
await userEvent.click(within(confirm).getByRole("button", { name: "Delete" }));
|
||||
await user.click(within(confirm).getByRole("button", { name: "Delete" }));
|
||||
|
||||
await waitFor(() => expect(toast.success).toHaveBeenCalledWith("Template deleted"));
|
||||
expect(api.apiDeleteNoContent).toHaveBeenCalledWith("workorders/completion-templates/13");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue