/testing-patterns
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.
$ npx -y skills add bybren-llc/safe-agentic-workflow --skill testing-patterns --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
/testing-patterns
Context preview
The summary Claude sees to decide when to auto-load this skill.
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
testing-patterns.SKILL.mdname: 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.
allowed-tools: Read, Bash, Grep, Glob
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 C
Read more
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. allowed-tools: Read, Bash, Grep, Glob
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 C
SAW — SAFe Agentic Workflow AI Agent Harness for Multi-Agent Team Workflows Built on SAFe methodology (Scaled Agile Framework), adapted for AI agent teams (Now With AI-DLC!) Works for any team with repeatable processes: Software, Marketing, Research, Legal, Operations.
Other skills on safe-agentic-workflow.
- /agent-coordination
Agent assignment matrix, blocker escalation, and TDM coordination patterns. Use when assigning work to specialists, managing blockers, or coordinating multi-agent workflows.
Open skill - /api-patterns
API route implementation patterns with RLS, Zod validation, and error handling. Use when creating API routes, implementing endpoints, or adding server-side validation.
Open skill - /confluence-docs
Documentation templates for ADRs, runbooks, and architecture docs. Use when creating architectural decision records, operational runbooks, or technical documentation.
Open skill - /deployment-sop
Deployment workflows, pre-deploy validation, and smoke testing patterns. Use when deploying to staging or production, running smoke tests, or validating deployments.
Open skill - /frontend-patterns
Frontend patterns for Next.js App Router, Clerk auth, shadcn/Radix UI, and PostHog analytics. Use when building UI components, creating pages, implementing auth flows, or adding analytics events. Ensures consistent UX patterns and accessibility standards.
Open skill - /git-advanced
Advanced git operations including rebase, bisect, cherry-pick, and conflict resolution. Use when rebasing branches, debugging with bisect, cherry-picking commits, or resolving complex merge conflicts.
Open skill

