feat(stock-quick-draft): mount modal on /stock-orders via New Stock Item button
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
6329a100e7
commit
4cf9c782e6
2 changed files with 153 additions and 0 deletions
138
src/app/(app)/stock-orders/page.test.tsx
Normal file
138
src/app/(app)/stock-orders/page.test.tsx
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { cleanup, render, screen, fireEvent } from "@testing-library/react";
|
||||
|
||||
// ── Mocks ────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// The page is a client component that reads PocketBase via
|
||||
// `@/lib/pocketbase`. We mock both the `pocketbase` npm package
|
||||
// (to keep EventSource out of jsdom) and the local helper module.
|
||||
|
||||
// Shared hoisted state so both mocks agree on what getList returns.
|
||||
const { _state, _collectionMock, _resetState } = vi.hoisted(() => {
|
||||
const state = {
|
||||
items: [] as unknown[],
|
||||
error: undefined as string | undefined,
|
||||
};
|
||||
const collectionMock = {
|
||||
getList: vi.fn().mockImplementation(async () => ({
|
||||
items: state.items,
|
||||
totalItems: state.items.length,
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
totalPages: 1,
|
||||
})),
|
||||
getFullList: vi.fn().mockResolvedValue([]),
|
||||
};
|
||||
return {
|
||||
_state: state,
|
||||
_collectionMock: collectionMock,
|
||||
_resetState: () => {
|
||||
state.items.length = 0;
|
||||
state.error = undefined;
|
||||
collectionMock.getList.mockClear();
|
||||
collectionMock.getList.mockImplementation(async () => ({
|
||||
items: state.items,
|
||||
totalItems: state.items.length,
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
totalPages: 1,
|
||||
}));
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("pocketbase", () => ({
|
||||
default: class MockPocketBase {
|
||||
autoCancellation() {}
|
||||
collection(_name: string) {
|
||||
return _collectionMock;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/pocketbase", () => ({
|
||||
__esModule: true,
|
||||
default: () => ({
|
||||
collection: () => _collectionMock,
|
||||
}),
|
||||
getPocketBase: () => ({
|
||||
collection: () => _collectionMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
back: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
// Stub the QuickDraftModal — it's already tested in
|
||||
// `src/components/stock-orders/quick-draft-modal.test.tsx`. This test
|
||||
// only cares that the page mounts the modal and toggles its `open`
|
||||
// prop when the button is clicked.
|
||||
vi.mock("@/components/stock-orders/quick-draft-modal", () => ({
|
||||
QuickDraftModal: ({
|
||||
open,
|
||||
onOpenChange,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (b: boolean) => void;
|
||||
}) => (
|
||||
<div data-testid="quick-draft-modal" data-open={open}>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="quick-draft-modal-close"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
import StockOrdersPage from "./page";
|
||||
|
||||
// ── Tests ────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("StockOrdersPage — quick draft button", () => {
|
||||
beforeEach(() => {
|
||||
_resetState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("renders a New Stock Item button in the page header", async () => {
|
||||
render(<StockOrdersPage />);
|
||||
const button = await screen.findByTestId("new-stock-item-button");
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveTextContent(/new stock item/i);
|
||||
});
|
||||
|
||||
it("mounts the QuickDraftModal (closed by default)", async () => {
|
||||
render(<StockOrdersPage />);
|
||||
const modal = await screen.findByTestId("quick-draft-modal");
|
||||
expect(modal).toBeInTheDocument();
|
||||
expect(modal.dataset.open).toBe("false");
|
||||
});
|
||||
|
||||
it("opens the QuickDraftModal when the button is clicked", async () => {
|
||||
render(<StockOrdersPage />);
|
||||
const modal = await screen.findByTestId("quick-draft-modal");
|
||||
expect(modal.dataset.open).toBe("false");
|
||||
fireEvent.click(screen.getByTestId("new-stock-item-button"));
|
||||
expect(modal.dataset.open).toBe("true");
|
||||
});
|
||||
|
||||
it("closes the QuickDraftModal when the modal calls onOpenChange(false)", async () => {
|
||||
render(<StockOrdersPage />);
|
||||
const modal = await screen.findByTestId("quick-draft-modal");
|
||||
fireEvent.click(screen.getByTestId("new-stock-item-button"));
|
||||
expect(modal.dataset.open).toBe("true");
|
||||
fireEvent.click(screen.getByTestId("quick-draft-modal-close"));
|
||||
expect(modal.dataset.open).toBe("false");
|
||||
});
|
||||
});
|
||||
|
|
@ -16,6 +16,7 @@ import { Card, CardContent } from "@/components/ui/card";
|
|||
import { Loader2, Plus, AlertCircle, Package, ChevronRight, Boxes } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import getPocketBase from "@/lib/pocketbase";
|
||||
import { QuickDraftModal } from "@/components/stock-orders/quick-draft-modal";
|
||||
import {
|
||||
STOCK_PURCHASE_STATUS_VALUES,
|
||||
type StockPurchase,
|
||||
|
|
@ -93,6 +94,7 @@ export default function StockOrdersPage() {
|
|||
const [status, setStatus] = useState<StockPurchaseStatus | "">("");
|
||||
const [page, setPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
const [quickDraftOpen, setQuickDraftOpen] = useState(false);
|
||||
|
||||
const fetchRows = useCallback(async () => {
|
||||
setLoading(true);
|
||||
|
|
@ -160,6 +162,14 @@ export default function StockOrdersPage() {
|
|||
<Boxes className="mr-2 h-4 w-4" />
|
||||
Reorder from low stock
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
data-testid="new-stock-item-button"
|
||||
onClick={() => setQuickDraftOpen(true)}
|
||||
>
|
||||
New Stock Item
|
||||
</Button>
|
||||
<Button onClick={() => router.push("/stock-orders/new")}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
New stock order
|
||||
|
|
@ -298,6 +308,11 @@ export default function StockOrdersPage() {
|
|||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<QuickDraftModal
|
||||
open={quickDraftOpen}
|
||||
onOpenChange={setQuickDraftOpen}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue