Awesome-omni-skill webapp-testing
Comprehensive testing toolkit for LucidData using Playwright (E2E) and Vitest (unit/integration). Use when verifying frontend functionality, testing user flows (auth, vault CRUD, consent management), running test suites, debugging test failures, capturing screenshots, checking browser console logs, or validating responsive behavior. Integrates with existing LucidData test infrastructure and follows TDD practices.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/webapp-testing-thyde" ~/.claude/skills/diegosouzapw-awesome-omni-skill-webapp-testing-a62dea && rm -rf "$T"
skills/development/webapp-testing-thyde/SKILL.mdWeb Application Testing
Comprehensive testing toolkit for the LucidData personal data bank MVP, integrating Playwright for end-to-end testing and Vitest for unit/integration testing.
Overview
This skill enables test-driven development (TDD) workflows for the LucidData Next.js application. It provides patterns for writing, running, and debugging tests across all layers: unit tests for utilities and services, integration tests for React components and API routes, and end-to-end tests for complete user workflows.
When to Use This Skill
Activate this skill when you need to:
- Run test suites: Execute unit, integration, or E2E tests
- Debug test failures: Investigate failing tests, review screenshots, check console logs
- Write new tests: Create test files following LucidData patterns
- Verify functionality: Test user flows like signup → vault creation → consent management
- Check test coverage: Generate and analyze coverage reports (80% target)
- Test responsive behavior: Validate UI across different viewports
- Validate accessibility: Ensure WCAG compliance through automated tests
LucidData Testing Philosophy
The project follows a test pyramid approach:
- Many unit tests: Fast, isolated tests for utilities, services, and pure functions
- Some integration tests: Tests for React components with hooks, API route handlers
- Few E2E tests: Critical user journeys (auth, core workflows)
Coverage targets: 80% for lines, functions, branches, and statements.
Test naming conventions:
- Unit/Integration:
or*.test.ts*.test.tsx - E2E:
*.spec.ts
Test Infrastructure
Vitest (Unit & Integration Testing)
Configuration: vitest.config.ts
Key settings:
- Environment:
(simulates browser DOM)jsdom - Setup file: test/setup.ts
- Global test functions:
,describe
,it
available everywhereexpect - Excludes E2E tests:
directory excluded__tests__/e2e/** - Coverage: v8 provider with 80% thresholds
- Timeouts: 10000ms for tests and hooks
Test utilities:
- Mocks: test/mocks/prisma.ts, test/mocks/supabase.ts
- Fixtures: test/fixtures/ (sample data factories)
- Helpers: test/utils/ and test/helpers/
Libraries:
: Component testing with queries and user events@testing-library/react
: Custom matchers (e.g.,@testing-library/jest-dom
)toBeInTheDocument()
: Simulate user interactions@testing-library/user-event
: Advanced mocking withvitest-mock-extendedDeepMockProxy
: Mock Service Worker for API request interceptionmsw
Playwright (E2E Testing)
Configuration: playwright.config.ts
Key settings:
- Test directory:
__tests__/e2e - Global setup: tests/e2e/global-setup.ts
- Global teardown: tests/e2e/global-teardown.ts
- Base URL:
http://localhost:3000 - Parallel execution: Enabled
- Browsers: Chromium, Firefox, WebKit, Mobile Chrome (Pixel 5), Mobile Safari (iPhone 12)
- Web server: Auto-starts Next.js dev server before tests
- Screenshots: On failure
- Videos: On first retry
- Traces: On first retry
Helpers:
- Auth helper: tests/e2e/helpers/auth.ts
,login()
,signup()
,logout()
,isAuthenticated()
,getUniqueEmail()clearSession()
- Data generators: tests/e2e/helpers/data-generators.ts
,generateVaultEntry()
,generateConsent()generateVaultEntries(count)
Common Testing Patterns
Unit Testing Pattern (Vitest)
import { describe, it, expect, vi } from 'vitest'; describe('UtilityFunction', () => { it('should perform expected operation', () => { const result = utilityFunction('input'); expect(result).toBe('expected output'); }); it('should handle edge cases', () => { const result = utilityFunction(''); expect(result).toBe('default value'); }); });
Example from LucidData (lib/crypto/tests/hashing.test.ts):
describe('hash', () => { it('should produce SHA-256 hex string', () => { const hashed = hash('test data'); expect(typeof hashed).toBe('string'); expect(hashed).toHaveLength(64); expect(hashed).toMatch(/^[a-f0-9]{64}$/); }); it('should be deterministic', () => { const hash1 = hash('data'); const hash2 = hash('data'); expect(hash1).toBe(hash2); }); });
Integration Testing Pattern (React Components)
import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { vi } from 'vitest'; vi.mock('@/lib/db/prisma', () => ({ prisma: prismaMock, })); describe('ComponentName', () => { it('should render correctly', () => { render(<ComponentName />); expect(screen.getByText('Expected Text')).toBeInTheDocument(); }); it('should handle user interaction', async () => { const user = userEvent.setup(); render(<ComponentName />); await user.click(screen.getByRole('button', { name: 'Submit' })); expect(mockFunction).toHaveBeenCalled(); }); });
Example from LucidData (repository tests):
import { prismaMock } from '@/test/mocks/prisma'; describe('VaultRepository', () => { beforeEach(() => { repository = new VaultRepository(); vi.clearAllMocks(); }); it('should return vault entries for a user', async () => { prismaMock.vaultData.findMany.mockResolvedValue(userEntries); const result = await repository.findByUserId(userId); expect(result).toEqual(userEntries); expect(prismaMock.vaultData.findMany).toHaveBeenCalledWith({ where: { userId }, orderBy: { createdAt: 'desc' }, }); }); });
E2E Testing Pattern (Playwright)
import { test, expect } from '@playwright/test'; test.describe('Feature Flow', () => { test.beforeEach(async ({ page }) => { await clearSession(page); }); test('should complete user workflow', async ({ page }) => { await page.goto('/feature-page'); // Fill form await page.fill('input[name="field"]', 'value'); await page.click('button[type="submit"]'); // Verify result await expect(page.locator('text=Success')).toBeVisible(); }); });
Example from LucidData (tests/e2e/auth/signup.spec.ts):
test.describe('Signup Flow', () => { test('should successfully create account', async ({ page }) => { await page.goto('/signup'); const email = getUniqueEmail(); await page.fill('input[name="email"]', email); await page.fill('input[name="password"]', TEST_USER.password); await page.click('button[type="submit"]'); await expect(page).toHaveURL('/dashboard', { timeout: 10000 }); await expect(page.locator(`text=${email}`)).toBeVisible(); }); });
Test Commands
Unit & Integration Tests (Vitest)
# Watch mode (re-runs on file changes) npm run test # Single run (for CI/CD) npm run test:run # With coverage report npm run test:coverage # UI mode (visual test runner) npm run test:ui # Run specific test file npx vitest run path/to/test.test.ts # Run tests matching pattern npx vitest run --grep "VaultRepository"
E2E Tests (Playwright)
# Headless mode (no browser UI) npm run test:e2e # Headed mode (see browser) npm run test:e2e:headed # UI mode (interactive) npm run test:e2e:ui # Debug mode (step through tests) npm run test:e2e:debug # Run specific test file npx playwright test __tests__/e2e/auth/signup.spec.ts # Run specific browser npx playwright test --project=chromium # Run specific test by name npx playwright test --grep "should successfully create account"
All Tests
# Run all test suites (unit, integration, E2E) npm run test:all
Debugging Test Failures
Step-by-Step Debugging Process
- Read test output: Identify the failing test and assertion error
- Check screenshots: E2E failures auto-save screenshots in
test-results/ - Review browser logs: Playwright captures
,console.log
messagesconsole.error - Use interactive debugging:
- Vitest: Add
to inspect valuesvi.debug() - Playwright: Add
for interactive debuggingawait page.pause()
- Vitest: Add
- Verify test fixtures/mocks: Ensure mock data matches expected structure
- Check environment variables: Verify
has correct configuration.env.test - Isolate the issue: Run only the failing test with
flag--grep
Common Issues & Solutions
Issue: "Element not found" in E2E test
- Cause: Selector changed or element not rendered yet
- Solution: Use
or increase timeoutawait expect(element).toBeVisible()
Issue: "React Query mutation not called" in integration test
- Cause: Mock setup timing issue
- Solution: Use
from @testing-library/reactawait waitFor()
Issue: "Prisma method not mocked"
- Cause: Missing mock setup for database call
- Solution: Add
in test setupprismaMock.model.method.mockResolvedValue()
Issue: Test timeout
- Cause: Async operation taking too long
- Solution: Increase timeout in test config or use
in specific test{ timeout: 15000 }
LucidData-Specific Test Scenarios
Authentication Tests
- Signup flow: Create account → Redirect to dashboard
- Login flow: Enter credentials → Successful login
- Logout flow: Click logout → Redirect to landing page
- Protected routes: Access dashboard without auth → Redirect to login
Location:
__tests__/e2e/auth/
Vault CRUD Tests
- Create vault entry: Fill form → Submit → Entry appears in list
- Read vault entry: Click entry → View dialog shows data
- Update vault entry: Edit form → Save → Changes reflected
- Delete vault entry: Click delete → Confirm → Entry removed
Location:
__tests__/e2e/vault/
Consent Management Tests
- Grant consent: Create consent → Set permissions → Save
- View consent: Open consent list → See active/revoked consents
- Revoke consent: Click revoke → Confirm → Status updated
- Filter consents: Apply filters → List updates
Location:
__tests__/e2e/consent/ (when implemented)
Audit Log Tests
- View audit log: Navigate to audit page → See log entries
- Filter by event type: Select filter → Entries update
- Verify hash chain: Check hash integrity across entries
Location:
__tests__/e2e/audit/ (when implemented)
Responsive Design Tests
Test UI across viewports:
- Mobile: 375px, 390px (iPhone SE, iPhone 12)
- Tablet: 768px, 1024px (iPad, iPad Pro)
- Desktop: 1920px, 2560px (HD, QHD)
test('should be responsive on mobile', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); await page.goto('/dashboard'); // Verify mobile layout });
Accessibility Tests
- Keyboard navigation: Tab through forms, activate buttons with Enter
- Screen reader labels: Check ARIA attributes, alt text
- Color contrast: Automated checks with axe-core (future integration)
Test Data Management
Using Fixtures
Import pre-built data from
test/fixtures/:
import { mockVaultEntry, createMockVaultEntries } from '@/test/fixtures/vault-data'; // Use single entry const entry = mockVaultEntry; // Generate multiple entries const entries = createMockVaultEntries(5, 'user-id-123');
E2E Data Generators
For E2E tests, use helper functions to create unique test data:
import { generateVaultEntry } from '@/__tests__/e2e/helpers/data-generators'; const entry = generateVaultEntry({ label: 'Test Entry', category: 'Identity', });
Cleanup After E2E Tests
Global teardown (tests/e2e/global-teardown.ts) handles cleanup. For manual cleanup:
test.afterEach(async ({ page }) => { // Delete test data created during test await clearTestData(page); });
Writing New Tests
1. Decide Test Type
- Unit test: Testing a single function/utility in isolation
- Integration test: Testing components with hooks, API routes with database calls
- E2E test: Testing complete user workflows across multiple pages
2. Choose Location
- Unit:
lib/**/__tests__/utility.test.ts - Integration:
orcomponents/**/__tests__/component.test.tsxlib/**/__tests__/service.test.ts - E2E:
__tests__/e2e/feature/flow.spec.ts
3. Follow Existing Patterns
Look at similar existing tests for structure, mock setup, and assertion patterns.
4. Use Descriptive Test Names
// Good it('should encrypt data with AES-256-GCM') it('should redirect to login when not authenticated') // Bad it('works') it('test encryption')
5. Arrange-Act-Assert (AAA) Pattern
it('should create vault entry', async () => { // Arrange: Set up test data and mocks const input = { label: 'Test', category: 'Identity' }; prismaMock.vaultData.create.mockResolvedValue(mockEntry); // Act: Execute the function under test const result = await vaultService.create(input); // Assert: Verify the outcome expect(result).toEqual(mockEntry); expect(prismaMock.vaultData.create).toHaveBeenCalledWith({ data: expect.objectContaining(input), }); });
Best Practices
- Isolate tests: Each test should be independent, not relying on other tests' state
- Use realistic data: Fixtures should resemble production data (but never use real PII)
- Mock external dependencies: Mock Prisma, Supabase, API calls for unit/integration tests
- Test error paths: Don't just test happy paths; verify error handling
- Keep tests fast: Unit tests should run in milliseconds, integration tests in seconds
- Avoid test pollution: Clean up mocks with
invi.clearAllMocks()beforeEach() - Use semantic queries: Prefer
,getByRole
overgetByLabelTextgetByTestId - Wait for async updates: Use
orawait waitFor()
for async operationsawait expect().toBeVisible()
Coverage Analysis
Generate coverage report:
npm run test:coverage
Coverage report location:
coverage/index.html (open in browser)
Interpreting coverage:
- Green: Above 80% threshold
- Yellow: 50-80% coverage
- Red: Below 50% coverage
Focus on:
- Lines: Percentage of code lines executed
- Functions: Percentage of functions called
- Branches: Percentage of if/else paths tested
- Statements: Percentage of statements executed
Note: 100% coverage doesn't guarantee bug-free code. Focus on testing critical paths and edge cases.
Environment Configuration
Test environment uses
.env.test file:
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 SUPABASE_SERVICE_ROLE_KEY=<test-key> DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres ENCRYPTION_KEY=eugsb9sWIyEOcEg5AzHazv0k7CMTjSsGfL1lbLp8duU= NEXT_PUBLIC_APP_URL=http://localhost:3000 NODE_ENV=test
Important: Never commit real keys or sensitive data to version control.
References
For more detailed information, see:
- Playwright Patterns - Page objects, selectors, authentication helpers
- Vitest Patterns - Component testing, mocking strategies, async patterns
- Test Data - Fixture structure, sample data, test credentials
Unified Test Runner Script
For convenience, use the run-tests.sh script:
# Run unit tests ./scripts/run-tests.sh unit # Run E2E tests ./scripts/run-tests.sh e2e # Run all tests ./scripts/run-tests.sh all # With coverage ./scripts/run-tests.sh unit --coverage
Quick Reference
| Task | Command | Notes |
|---|---|---|
| Run unit tests (watch) | | Re-runs on file changes |
| Run unit tests (once) | | For CI/CD |
| Coverage report | | 80% threshold |
| Vitest UI | | Visual test runner |
| Run E2E tests | | Headless mode |
| E2E headed mode | | See browser |
| E2E UI mode | | Interactive debugging |
| E2E debug mode | | Step through tests |
| Run all tests | | Unit + E2E |
| Specific test file | | Single file |
| Specific E2E test | | Single E2E file |
Version: 1.0 Last Updated: 2026-01-13 Maintained by: LucidData Team