test(stock-orders): move page tests to StockOrdersTable + add total-value case
The seven pre-existing assertions on the quick-draft modal and edit button now exercise the new client component. New case confirms ordered_value/received_value render in the row. Uses Intl-format substring assertions because the currency glyph/spacing varies by ICU version. Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
b89a4a7fa3
commit
760f088ec9
1 changed files with 209 additions and 0 deletions
209
src/app/(app)/stock-orders/StockOrdersTable.test.tsx
Normal file
209
src/app/(app)/stock-orders/StockOrdersTable.test.tsx
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { cleanup, render, screen, fireEvent } from "@testing-library/react";
|
||||
|
||||
// Stub the QuickDraftModal — it's tested in
|
||||
// `src/components/stock-orders/quick-draft-modal.test.tsx`.
|
||||
vi.mock("@/components/stock-orders/quick-draft-modal", () => ({
|
||||
QuickDraftModal: ({
|
||||
open,
|
||||
onOpenChange,
|
||||
editPurchaseId,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (b: boolean) => void;
|
||||
editPurchaseId?: string;
|
||||
}) => (
|
||||
<div
|
||||
data-testid="quick-draft-modal"
|
||||
data-open={open}
|
||||
data-edit-purchase-id={editPurchaseId ?? ""}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="quick-draft-modal-close"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
// Stub Next's <Link> so the test doesn't pull the router — these
|
||||
// tests don't assert navigation.
|
||||
vi.mock("next/link", () => ({
|
||||
default: ({
|
||||
children,
|
||||
href,
|
||||
...rest
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
href: string;
|
||||
[k: string]: unknown;
|
||||
}) => (
|
||||
<a href={href} {...rest}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}));
|
||||
|
||||
// Stub Next's `useRouter` — the table rows are clickable and push
|
||||
// `/stock-orders/[id]`, which jsdom can't handle.
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
back: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
import StockOrdersTable from "./StockOrdersTable";
|
||||
import type { StockPurchase, Supplier } from "@/lib/types";
|
||||
|
||||
// --- Helpers --------------------------------------------------------------
|
||||
|
||||
// Inline shape (the plan's fallback) — `Parameters<typeof StockOrdersTable>`
|
||||
// is too tight because the `StockPurchase` type requires every `Supplier`
|
||||
// field expanded (email, address, etc.), but the table only reads
|
||||
// `expand.supplier.suppliername`. The component's prop type itself is
|
||||
// fine; this is just a fixture-side cast.
|
||||
type ListRow = StockPurchase & {
|
||||
item_count: number;
|
||||
ordered_value: number;
|
||||
received_value: number;
|
||||
expand?: { supplier?: Supplier };
|
||||
};
|
||||
|
||||
function makeRow(overrides: {
|
||||
id: string;
|
||||
status: "draft" | "sent" | "received" | "cancelled" | "sending" | "mail_failed";
|
||||
purchase_number: string;
|
||||
}): ListRow {
|
||||
return {
|
||||
id: overrides.id,
|
||||
created: "2026-07-30 10:00:00.000Z",
|
||||
updated: "2026-07-30 10:00:00.000Z",
|
||||
collectionId: "stock_purchases",
|
||||
collectionName: "stock_purchases",
|
||||
purchase_number: overrides.purchase_number,
|
||||
supplier: "supplier-1",
|
||||
status: overrides.status,
|
||||
notes: "",
|
||||
email_error: "",
|
||||
email_message_id: "",
|
||||
item_count: 0,
|
||||
ordered_value: 0,
|
||||
received_value: 0,
|
||||
expand: {
|
||||
supplier: {
|
||||
id: "supplier-1",
|
||||
collectionId: "suppliers",
|
||||
collectionName: "suppliers",
|
||||
created: "",
|
||||
updated: "",
|
||||
suppliername: "Steel & Pipe Co",
|
||||
suppliernumber: "SUP-1",
|
||||
supplieraddress: "",
|
||||
email: "",
|
||||
supply_tags: "",
|
||||
coordinates: "",
|
||||
closingtime: "",
|
||||
} as Supplier,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// --- Tests ----------------------------------------------------------------
|
||||
|
||||
describe("StockOrdersTable — quick draft button", () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("renders a New Stock Item button", () => {
|
||||
render(<StockOrdersTable rows={[]} currentStatus="" page={1} totalPages={1} />);
|
||||
const button = screen.getByTestId("new-stock-item-button");
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveTextContent(/new stock item/i);
|
||||
});
|
||||
|
||||
it("mounts the QuickDraftModal (closed by default)", () => {
|
||||
render(<StockOrdersTable rows={[]} currentStatus="" page={1} totalPages={1} />);
|
||||
const modal = screen.getByTestId("quick-draft-modal");
|
||||
expect(modal).toBeInTheDocument();
|
||||
expect(modal.dataset.open).toBe("false");
|
||||
});
|
||||
|
||||
it("opens the QuickDraftModal when the button is clicked", () => {
|
||||
render(<StockOrdersTable rows={[]} currentStatus="" page={1} totalPages={1} />);
|
||||
fireEvent.click(screen.getByTestId("new-stock-item-button"));
|
||||
expect(screen.getByTestId("quick-draft-modal").dataset.open).toBe("true");
|
||||
});
|
||||
|
||||
it("closes the QuickDraftModal when the modal calls onOpenChange(false)", () => {
|
||||
render(<StockOrdersTable rows={[]} currentStatus="" page={1} totalPages={1} />);
|
||||
fireEvent.click(screen.getByTestId("new-stock-item-button"));
|
||||
expect(screen.getByTestId("quick-draft-modal").dataset.open).toBe("true");
|
||||
fireEvent.click(screen.getByTestId("quick-draft-modal-close"));
|
||||
expect(screen.getByTestId("quick-draft-modal").dataset.open).toBe("false");
|
||||
});
|
||||
});
|
||||
|
||||
describe("StockOrdersTable — edit draft button", () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("renders an Edit button on draft rows", () => {
|
||||
const rows = [
|
||||
makeRow({ id: "po-abc", status: "draft", purchase_number: "PO26000001STK" }),
|
||||
];
|
||||
render(<StockOrdersTable rows={rows} currentStatus="" page={1} totalPages={1} />);
|
||||
const editButton = screen.getByTestId("edit-draft-button");
|
||||
expect(editButton).toBeInTheDocument();
|
||||
expect(editButton).toHaveAttribute("aria-label", "Edit draft PO26000001STK");
|
||||
});
|
||||
|
||||
it("does NOT render an Edit button on non-draft rows", () => {
|
||||
const rows = [
|
||||
makeRow({ id: "po-sent", status: "sent", purchase_number: "PO26000002STK" }),
|
||||
];
|
||||
render(<StockOrdersTable rows={rows} currentStatus="" page={1} totalPages={1} />);
|
||||
expect(screen.queryByTestId("edit-draft-button")).toBeNull();
|
||||
});
|
||||
|
||||
it("clicking Edit opens the modal with editPurchaseId set to the row id", () => {
|
||||
const rows = [
|
||||
makeRow({ id: "po-abc", status: "draft", purchase_number: "PO26000001STK" }),
|
||||
];
|
||||
render(<StockOrdersTable rows={rows} currentStatus="" page={1} totalPages={1} />);
|
||||
const modal = screen.getByTestId("quick-draft-modal");
|
||||
expect(modal.dataset.open).toBe("false");
|
||||
expect(modal.dataset.editPurchaseId).toBe("");
|
||||
fireEvent.click(screen.getByTestId("edit-draft-button"));
|
||||
expect(modal.dataset.open).toBe("true");
|
||||
expect(modal.dataset.editPurchaseId).toBe("po-abc");
|
||||
});
|
||||
});
|
||||
|
||||
describe("StockOrdersTable — total value columns", () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("renders ordered_value and received_value formatted as ZAR currency", () => {
|
||||
const row = makeRow({
|
||||
id: "po-1",
|
||||
status: "draft",
|
||||
purchase_number: "PO26000001STK",
|
||||
});
|
||||
row.ordered_value = 521;
|
||||
row.received_value = 75;
|
||||
render(<StockOrdersTable rows={[row]} currentStatus="" page={1} totalPages={1} />);
|
||||
// formatCurrency uses Intl with en-ZA / ZAR — the exact glyph
|
||||
// and spacing can vary by ICU version, so assert on the numeric
|
||||
// parts instead.
|
||||
expect(screen.getAllByText(/521/).length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText(/75/).length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue