import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { MemoryRouter } from "react-router-dom"; import { describe, expect, it, vi } from "vitest"; import { TimeEntryForm } from "../src/components/TimeEntryForm"; // stub api module vi.mock("../src/lib/api", () => ({ api: { projects: { list: () => Promise.resolve([ { id: 1, name: "Test Project", timeBudgetMinutes: 6000, costBudgetCents: 100000, hourlyRateCents: 10000, minutesLogged: 0, costLoggedCents: 0, timeExceeded: false, costExceeded: false, }, ]), }, timeEntries: { create: vi.fn().mockResolvedValue({ id: 42, userId: 1, userName: "Alice", projectId: 1, projectName: "Test Project", entryDate: "2026-05-16", durationMinutes: 90, }), }, }, })); function renderForm(onSaved = vi.fn()) { return render( , ); } describe("TimeEntryForm", () => { it("submits with correct durationMinutes", async () => { const onSaved = vi.fn(); renderForm(onSaved); // wait for project list to load await waitFor(() => expect(screen.getByRole("combobox")).toBeInTheDocument(), ); const durationInput = screen.getByPlaceholderText(/e\.g\. 2:30/i); fireEvent.change(durationInput, { target: { value: "1:30" } }); fireEvent.click(screen.getByRole("button", { name: /log/i })); const { api } = await import("../src/lib/api"); await waitFor(() => { expect(api.timeEntries.create).toHaveBeenCalledWith( expect.objectContaining({ durationMinutes: 90 }), ); }); await waitFor(() => expect(onSaved).toHaveBeenCalled()); }); it("shows error for invalid duration format", async () => { renderForm(); await waitFor(() => screen.getByRole("combobox")); const durationInput = screen.getByPlaceholderText(/e\.g\. 2:30/i); fireEvent.change(durationInput, { target: { value: "abc" } }); fireEvent.click(screen.getByRole("button", { name: /log/i })); expect(await screen.findByText(/h:mm format/i)).toBeInTheDocument(); }); });