with some tests
This commit is contained in:
parent
a764c92a0b
commit
0995e1cc77
18 changed files with 3020 additions and 16 deletions
68
frontend/app/page.test.tsx
Normal file
68
frontend/app/page.test.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { render, screen, fireEvent, waitFor, cleanup } from "@testing-library/react";
|
||||
import { expect, test, vi, beforeEach, afterEach } from "vitest";
|
||||
import Home from "./page";
|
||||
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test("renders loading state initially", () => {
|
||||
vi.spyOn(global, "fetch").mockImplementation(() => new Promise(() => {}));
|
||||
render(<Home />);
|
||||
expect(screen.getByText("...")).toBeDefined();
|
||||
});
|
||||
|
||||
test("renders counter value after fetch", async () => {
|
||||
vi.spyOn(global, "fetch").mockResolvedValue({
|
||||
json: () => Promise.resolve({ value: 42 }),
|
||||
} as Response);
|
||||
|
||||
render(<Home />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("42")).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
test("renders +1 button", async () => {
|
||||
vi.spyOn(global, "fetch").mockResolvedValue({
|
||||
json: () => Promise.resolve({ value: 0 }),
|
||||
} as Response);
|
||||
|
||||
render(<Home />);
|
||||
expect(screen.getByText("+1")).toBeDefined();
|
||||
});
|
||||
|
||||
test("clicking button calls increment endpoint", async () => {
|
||||
const fetchSpy = vi.spyOn(global, "fetch")
|
||||
.mockResolvedValueOnce({ json: () => Promise.resolve({ value: 0 }) } as Response)
|
||||
.mockResolvedValueOnce({ json: () => Promise.resolve({ value: 1 }) } as Response);
|
||||
|
||||
render(<Home />);
|
||||
await waitFor(() => expect(screen.getByText("0")).toBeDefined());
|
||||
|
||||
fireEvent.click(screen.getByText("+1"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchSpy).toHaveBeenCalledWith(
|
||||
"http://localhost:8000/api/counter/increment",
|
||||
{ method: "POST" }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("clicking button updates displayed count", async () => {
|
||||
vi.spyOn(global, "fetch")
|
||||
.mockResolvedValueOnce({ json: () => Promise.resolve({ value: 0 }) } as Response)
|
||||
.mockResolvedValueOnce({ json: () => Promise.resolve({ value: 1 }) } as Response);
|
||||
|
||||
render(<Home />);
|
||||
await waitFor(() => expect(screen.getByText("0")).toBeDefined());
|
||||
|
||||
fireEvent.click(screen.getByText("+1"));
|
||||
await waitFor(() => expect(screen.getByText("1")).toBeDefined());
|
||||
});
|
||||
|
||||
|
|
@ -3,18 +3,37 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function Home() {
|
||||
const [message, setMessage] = useState("");
|
||||
const [count, setCount] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("http://localhost:8000/api/hello")
|
||||
fetch("http://localhost:8000/api/counter")
|
||||
.then((res) => res.json())
|
||||
.then((data) => setMessage(data.message));
|
||||
.then((data) => setCount(data.value));
|
||||
}, []);
|
||||
|
||||
const increment = async () => {
|
||||
const res = await fetch("http://localhost:8000/api/counter/increment", {
|
||||
method: "POST",
|
||||
});
|
||||
const data = await res.json();
|
||||
setCount(data.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<main style={{ padding: "2rem", fontFamily: "system-ui" }}>
|
||||
<h1>FastAPI + Next.js</h1>
|
||||
<p>{message || "Loading..."}</p>
|
||||
<main style={{ padding: "2rem", fontFamily: "system-ui", textAlign: "center" }}>
|
||||
<h1 style={{ fontSize: "6rem", margin: "2rem 0" }}>
|
||||
{count === null ? "..." : count}
|
||||
</h1>
|
||||
<button
|
||||
onClick={increment}
|
||||
style={{
|
||||
fontSize: "1.5rem",
|
||||
padding: "1rem 2rem",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
+1
|
||||
</button>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
29
frontend/e2e/counter.spec.ts
Normal file
29
frontend/e2e/counter.spec.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test("displays counter value", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page.locator("h1")).not.toHaveText("...");
|
||||
});
|
||||
|
||||
test("clicking +1 increments the counter", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page.locator("h1")).not.toHaveText("...");
|
||||
|
||||
const before = await page.locator("h1").textContent();
|
||||
await page.click("button");
|
||||
await expect(page.locator("h1")).toHaveText(String(Number(before) + 1));
|
||||
});
|
||||
|
||||
test("counter persists after reload", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page.locator("h1")).not.toHaveText("...");
|
||||
|
||||
const before = await page.locator("h1").textContent();
|
||||
await page.click("button");
|
||||
const expected = String(Number(before) + 1);
|
||||
await expect(page.locator("h1")).toHaveText(expected);
|
||||
|
||||
await page.reload();
|
||||
await expect(page.locator("h1")).toHaveText(expected);
|
||||
});
|
||||
|
||||
2650
frontend/package-lock.json
generated
2650
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,9 @@
|
|||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
"start": "next start",
|
||||
"test": "vitest run",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "15.1.2",
|
||||
|
|
@ -13,8 +15,13 @@
|
|||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.49.1",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@types/node": "25.0.3",
|
||||
"@types/react": "19.2.7",
|
||||
"typescript": "5.9.3"
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"jsdom": "^26.0.0",
|
||||
"typescript": "5.9.3",
|
||||
"vitest": "^2.1.8"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
frontend/playwright.config.ts
Normal file
14
frontend/playwright.config.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { defineConfig } from "@playwright/test";
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./e2e",
|
||||
webServer: {
|
||||
command: "npm run dev",
|
||||
url: "http://localhost:3000",
|
||||
reuseExistingServer: true,
|
||||
},
|
||||
use: {
|
||||
baseURL: "http://localhost:3000",
|
||||
},
|
||||
});
|
||||
|
||||
4
frontend/test-results/.last-run.json
Normal file
4
frontend/test-results/.last-run.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"status": "passed",
|
||||
"failedTests": []
|
||||
}
|
||||
11
frontend/vitest.config.ts
Normal file
11
frontend/vitest.config.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { defineConfig } from "vitest/config";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
environment: "jsdom",
|
||||
include: ["app/**/*.test.{ts,tsx}"],
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue