/vitest
Vitest unit testing patterns with React Testing Library. Trigger: When writing unit tests for React components, hooks, or utilities.
$ npx -y skills add prowler-cloud/prowler --skill vitest --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/vitest
Context preview
The summary Claude sees to decide when to auto-load this skill.
Vitest unit testing patterns with React Testing Library. Trigger: When writing unit tests for React components, hooks, or utilities.
SKILL.md
vitest.SKILL.mdname: 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-ui` skill (Playwright). > This skill covers **unit/integration tests** with Vitest + React Testing Library.
Test Structure (REQUIRED)
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 Block Organization
describe("ComponentName", () => {
describe("when [condition]", () => {
it("should [expected behavior]", () => {});
});
});**Group by behavior, NOT by method.**
---
Query Priority (REQUIRED)
| 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");---
userEvent over fireEvent (REQUIRED)
// ✅ ALWAYS use userEvent
const user = userEvent.setup();
await user.click(button);
await user.type(input, "hello");
// ❌ NEVER use fireEvent for interactions
fireEvent.click(button);
---
Async Testing Patterns
// ✅ 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
});---
Mocking
// Basic mock
const handleClick = vi.fn();
// Mock with return value
const fetchUser = vi.fn().mockResolvedValue({ name: "John" });
// Always clean up
afterEach(() => {
vi.restoreAllMocks();
});vi.spyOn vs vi.mock
| Method | When to Use | |--------|-------------| | `vi.spyOn` | Observe without replacing (PREFERRED) | | `vi.mock` | Replace entire module (use sparingly) |
---
Common Matchers
// 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);---
What NOT to Test
// ❌ 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();---
File Organization
components/
├── Button/
│ ├── Button.tsx
│ ├── Button.test.tsx # Co-located
│ └── index.ts
---
Commands
pnpm test # Watch mode
pnpm test:run # Single run
pnpm test:coverage # With coverage
pnpm test Button # Filter by name
Read more
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-ui` skill (Playwright). > This skill covers **unit/integration tests** with Vitest + React Testing Library.
Test Structure (REQUIRED)
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 Block Organization
describe("ComponentName", () => {
describe("when [condition]", () => {
it("should [expected behavior]", () => {});
});
});**Group by behavior, NOT by method.**
---
Query Priority (REQUIRED)
| 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");---
userEvent over fireEvent (REQUIRED)
// ✅ ALWAYS use userEvent const user = userEvent.setup(); await user.click(button); await user.type(input, "hello"); // ❌ NEVER use fireEvent for interactions fireEvent.click(button);
---
Async Testing Patterns
// ✅ 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
});---
Mocking
// Basic mock
const handleClick = vi.fn();
// Mock with return value
const fetchUser = vi.fn().mockResolvedValue({ name: "John" });
// Always clean up
afterEach(() => {
vi.restoreAllMocks();
});vi.spyOn vs vi.mock
| Method | When to Use | |--------|-------------| | `vi.spyOn` | Observe without replacing (PREFERRED) | | `vi.mock` | Replace entire module (use sparingly) |
---
Common Matchers
// 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);---
What NOT to Test
// ❌ 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();---
File Organization
components/ ├── Button/ │ ├── Button.tsx │ ├── Button.test.tsx # Co-located │ └── index.ts
---
Commands
pnpm test # Watch mode pnpm test:run # Single run pnpm test:coverage # With coverage pnpm test Button # Filter by name
Prowler is the world’s most widely used Open-Source Cloud Security Platform that automates security and compliance across any cloud environment.
Repo: prowler-cloud/prowler
Other skills on prowler.
- /framework-compliance-triage
Make a cloud account compliant with a security or industry framework using Prowler Cloud.
Open skill - /ai-sdk-5
Vercel AI SDK 5 patterns. Trigger: When building AI features with AI SDK v5 (chat, streaming, tools/function calling, UIMessage parts), including migration from v4.
Open skill - /django-drf
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.
Open skill - /django-migration-psql
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
Open skill - /gh-aw
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.
Open skill - /jsonapi
Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.
Open skill

