Files
qavion-time-tracker-assessment/frontend/tests/TimeEntryForm.test.tsx
achim bef6abdd2e test(frontend): add component tests — TimeEntryForm, WeeklyView, App smoke
- TimeEntryForm.test.tsx: fills date/project/h:mm, clicks submit,
  asserts fetch called with correct JSON body (mocked fetch)
- WeeklyView.test.tsx: renders with stubbed WeeklyReport fixture,
  asserts each day's h:mm and the week total display correctly
- App.test.tsx: smoke test — renders layout with TopBar and main content

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:57:07 +02:00

82 lines
2.3 KiB
TypeScript

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(
<MemoryRouter>
<TimeEntryForm userId={1} onSaved={onSaved} />
</MemoryRouter>,
);
}
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();
});
});