원클릭으로
vitest
Vitest unit testing patterns with React Testing Library. Trigger: When writing unit tests for React components, hooks, or utilities.
메뉴
Vitest unit testing patterns with React Testing Library. Trigger: When writing unit tests for React components, hooks, or utilities.
SOC 직업 분류 기준
Keeps product-tour definitions aligned with the UI features they describe. Trigger: When modifying UI components that have associated tours, editing tour definition files, or renaming data-tour-id attributes.
Django REST Framework patterns. Trigger: When implementing generic DRF APIs (ViewSets, serializers, routers, permissions, filtersets). For Prowler API specifics (RLS/RBAC/Providers), also use prowler-api.
Reviews Django migration files for PostgreSQL best practices specific to Prowler. Trigger: When creating migrations, running makemigrations/pgmakemigrations, reviewing migration PRs, adding indexes or constraints to database tables, modifying existing migration files, or writing data backfill migrations. Always use this skill when you see AddIndex, CreateModel, AddConstraint, RunPython, bulk_create, bulk_update, or backfill operations in migration files.
Create and maintain GitHub Agentic Workflows (gh-aw) for Prowler. Trigger: When creating agentic workflows, modifying gh-aw frontmatter, configuring safe-outputs, setting up MCP servers in workflows, importing Copilot Custom Agents, or debugging gh-aw compilation.
Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.
Next.js 16 App Router patterns. Trigger: When working in Next.js App Router (app/), Server Components vs Client Components, Server Actions, Route Handlers, proxy.ts, caching/revalidation, Cache Components, and streaming/Suspense.
| name | vitest |
| description | Vitest unit testing patterns with React Testing Library. Trigger: When writing unit tests for React components, hooks, or utilities. |
| license | Apache-2.0 |
| metadata | {"author":"prowler-cloud","version":"1.0","scope":["root","ui"],"auto_invoke":["Writing Vitest tests","Writing React component tests","Writing unit tests for UI","Testing hooks or utilities"]} |
| allowed-tools | Read, Edit, Write, Glob, Grep, Bash, Task |
For E2E tests: Use
prowler-test-uiskill (Playwright). This skill covers unit/integration tests with Vitest + React Testing Library.
Use Given/When/Then (AAA) pattern with comments:
it("should update user name when form is submitted", async () => {
// Given - Arrange
const user = userEvent.setup();
const onSubmit = vi.fn();
render(<UserForm onSubmit={onSubmit} />);
// When - Act
await user.type(screen.getByLabelText(/name/i), "John");
await user.click(screen.getByRole("button", { name: /submit/i }));
// Then - Assert
expect(onSubmit).toHaveBeenCalledWith({ name: "John" });
});
describe("ComponentName", () => {
describe("when [condition]", () => {
it("should [expected behavior]", () => {});
});
});
Group by behavior, NOT by method.
| Priority | Query | Use Case |
|---|---|---|
| 1 | getByRole | Buttons, inputs, headings |
| 2 | getByLabelText | Form fields |
| 3 | getByPlaceholderText | Inputs without label |
| 4 | getByText | Static text |
| 5 | getByTestId | Last resort only |
// ✅ GOOD
screen.getByRole("button", { name: /submit/i });
screen.getByLabelText(/email/i);
// ❌ BAD
container.querySelector(".btn-primary");
// ✅ ALWAYS use userEvent
const user = userEvent.setup();
await user.click(button);
await user.type(input, "hello");
// ❌ NEVER use fireEvent for interactions
fireEvent.click(button);
// ✅ findBy for elements that appear async
const element = await screen.findByText(/loaded/i);
// ✅ waitFor for assertions
await waitFor(() => {
expect(screen.getByText(/success/i)).toBeInTheDocument();
});
// ✅ ONE assertion per waitFor
await waitFor(() => expect(mockFn).toHaveBeenCalled());
await waitFor(() => expect(screen.getByText(/done/i)).toBeVisible());
// ❌ NEVER multiple assertions in waitFor
await waitFor(() => {
expect(mockFn).toHaveBeenCalled();
expect(screen.getByText(/done/i)).toBeVisible(); // Slower failures
});
// Basic mock
const handleClick = vi.fn();
// Mock with return value
const fetchUser = vi.fn().mockResolvedValue({ name: "John" });
// Always clean up
afterEach(() => {
vi.restoreAllMocks();
});
| Method | When to Use |
|---|---|
vi.spyOn | Observe without replacing (PREFERRED) |
vi.mock | Replace entire module (use sparingly) |
// Presence
expect(element).toBeInTheDocument();
expect(element).toBeVisible();
// State
expect(button).toBeDisabled();
expect(input).toHaveValue("text");
expect(checkbox).toBeChecked();
// Content
expect(element).toHaveTextContent(/hello/i);
expect(element).toHaveAttribute("href", "/home");
// Functions
expect(fn).toHaveBeenCalledWith(arg1, arg2);
expect(fn).toHaveBeenCalledTimes(2);
// ❌ Internal state
expect(component.state.isLoading).toBe(true);
// ❌ Third-party libraries
expect(axios.get).toHaveBeenCalled();
// ❌ Static content (unless conditional)
expect(screen.getByText("Welcome")).toBeInTheDocument();
// ✅ User-visible behavior
expect(screen.getByRole("button")).toBeDisabled();
components/
├── Button/
│ ├── Button.tsx
│ ├── Button.test.tsx # Co-located
│ └── index.ts
pnpm test # Watch mode
pnpm test:run # Single run
pnpm test:coverage # With coverage
pnpm test Button # Filter by name