test(stock-orders): QuickDraftModal edit-mode coverage
Asserts the modal hides non-targeted drafts, renders qty/cost inputs per line, PATCHes only changed fields on Save, and that remove still works in edit mode. Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
2303d8a29d
commit
5e91a5d7c0
1 changed files with 292 additions and 0 deletions
|
|
@ -616,3 +616,295 @@ describe("QuickDraftModal — no-history fallback", () => {
|
|||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("QuickDraftModal — edit mode", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("renders only the targeted draft and hides the others", async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
ok: true,
|
||||
items: [
|
||||
{
|
||||
id: "po-1",
|
||||
purchase_number: "PO26073001STK",
|
||||
supplier: "sup-1",
|
||||
supplier_name: "Steel & Pipe Co",
|
||||
lines: [
|
||||
{
|
||||
id: "line-1",
|
||||
description: "115mm Cutting Disc",
|
||||
quantity: 50,
|
||||
cost_price: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "po-2",
|
||||
purchase_number: "PO26073002STK",
|
||||
supplier: "sup-2",
|
||||
supplier_name: "Hardware Hub",
|
||||
lines: [
|
||||
{
|
||||
id: "line-3",
|
||||
description: "Bolt pack",
|
||||
quantity: 5,
|
||||
cost_price: 100,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
render(
|
||||
<QuickDraftModal
|
||||
open
|
||||
onOpenChange={() => {}}
|
||||
editPurchaseId="po-1"
|
||||
/>,
|
||||
);
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
|
||||
expect(screen.getByText(/Steel & Pipe Co/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Hardware Hub/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders edit-mode qty + cost inputs for each line of the targeted draft", async () => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
ok: true,
|
||||
items: [
|
||||
{
|
||||
id: "po-1",
|
||||
purchase_number: "PO26073001STK",
|
||||
supplier: "sup-1",
|
||||
supplier_name: "Steel & Pipe Co",
|
||||
lines: [
|
||||
{
|
||||
id: "line-1",
|
||||
description: "115mm Cutting Disc",
|
||||
quantity: 50,
|
||||
cost_price: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
render(
|
||||
<QuickDraftModal
|
||||
open
|
||||
onOpenChange={() => {}}
|
||||
editPurchaseId="po-1"
|
||||
/>,
|
||||
);
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
|
||||
const qtyInput = screen.getByTestId(
|
||||
"quick-draft-edit-qty-line-1",
|
||||
) as HTMLInputElement;
|
||||
expect(qtyInput).toBeInTheDocument();
|
||||
expect(qtyInput.value).toBe("50");
|
||||
|
||||
const costInput = screen.getByTestId(
|
||||
"quick-draft-edit-cost-line-1",
|
||||
) as HTMLInputElement;
|
||||
expect(costInput).toBeInTheDocument();
|
||||
expect(costInput.value).toBe("20");
|
||||
});
|
||||
|
||||
it("Save PATCHes only the changed fields and refetches cards", async () => {
|
||||
const { toast } = await import("sonner");
|
||||
const successSpy = vi
|
||||
.spyOn(toast, "success")
|
||||
.mockImplementation(() => "");
|
||||
|
||||
global.fetch = vi
|
||||
.fn()
|
||||
// initial cards fetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
ok: true,
|
||||
items: [
|
||||
{
|
||||
id: "po-1",
|
||||
purchase_number: "PO26073001STK",
|
||||
supplier: "sup-1",
|
||||
supplier_name: "Steel & Pipe Co",
|
||||
lines: [
|
||||
{
|
||||
id: "line-1",
|
||||
description: "115mm Cutting Disc",
|
||||
quantity: 50,
|
||||
cost_price: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
// PATCH response
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ ok: true }),
|
||||
})
|
||||
// refetch cards after save
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
ok: true,
|
||||
items: [
|
||||
{
|
||||
id: "po-1",
|
||||
purchase_number: "PO26073001STK",
|
||||
supplier: "sup-1",
|
||||
supplier_name: "Steel & Pipe Co",
|
||||
lines: [
|
||||
{
|
||||
id: "line-1",
|
||||
description: "115mm Cutting Disc",
|
||||
quantity: 75,
|
||||
cost_price: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
render(
|
||||
<QuickDraftModal
|
||||
open
|
||||
onOpenChange={() => {}}
|
||||
editPurchaseId="po-1"
|
||||
/>,
|
||||
);
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
|
||||
const qtyInput = screen.getByTestId(
|
||||
"quick-draft-edit-qty-line-1",
|
||||
) as HTMLInputElement;
|
||||
fireEvent.change(qtyInput, { target: { value: "75" } });
|
||||
fireEvent.click(screen.getByTestId("quick-draft-save-line-line-1"));
|
||||
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
|
||||
const patchCalls = (
|
||||
global.fetch as ReturnType<typeof vi.fn>
|
||||
).mock.calls.filter(
|
||||
(c) => typeof c[0] === "string" && c[0].includes("/line/line-1"),
|
||||
);
|
||||
expect(patchCalls.length).toBeGreaterThan(0);
|
||||
expect(patchCalls[0][1].method).toBe("PATCH");
|
||||
const body = JSON.parse(patchCalls[0][1].body);
|
||||
expect(body).toEqual({ quantity: 75 });
|
||||
expect(successSpy).toHaveBeenCalledWith("Line updated");
|
||||
successSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("remove still works in edit mode", async () => {
|
||||
global.fetch = vi
|
||||
.fn()
|
||||
// initial cards fetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
ok: true,
|
||||
items: [
|
||||
{
|
||||
id: "po-1",
|
||||
purchase_number: "PO26073001STK",
|
||||
supplier: "sup-1",
|
||||
supplier_name: "Steel & Pipe Co",
|
||||
lines: [
|
||||
{
|
||||
id: "line-1",
|
||||
description: "115mm Cutting Disc",
|
||||
quantity: 50,
|
||||
cost_price: 20,
|
||||
},
|
||||
{
|
||||
id: "line-2",
|
||||
description: "Angle grinder",
|
||||
quantity: 1,
|
||||
cost_price: 1500,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
// DELETE response
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ ok: true }),
|
||||
})
|
||||
// refetch cards after delete
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
ok: true,
|
||||
items: [
|
||||
{
|
||||
id: "po-1",
|
||||
purchase_number: "PO26073001STK",
|
||||
supplier: "sup-1",
|
||||
supplier_name: "Steel & Pipe Co",
|
||||
lines: [
|
||||
{
|
||||
id: "line-1",
|
||||
description: "115mm Cutting Disc",
|
||||
quantity: 50,
|
||||
cost_price: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
render(
|
||||
<QuickDraftModal
|
||||
open
|
||||
onOpenChange={() => {}}
|
||||
editPurchaseId="po-1"
|
||||
/>,
|
||||
);
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId("quick-draft-remove-line-line-2"));
|
||||
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
});
|
||||
|
||||
const deleteCalls = (
|
||||
global.fetch as ReturnType<typeof vi.fn>
|
||||
).mock.calls.filter(
|
||||
(c) => typeof c[0] === "string" && c[0].includes("/line/line-2"),
|
||||
);
|
||||
expect(deleteCalls.length).toBe(1);
|
||||
expect(deleteCalls[0][1].method).toBe("DELETE");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue