Tests (Vitest / React Testing Library)
We test the ZEUS frontend with Vitest (fast, compatible with Vite/ESM) and React Testing Library (RTL). The RTL philosophy: test what the user sees, not implementation details.
Vitest — testing logic
We test pure logic from lib/ like ordinary functions.
import { describe, it, expect } from "vitest";
import { formatForest } from "@/lib/format";
describe("formatForest", () => {
it("trims and lowercases", () => {
expect(formatForest(" CORP.LOCAL ")).toBe("corp.local");
});
});
ProfessNet standard: test files sit next to the code as
*.test.ts/*.test.tsx. Theit(...)name describes the behavior in Polish or English — just consistently within a project.
React Testing Library — testing components
We test behavior from the user's perspective: what is on the screen, what happens after a click. We pick selectors by role and text, not by CSS classes.
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { ProbeTable } from "@/app/(dashboard)/probes/probe-table";
const probes = [
{ id: "1", host: "dc01.corp", forest: "corp" },
{ id: "2", host: "dc02.dmz", forest: "dmz" },
];
it("filters probes after typing in the field", async () => {
render(<ProbeTable probes={probes} />);
await userEvent.type(screen.getByRole("textbox"), "dmz");
expect(screen.getByText("dc02.dmz")).toBeInTheDocument();
expect(screen.queryByText("dc01.corp")).not.toBeInTheDocument();
});
ProfessNet standard: the selector priority is
getByRole→getByLabelText→getByText. We usegetByTestIdas a last resort. We don't test a component's internal state or class names.
Mocking fetch and modules
We mock network requests so that tests are fast and deterministic.
import { vi, beforeEach, it, expect } from "vitest";
import { getProbe } from "@/lib/probes";
beforeEach(() => {
vi.restoreAllMocks();
});
it("returns the parsed probe object", async () => {
vi.spyOn(globalThis, "fetch").mockResolvedValue(
new Response(JSON.stringify({ id: "1", host: "dc01", forest: "corp" })),
);
const probe = await getProbe("1");
expect(probe.host).toBe("dc01");
});
Configuration
// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
setupFiles: ["./vitest.setup.ts"],
globals: true,
},
});
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest"
}
}
| Test type | Tool | What it checks |
|---|---|---|
lib/ logic | Vitest | function input → output |
| component | Vitest + RTL | render and interaction like a user's |
| hook | RTL renderHook | the returned state and effects |
Tip: if a test keeps breaking on a refactor even though the app works — you're probably testing the implementation, not the behavior. Rewrite it in the RTL style.
Vitest for logic, RTL for components, test behavior not internals. Such a test suite survives refactors and gives confidence with every PR.