dcyfr-testing
Testing patterns, 99% pass rate target, strategic skips, and E2E workflows
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
What I do
I ensure DCYFR testing follows quality standards:
- 99% test pass rate target (not 100%)
- Strategic skips for valid reasons (not failures)
- When/when-not-to-test decision making
- E2E testing with Playwright
- Test data protection with environment checks
When to use me
✅ Use this skill when:
- Writing new tests for components or API routes
- Deciding whether to skip tests strategically
- Setting up E2E test suites
- Fixing failing tests
- Reviewing test coverage
❌ Don't use this skill for:
- Production deployments (tests run in CI)
- Manual testing workflows
- Performance benchmarking (use separate tools)
Core Rules
1. 99% Pass Rate Target (Not 100%)
Why not 100%?
- Strategic skips are acceptable for valid reasons
- Flaky tests should be skipped temporarily (with GitHub issues)
- Environment-specific tests (e.g., requires Redis) can be skipped
Current status: 1659/1717 tests passing (96.6%)
2. Strategic Skips vs Failures
// ✅ CORRECT: Strategic skip with reason
test.skip('renders with Redis data', () => {
// Skipped: Requires Redis connection (issue #123)
});
// ❌ WRONG: Failing test left as-is
test('renders with Redis data', () => {
expect(fetchData()).resolves.toBe(data); // Fails in CI
});
3. When to Write Tests
✅ Always test:
- Public API routes (validation, error handling)
- Utility functions (pure functions, data transformations)
- Critical user flows (auth, payments, form submissions)
- Bug fixes (regression tests)
⚠️ Sometimes test:
- UI components (if complex logic or many props)
- Integration points (database, external APIs)
- Edge cases (boundary conditions)
❌ Don't test:
- Simple pass-through components (
<div>{children}</div>) - Third-party library internals
- Implementation details (use public APIs)
Test Commands
# Run tests once (no watch) - RECOMMENDED for AI agents
npm run test:run
# Run specific test file
npm run test:run src/components/blog/post-card.test.tsx
# Run E2E tests
npm run test:e2e
# Run E2E in headed mode (see browser)
npm run test:e2e:headed
# Full quality check (type + lint + test)
npm run check
⚠️ For AI Agents: Always use npm run test:run instead of npm test to avoid watch mode hanging.
Common Test Patterns
Component Test
import { render, screen } from '@testing-library/react';
import { PostCard } from './post-card';
describe('PostCard', () => {
it('renders post title and excerpt', () => {
render(
<PostCard
title="Test Post"
excerpt="Test excerpt"
date="2024-01-01"
slug="test-post"
/>
);
expect(screen.getByText('Test Post')).toBeInTheDocument();
expect(screen.getByText('Test excerpt')).toBeInTheDocument();
});
});
API Route Test
import { POST } from './route';
import { NextRequest } from 'next/server';
describe('/api/submit', () => {
it('validates required fields', async () => {
const request = new NextRequest('http://localhost:3000/api/submit', {
method: 'POST',
body: JSON.stringify({}),
});
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(400);
expect(data.error).toBe('Missing required fields');
});
});
Utility Function Test
import { formatDate } from './date-utils';
describe('formatDate', () => {
it('formats date as YYYY-MM-DD', () => {
expect(formatDate(new Date('2024-01-15'))).toBe('2024-01-15');
});
it('handles invalid dates', () => {
expect(formatDate(null)).toBe('');
});
});
E2E Testing with Playwright
Basic E2E Test
import { test, expect } from '@playwright/test';
test('navigates to blog and filters by category', async ({ page }) => {
await page.goto('/blog');
await expect(page.getByRole('heading', { name: 'Blog' })).toBeVisible();
await page.getByRole('button', { name: 'Web Development' }).click();
await expect(page.getByText('Showing posts in: Web Development')).toBeVisible();
});
Authenticated E2E Test
test.use({ storageState: 'playwright/.auth/user.json' });
test('creates new post as admin', async ({ page }) => {
await page.goto('/admin/posts/new');
await page.fill('[name="title"]', 'New Post');
await page.fill('[name="content"]', 'Post content');
await page.click('[type="submit"]');
await expect(page.getByText('Post created')).toBeVisible();
});
Test Data Protection
All test data MUST have environment checks:
// ✅ CORRECT: Environment-protected test data
if (process.env.NODE_ENV !== 'production' && process.env.VERCEL_ENV !== 'production') {
await redis.set('test:user:123', { name: 'Test User' });
}
// ❌ WRONG: No environment checks
await redis.set('test:user:123', { name: 'Test User' });
Strategic Skip Reasons
Valid reasons to skip tests:
- Environment-specific - Requires Redis, Postgres, etc.
- Flaky test - Intermittent failures (create GitHub issue)
- Component refactor planned - Will be rewritten soon
- CI timing issues - E2E tests timeout in CI only
- External dependency - Requires third-party API
Related Documentation
-
Full testing patterns:
.github/agents/patterns/TESTING_PATTERNS.md -
Test data prevention:
.github/agents/enforcement/TEST_DATA_PREVENTION.md -
Automated testing guide:
docs/testing/automated-testing-guide.md
Approval Gates
Testing compliance is FLEXIBLE (warning only):
- ⚠️ Target: ≥99% test pass rate
- ⚠️ Strategic skips allowed (document reason)
- ✅ Must maintain ≥95% pass rate minimum
- ✅ All tests must run (no permanent skips without issues)
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-01-26
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!