- Added an audit trail feature that logs every create, update, and delete action on delivery notes and orders, capturing detailed changes per field. - Introduced the `AuditTimeline` component to display the history of changes for delivery notes and orders, enhancing traceability. - Updated the `page.tsx` files for delivery notes and orders to fetch and display audit logs, ensuring users can view the history of modifications. - Enhanced the `package.json` with a new migration script for setting up the audit log functionality. This feature improves accountability and transparency in the management of delivery notes and orders.
118 lines
3.8 KiB
TypeScript
118 lines
3.8 KiB
TypeScript
import { cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { SupplierForm } from "./supplier-form";
|
|
|
|
/**
|
|
* Tests for the SupplierForm's duplicate-detection flow. There was no
|
|
* prior supplier-form test file, so this also covers the basic save
|
|
* path that the company/customer tests already exercise — we want
|
|
* the same shape of safety net for every entity that grew the dup
|
|
* check.
|
|
*/
|
|
|
|
const { createMock, findSupplierDuplicatesMock } = vi.hoisted(() => ({
|
|
createMock: vi.fn(),
|
|
findSupplierDuplicatesMock: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/lib/pocketbase", () => ({
|
|
__esModule: true,
|
|
default: () => ({
|
|
collection: () => ({
|
|
create: createMock,
|
|
update: vi.fn(),
|
|
}),
|
|
}),
|
|
}));
|
|
|
|
vi.mock("sonner", () => ({
|
|
toast: { error: vi.fn(), success: vi.fn() },
|
|
}));
|
|
|
|
vi.mock("@/lib/pb-helpers", () => ({
|
|
findSupplierDuplicates: findSupplierDuplicatesMock,
|
|
}));
|
|
|
|
beforeEach(() => {
|
|
// Default for every debounced duplicate check. Tests layer
|
|
// mockResolvedValueOnce on top; later keystrokes fall back to [].
|
|
findSupplierDuplicatesMock.mockResolvedValue([]);
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Unmount first so the 300ms duplicate-check debounce is cleared
|
|
// before the mocks are reset (otherwise it fires into a bare mock).
|
|
cleanup();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("SupplierForm", () => {
|
|
it("blocks save when an existing supplier has the same name and lets the operator override", async () => {
|
|
// Form is filling out "West Rand". PB returns an existing row
|
|
// with the same name (case-insensitive). Submit should pop the
|
|
// duplicate dialog instead of writing.
|
|
findSupplierDuplicatesMock.mockResolvedValueOnce([
|
|
{ id: "sup-existing", suppliername: "West Rand" } as never,
|
|
]);
|
|
createMock.mockResolvedValue({ id: "sup1" });
|
|
|
|
render(
|
|
<SupplierForm
|
|
open
|
|
onOpenChange={() => undefined}
|
|
onSaved={() => undefined}
|
|
/>
|
|
);
|
|
|
|
const form = screen.getByRole("dialog").querySelector("form")!;
|
|
fireEvent.change(within(form).getByPlaceholderText("e.g. West Rand Industrial"), {
|
|
target: { value: "west rand" },
|
|
});
|
|
fireEvent.click(within(form).getByRole("button", { name: "Create Supplier" }));
|
|
|
|
// Wait for the dialog to show the existing match.
|
|
await waitFor(() => {
|
|
expect(screen.getByText("West Rand")).toBeInTheDocument();
|
|
});
|
|
// The write was NOT performed yet.
|
|
expect(createMock).not.toHaveBeenCalled();
|
|
|
|
// "Save anyway" should land the write with the typed value.
|
|
fireEvent.click(screen.getByRole("button", { name: /save anyway/i }));
|
|
await waitFor(() => {
|
|
expect(createMock).toHaveBeenCalledWith(
|
|
expect.objectContaining({ suppliername: "west rand" })
|
|
);
|
|
});
|
|
});
|
|
|
|
it("excludes the current record from the duplicate check in edit mode", async () => {
|
|
// Bound to an existing supplier; typing the same name shouldn't
|
|
// flag itself. We assert the helper was called with the record's
|
|
// id as the excludeId.
|
|
const existing = { id: "sup-self", suppliername: "West Rand" };
|
|
findSupplierDuplicatesMock.mockResolvedValue([]);
|
|
render(
|
|
<SupplierForm
|
|
open
|
|
onOpenChange={() => undefined}
|
|
onSaved={() => undefined}
|
|
supplier={existing as never}
|
|
/>
|
|
);
|
|
|
|
const form = screen.getByRole("dialog").querySelector("form")!;
|
|
fireEvent.change(within(form).getByPlaceholderText("e.g. West Rand Industrial"), {
|
|
target: { value: "West Rand" },
|
|
});
|
|
|
|
// Live check debounces by 300ms — wait for the helper call.
|
|
await waitFor(() => {
|
|
expect(findSupplierDuplicatesMock).toHaveBeenCalledWith(
|
|
"West Rand",
|
|
"sup-self"
|
|
);
|
|
});
|
|
});
|
|
});
|