Set up unit testing infrastructure
- Extract slot generation logic into apps/api/src/lib/slots.ts for testability - Add 8 unit tests covering slot generation edge cases (overlap, multi-groomer, boundary) - Add @testing-library/react + jsdom to apps/web; configure vitest with jsdom environment - Add 4 component tests for App navigation rendering and active-link highlighting - Remove passWithNoTests: true from both vitest configs; add coverage thresholds Closes #39 Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -18,10 +18,15 @@
|
||||
"react-router-dom": "^7.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/react": "^19.0.6",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"eslint": "^9.18.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "^6.0.7",
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, within, act } from "@testing-library/react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { App } from "../App.js";
|
||||
|
||||
// Prevent fetch errors from page components loading data on mount
|
||||
beforeEach(() => {
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => [],
|
||||
} as unknown as Response);
|
||||
});
|
||||
|
||||
async function renderApp(route = "/") {
|
||||
await act(async () => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={[route]}>
|
||||
<App />
|
||||
</MemoryRouter>
|
||||
);
|
||||
});
|
||||
return screen.getByRole("navigation");
|
||||
}
|
||||
|
||||
describe("App navigation", () => {
|
||||
it("renders the Groom Book brand", async () => {
|
||||
const nav = await renderApp();
|
||||
expect(within(nav).getByText("Groom Book")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the Book CTA button", async () => {
|
||||
const nav = await renderApp();
|
||||
expect(within(nav).getByText("Book")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders all primary nav links", async () => {
|
||||
const nav = await renderApp();
|
||||
const expectedLinks = [
|
||||
"Appointments",
|
||||
"Clients",
|
||||
"Services",
|
||||
"Staff",
|
||||
"Invoices",
|
||||
"Group Bookings",
|
||||
"Reports",
|
||||
];
|
||||
expectedLinks.forEach((label) => {
|
||||
expect(within(nav).getByText(label)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("highlights the active route link", async () => {
|
||||
const nav = await renderApp("/clients");
|
||||
const clientsLink = within(nav).getByText("Clients");
|
||||
// Active links use fontWeight 600
|
||||
expect(clientsLink).toHaveStyle({ fontWeight: "600" });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
import "@testing-library/jest-dom";
|
||||
@@ -1,7 +1,20 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
passWithNoTests: true,
|
||||
environment: "jsdom",
|
||||
setupFiles: ["./src/test/setup.ts"],
|
||||
globals: true,
|
||||
coverage: {
|
||||
provider: "v8",
|
||||
include: ["src/**"],
|
||||
exclude: ["src/test/**", "src/main.tsx"],
|
||||
thresholds: {
|
||||
lines: 50,
|
||||
functions: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user