feat: added initial implementation
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { vi } from "vitest";
|
||||
import { MemoryRouter, Route, Routes } from "react-router-dom";
|
||||
import BookPage from "./BookPage";
|
||||
import { AuthProvider } from "../contexts/AuthContext";
|
||||
import i18n from "../i18n";
|
||||
|
||||
vi.mock("../components/ProtectedRoute", () => ({
|
||||
default: ({ children }) => <>{children}</>,
|
||||
}));
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
apiGet: vi.fn(),
|
||||
apiPost: vi.fn(),
|
||||
}));
|
||||
|
||||
const { apiGet, apiPost } = await import("../api/client");
|
||||
|
||||
function renderBook(initialEntries = ["/book?salon=1"]) {
|
||||
return render(
|
||||
<AuthProvider>
|
||||
<MemoryRouter initialEntries={initialEntries}>
|
||||
<Routes>
|
||||
<Route path="/book" element={<BookPage />} />
|
||||
<Route path="/pay" element={<div>Pay Page</div>} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</AuthProvider>
|
||||
);
|
||||
}
|
||||
|
||||
const salonFixture = {
|
||||
id: 1,
|
||||
name: "Riyadh Salon",
|
||||
services: [
|
||||
{ id: 10, name: "Cut", duration_minutes: 60, price_amount: 120, currency: "SAR" },
|
||||
],
|
||||
staff: [{ id: 99, name: "Mona" }],
|
||||
};
|
||||
|
||||
describe("BookPage", () => {
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
apiGet.mockResolvedValue(salonFixture);
|
||||
await i18n.changeLanguage("en");
|
||||
});
|
||||
|
||||
it("validates required fields", async () => {
|
||||
renderBook();
|
||||
await screen.findByText("Riyadh Salon");
|
||||
fireEvent.click(screen.getByRole("button", { name: "Confirm booking" }));
|
||||
expect(screen.getByText("Please fill all required fields.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("submits booking and redirects to payment", async () => {
|
||||
apiPost.mockResolvedValueOnce({ id: 55 });
|
||||
renderBook();
|
||||
|
||||
await screen.findByText("Riyadh Salon");
|
||||
fireEvent.change(screen.getByLabelText("Service"), { target: { value: "10" } });
|
||||
fireEvent.change(screen.getByLabelText("Staff"), { target: { value: "99" } });
|
||||
fireEvent.change(screen.getByLabelText("Date"), { target: { value: "2026-03-14" } });
|
||||
fireEvent.change(screen.getByLabelText("Time"), { target: { value: "10:30" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Confirm booking" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Pay Page")).toBeInTheDocument();
|
||||
});
|
||||
expect(apiPost).toHaveBeenCalledWith(
|
||||
"/bookings/",
|
||||
expect.objectContaining({
|
||||
service: 10,
|
||||
staff: 99,
|
||||
start_time: "2026-03-14T10:30:00+03:00",
|
||||
end_time: expect.any(String),
|
||||
}),
|
||||
null
|
||||
);
|
||||
const payload = apiPost.mock.calls[0][1];
|
||||
const startMs = new Date(payload.start_time).getTime();
|
||||
const endMs = new Date(payload.end_time).getTime();
|
||||
expect(endMs - startMs).toBe(60 * 60 * 1000);
|
||||
});
|
||||
|
||||
it("shows API error message", async () => {
|
||||
apiPost.mockRejectedValueOnce(new Error("Booking failed"));
|
||||
renderBook();
|
||||
|
||||
await screen.findByText("Riyadh Salon");
|
||||
fireEvent.change(screen.getByLabelText("Service"), { target: { value: "10" } });
|
||||
fireEvent.change(screen.getByLabelText("Staff"), { target: { value: "99" } });
|
||||
fireEvent.change(screen.getByLabelText("Date"), { target: { value: "2026-03-14" } });
|
||||
fireEvent.change(screen.getByLabelText("Time"), { target: { value: "10:30" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Confirm booking" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Booking failed")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user