feat(ui): refine global shell utilities

This commit is contained in:
cesnimda
2026-09-28 18:19:27 +02:00
parent 8e56ceabf5
commit 0f54007015
18 changed files with 322 additions and 63 deletions
@@ -1,6 +1,6 @@
import React from "react";
import "@testing-library/jest-dom";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { api } from "./api";
import { I18nProvider } from "./i18n/I18nProvider";
import QuickCommandDialog from "./components/QuickCommandDialog";
@@ -48,12 +48,80 @@ test("[PRES-E07 PRES-J04] the capture command opens review without creating a jo
test("[P5-00 PRES-E07] command and empty results use valid list semantics", async () => {
render(<I18nProvider><QuickCommandDialog open onClose={jest.fn()} onNavigate={jest.fn()} onOpenAddJob={jest.fn()} /></I18nProvider>);
const list = screen.getByRole("list");
expect(screen.getAllByRole("listitem")).toHaveLength(6);
expect(Array.from(list.children).every(item => item.tagName === "LI")).toBe(true);
expect(screen.getAllByRole("list").every(list => Array.from(list.children).every(item => item.tagName === "LI"))).toBe(true);
fireEvent.change(screen.getByRole("textbox"), { target: { value: "no-such-result" } });
mockedApi.get.mockResolvedValue({ data: [] } as never);
await screen.findByText("No matching commands or records.");
expect(Array.from(list.children).every(item => item.tagName === "LI")).toBe(true);
expect(screen.queryAllByRole("list").every(list => Array.from(list.children).every(item => item.tagName === "LI"))).toBe(true);
expect(mockedApi.post).not.toHaveBeenCalled();
});
test("[P5-02 PRES-E07] search failure is distinct from no matches and Retry recovers", async () => {
mockedApi.get.mockRejectedValueOnce(new Error("offline"));
render(<I18nProvider><QuickCommandDialog open onClose={jest.fn()} onNavigate={jest.fn()} onOpenAddJob={jest.fn()} /></I18nProvider>);
fireEvent.change(screen.getByRole("textbox", { name: "Quick Search" }), { target: { value: "Nordlys" } });
expect(await screen.findByRole("alert")).toHaveTextContent("Search could not be loaded");
expect(screen.queryByText("No matching commands or records.")).not.toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "Retry" }));
expect(await screen.findByText("Nordlys AS - Platform engineer")).toBeInTheDocument();
});
test("[P5-02 PRES-E09] closed and superseded searches cannot display stale account results", async () => {
let resolveOld!: (value: unknown) => void;
mockedApi.get.mockImplementation((url: string) => url === "/jobapplications"
? new Promise(resolve => { resolveOld = resolve; }) as never
: Promise.resolve({ data: [] }) as never);
const props = { onClose: jest.fn(), onNavigate: jest.fn(), onOpenAddJob: jest.fn() };
const view = (open: boolean) => <I18nProvider><QuickCommandDialog open={open} {...props} /></I18nProvider>;
const { rerender } = render(view(true));
fireEvent.change(screen.getByRole("textbox"), { target: { value: "old" } });
await waitFor(() => expect(resolveOld).toBeDefined());
rerender(view(false));
rerender(view(true));
await act(async () => resolveOld({ data: { items: [{ id: 42, jobTitle: "Account A job", company: { name: "Private" } }] } }));
expect(screen.queryByText(/Account A job/)).not.toBeInTheDocument();
expect(screen.getByRole("textbox")).toHaveValue("");
});
test("[P5-02 PRES-E07] a slower old query cannot replace the newer matching results", async () => {
let resolveOld!: (value: unknown) => void;
mockedApi.get.mockImplementation((url: string, config?: any) => url === "/companies"
? Promise.resolve({ data: [] }) as never
: config?.params?.q === "old"
? new Promise(resolve => { resolveOld = resolve; }) as never
: Promise.resolve({ data: { items: [{ id: 9, jobTitle: "New result", company: { name: "Current" } }] } }) as never);
render(<I18nProvider><QuickCommandDialog open onClose={jest.fn()} onNavigate={jest.fn()} onOpenAddJob={jest.fn()} /></I18nProvider>);
const search = screen.getByRole("textbox", { name: "Quick Search" });
fireEvent.change(search, { target: { value: "old" } });
await waitFor(() => expect(resolveOld).toBeDefined());
fireEvent.change(search, { target: { value: "new" } });
expect(await screen.findByText("Current - New result")).toBeInTheDocument();
await act(async () => resolveOld({ data: { items: [{ id: 8, jobTitle: "Old result", company: { name: "Private" } }] } }));
expect(screen.queryByText("Private - Old result")).not.toBeInTheDocument();
});
test("[P5-02 PRES-E07] arrows, Home, End and Enter activate one existing command; composition does not", () => {
const onNavigate = jest.fn();
render(<I18nProvider><QuickCommandDialog open onClose={jest.fn()} onNavigate={onNavigate} onOpenAddJob={jest.fn()} /></I18nProvider>);
const search = screen.getByRole("textbox", { name: "Quick Search" });
fireEvent.keyDown(search, { key: "End" });
fireEvent.keyDown(search, { key: "Enter", isComposing: true });
expect(onNavigate).not.toHaveBeenCalled();
fireEvent.keyDown(search, { key: "Home" });
fireEvent.keyDown(search, { key: "ArrowDown" });
fireEvent.keyDown(search, { key: "Enter" });
expect(onNavigate).toHaveBeenCalledWith("/jobs");
});
test("[P5-02 PRES-E07] arrowing from a focused result keeps focus and activation on the same item", () => {
const onNavigate = jest.fn();
render(<I18nProvider><QuickCommandDialog open onClose={jest.fn()} onNavigate={onNavigate} onOpenAddJob={jest.fn()} /></I18nProvider>);
const dashboard = screen.getByRole("button", { name: /go to dashboard/i });
act(() => dashboard.focus());
fireEvent.keyDown(dashboard, { key: "ArrowDown" });
const jobs = screen.getByRole("button", { name: /go to jobs/i });
expect(jobs).toHaveFocus();
fireEvent.click(jobs);
expect(onNavigate).toHaveBeenCalledWith("/jobs");
});