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. The it(...) 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 getByRolegetByLabelTextgetByText. We use getByTestId as 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 typeToolWhat it checks
lib/ logicVitestfunction input → output
componentVitest + RTLrender and interaction like a user's
hookRTL renderHookthe 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.