Refactor high-complexity React components in Dify frontend. Use when `pnpm analyze-component...
npx skills add bybren-llc/safe-agentic-workflow --skill "testing-patterns"
Install specific skill from multi-skill repository
# Description
Testing patterns for Jest and Playwright. Use when writing tests, setting up test fixtures, or validating RLS enforcement. Routes to existing test conventions and provides evidence templates.
# SKILL.md
name: testing-patterns
description: Testing patterns for Jest and Playwright. Use when writing tests, setting up test fixtures, or validating RLS enforcement. Routes to existing test conventions and provides evidence templates.
Testing Patterns Skill
Purpose
Guide consistent and effective testing. Routes to existing test patterns and provides evidence templates for Linear.
When This Skill Applies
Invoke this skill when:
- Writing new unit tests
- Creating integration tests
- Setting up test fixtures with RLS
- Running test suites
- Packaging test evidence for Linear
Critical Rules
β FORBIDDEN Patterns
// FORBIDDEN: Direct Prisma calls in tests (bypass RLS)
const user = await prisma.user.findUnique({ where: { user_id } });
// FORBIDDEN: Shared test state (causes flaky tests)
let sharedUser: User;
beforeAll(() => { sharedUser = createUser(); });
// FORBIDDEN: Hard-coded IDs (test pollution)
const userId = "user-123";
// FORBIDDEN: Missing cleanup (leaky tests)
it("creates user", async () => {
await prisma.user.create({ data: userData });
// No cleanup!
});
β CORRECT Patterns
// CORRECT: Use RLS context helpers
const user = await withSystemContext(prisma, "test", async (client) => {
return client.user.findUnique({ where: { user_id } });
});
// CORRECT: Isolated test state per test
beforeEach(() => {
const testUser = createTestUser();
});
// CORRECT: Unique identifiers
const userId = `user-${crypto.randomUUID()}`;
const email = `test-${Date.now()}@example.com`;
// CORRECT: Proper cleanup
afterEach(async () => {
await withSystemContext(prisma, "test", async (client) => {
await client.user.deleteMany({ where: { email: { contains: "test-" } } });
});
});
Test Directory Structure
__tests__/
βββ unit/ # Fast, isolated tests
β βββ components/ # React component tests
β βββ lib/ # Library function tests
β βββ services/ # Service layer tests
β βββ user/ # User helper tests
βββ integration/ # API and database tests
βββ database/ # Database helper tests
βββ e2e/ # End-to-end tests (Playwright)
βββ payments/ # Payment flow tests
βββ setup.ts # Global test setup
Configuration Files
- Jest Config:
jest.config.js - Test Setup:
__tests__/setup.ts - Playwright Config:
playwright.config.ts
RLS-Aware Testing
Setting Up Test Context
Always use RLS context helpers in tests:
import { withUserContext, withSystemContext } from "@/lib/rls-context";
import { prisma } from "@/lib/prisma";
describe("User payments", () => {
const testUserId = "test-user-123";
beforeEach(async () => {
// Create test user with RLS context
await withSystemContext(prisma, "test", async (client) => {
await client.user.create({
data: {
user_id: testUserId,
email: `test-${Date.now()}@example.com`,
first_name: "Test",
last_name: "User",
},
});
});
});
it("should only see own payments", async () => {
const payments = await withUserContext(
prisma,
testUserId,
async (client) => {
return client.payments.findMany();
},
);
// RLS ensures only this user's payments returned
expect(payments.every((p) => p.user_id === testUserId)).toBe(true);
});
});
Test Isolation
Use unique identifiers to prevent test pollution:
const uniqueEmail = `test-${Date.now()}@example.com`;
const uniqueUserId = `user-${crypto.randomUUID()}`;
Test Commands
# Run all unit tests
yarn test:unit
# Run integration tests
yarn test:integration
# Run specific test file
yarn jest __tests__/unit/components/my-component.test.tsx
# Run tests matching pattern
yarn jest --testNamePattern="should handle"
# Run with coverage
yarn test:unit --coverage
# Run E2E tests
yarn test:e2e
Common Patterns
Component Testing
import { render, screen, fireEvent } from "@testing-library/react";
import { MyComponent } from "@/components/my-component";
describe("MyComponent", () => {
it("renders correctly", () => {
render(<MyComponent />);
expect(screen.getByRole("button")).toBeInTheDocument();
});
it("handles click events", async () => {
const onClickMock = jest.fn();
render(<MyComponent onClick={onClickMock} />);
fireEvent.click(screen.getByRole("button"));
expect(onClickMock).toHaveBeenCalledTimes(1);
});
});
API Route Testing
import { GET } from "@/app/api/my-route/route";
import { NextRequest } from "next/server";
describe("GET /api/my-route", () => {
it("returns 200 with data", async () => {
const request = new NextRequest("http://localhost:3000/api/my-route");
const response = await GET(request);
expect(response.status).toBe(200);
const data = await response.json();
expect(data).toHaveProperty("success", true);
});
});
Mocking Prisma
jest.mock("@/lib/prisma", () => ({
prisma: {
user: {
findUnique: jest.fn(),
create: jest.fn(),
},
},
}));
Evidence Template for Linear
When completing test work, attach this evidence block:
**Test Execution Evidence**
**Test Suite**: [unit/integration/e2e]
**Files Changed**: [list files]
**Test Results:**
- Total Tests: [X]
- Passed: [X]
- Failed: [0]
- Skipped: [X]
**Coverage** (if applicable):
- Statements: X%
- Branches: X%
- Functions: X%
- Lines: X%
**Commands Run:**
\`\`\`bash
yarn test:unit --coverage
\`\`\`
**Output:**
[Paste relevant test output]
Pre-Push Validation
Always run before pushing:
yarn ci:validate
This runs:
- Type checking
- ESLint
- Unit tests
- Format check
Authoritative References
- Jest Config:
jest.config.js - Test Setup:
__tests__/setup.ts - RLS Context:
lib/rls-context.ts - CI Validation:
package.jsonscripts
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.