Test frontend
AI operating system for product managers. 65 Claude Code skills, 7 multi-perspective review agents, a memory system. Battle-tested in real PM work.
npx -y skills add talgacapri/pm-os --skill test-frontendAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Write, review, and audit frontend tests for a React + Vitest + Playwright codebase. Applies ISTQB CTFL v4.0 standards (black-box techniques, test pyramid, risk-based prioritization, defect taxonomy) combined with E2E-first philosophy and AAA structure. Triggers on "write tests", "test this component", "test coverage", "Playwright", "Vitest", "RTL", "E2E for", or any request to write or review React/frontend tests.
SKILL.md
12.9 KB, as published. Nobody here has run it
Frontend Testing Skill
Stack: React · Vitest · React Testing Library · Playwright Standard: ISTQB CTFL v4.0.1 (ISO/IEC/IEEE 29119 aligned)
ISTQB Foundation: The Seven Testing Principles
Apply these every time. They are not optional ceremony — they shape every decision below.
- Testing shows presence, not absence of defects. Tests reduce risk; they don't prove correctness.
- Exhaustive testing is impossible. Use risk-based prioritization (see §Risk) to focus effort.
- Early testing saves time and money. Write tests before or alongside code, not after.
- Defects cluster together. When one bug appears in a component, look for siblings. Retest the cluster.
- Tests wear out. Rotate and extend tests each sprint. The same suite repeated blindly misses new defects.
- Testing is context dependent. Alpha-stage product? Risk tolerance is different from a financial product in production.
- Absence-of-defects fallacy. Passing tests don't mean users are happy. Validate behavior, not just spec conformance.
Test Levels (ISTQB) Mapped to This Stack
| ISTQB Level | Our Implementation | Tooling |
|---|---|---|
| Component (Unit) | Pure functions, utilities, custom hooks | Vitest |
| Component Integration | Components with children, context, stores | Vitest + RTL |
| System | Full user flows end-to-end | Playwright |
| Acceptance | UAT scenarios / acceptance criteria from user stories | Playwright (ATDD) |
The Test Pyramid: Where to Spend Effort
/ E2E (Playwright) \ ← few, slow, high confidence
/ Integration Tests \ ← moderate, medium speed
/ Unit Tests (Vitest) \ ← many, fast, isolated
/__________________________\
Rules:
- Unit tests: pure functions and custom hooks ONLY. No React components.
- Integration tests: components with real children, real context, minimal mocking (≤2 mocks per test).
- E2E tests: full user flows. If you need 3+ mocks to write a unit or integration test, write E2E instead.
Test Types (ISTQB §2.2.2)
| Type | Frontend Application |
|---|---|
| Functional | Does the component do what the user story says? |
| Non-functional | Accessibility (axe-core), performance (Core Web Vitals), usability |
| Black-box | Test via user-visible behavior — roles, labels, text — never internal state |
| White-box | Branch coverage in business logic utilities (Vitest coverage report) |
| Regression | Every bug fix gets a new test. Run full suite in CI on every PR. |
| Confirmation | After a fix: re-run the failing test and verify it passes. |
Testing Techniques (ISTQB §4)
Black-Box Techniques (apply to all component tests)
Equivalence Partitioning (EP) Group inputs that the component treats identically. Test one value per partition.
// Button disabled prop — three partitions: true, false, undefined (defaults to false)
it('should be clickable when enabled', ...) // valid partition
it('should be inert when disabled', ...) // invalid partition
it('should default to enabled when prop absent', ...) // default partition
Boundary Value Analysis (BVA) Apply to numeric inputs, string lengths, list sizes. Use 3-value BVA: boundary - 1, boundary, boundary + 1.
// Pagination showing max 10 items
it('renders 9 items', ...) // below boundary
it('renders 10 items', ...) // at boundary
it('renders 11 items — truncates or paginates', ...) // above boundary
Decision Table Testing Use when a component has multiple conditions producing different outcomes (e.g. form validation).
| isLoggedIn | hasPermission | isExpired | Result |
|------------|---------------|-----------|---------------|
| true | true | false | Show content |
| true | true | true | Show refresh |
| true | false | - | Show 403 |
| false | - | - | Redirect login|
Write one test per column.
State Transition Testing For components with finite states (modals, stepper flows, auth states):
// All States coverage minimum: test every state is reachable
// Valid Transitions coverage preferred: test every allowed transition
// e.g. idle → loading → success / error → idle
White-Box Techniques (utilities and hooks)
Statement Coverage: Every line of a utility executes under test. Target: 100%.
Branch Coverage: Every conditional branch (if/else, ternary, switch) is exercised. Target: ≥95%.
Run pnpm test:coverage to verify. Branch coverage subsumes statement coverage.
Experience-Based Techniques
Error Guessing: Before writing a test, list: what would a developer likely get wrong here? Null inputs, empty arrays, missing locale, timezone edge cases, race conditions on async.
Exploratory Testing: After writing scripted tests, spend 10 minutes clicking through the component freely. Log anomalies as new test cases.
Checklist-Based Testing: For each PR touching UI, run through:
- Renders without crash
- Keyboard navigable
- Screen reader roles correct
- Loading state shown
- Error state shown
- Empty state shown
- Responsive at 375px and 1280px
Core Philosophy
E2E tests are the default for user-facing behavior. Unit tests are for pure functions only. Component unit tests are an anti-pattern — they test the wrong thing (implementation) and break on every refactor.
Test behavior, not implementation.
Never instance.setState(), never component.props, never internal variable names.
Minimal mocking. Real code > fake code. If you need to mock more than 2 things, that's a signal to write E2E instead.
Selector priority (from strongest to weakest semantic signal):
getByRole > getByLabelText > getByPlaceholderText > getByText > getByTestId > CSS selectors (never).
File Structure
src/
components/
MyComponent/
index.tsx
index.test.tsx ← co-located unit/integration test (Vitest)
utils/
format.ts
format.test.ts ← unit test for pure function
e2e/
tests/
checkout.spec.ts ← E2E user flow (Playwright)
auth.spec.ts
utils/
test-helpers.ts ← shared E2E setup helpers
Test Structure: AAA Pattern (ISTQB)
Every test follows Arrange → Act → Assert. No exceptions.
it('should disable submit when form is invalid', () => {
// Arrange
render(<ContactForm />)
const submitButton = screen.getByRole('button', { name: /submit/i })
// Act
await userEvent.type(screen.getByLabelText(/email/i), 'not-an-email')
await userEvent.tab() // trigger blur/validation
// Assert
expect(submitButton).toBeDisabled()
})
Test Naming
Format: should [expected outcome] when [condition]
it('should show error message when email format is invalid')
it('should call onSubmit when form fields are valid')
it('should disable input when isReadOnly is true')
it('should render empty state when no items are provided')
Required Test Scenarios (Minimum)
Every component test file must include:
| Scenario | Always Required |
|---|---|
| Renders without crashing | ✅ |
| Required props render correctly | ✅ |
| Optional props with defaults | ✅ |
| null / undefined / empty inputs | ✅ |
| Boundary values (if numeric or list input) | ✅ |
Conditional (include when the feature is present):
| Feature | Add these tests |
|---|---|
onClick / onSubmit | Triggers once, passes correct args |
| Async / API call | Loading state, success state, error state |
useEffect | Runs on mount, runs on dep change, cleanup |
| Routing | Navigation happens, params passed |
| Form | Validates, submits, shows field errors |
| Pagination | At boundary, below, above |
Incremental Workflow
When testing a directory or feature, never generate all tests at once.
For each file (ordered: utilities → hooks → simple components → complex → integration):
1. Write test
2. Run: pnpm test path/to/file.test.tsx
3. PASS → mark done, proceed to next
4. FAIL → fix first, do not continue until green
Complexity > 50 lines of logic? Extract to a hook or utility, then test that in isolation first.
Coverage Targets
| Metric | Target |
|---|---|
| Statement coverage | 100% |
| Branch coverage | ≥95% |
| Function coverage | 100% |
| Line coverage | ≥95% |
Run: pnpm test:coverage and review the HTML report in coverage/. Fix uncovered branches before marking a story done.
E2E Test Structure (Playwright)
// e2e/tests/checkout.spec.ts
import { test, expect } from '@playwright/test'
import { createTestAccount, addFunds } from '../utils/test-helpers'
test.describe('Checkout', () => {
test.beforeEach(async ({ page, context }) => {
await createTestAccount(page, { status: 'active' })
const cookies = await context.cookies()
const accountId = cookies.find(c => c.name === 'account_id')?.value
await addFunds({ accountId, amount: 10000 })
})
test('user can complete checkout with default payment method', async ({ page }) => {
// Arrange
await page.goto('/catalog')
// Act
await page.getByRole('heading', { name: 'Product Name' }).click()
await page.getByRole('button', { name: /buy now/i }).click()
await page.getByRole('button', { name: /confirm/i }).click()
// Assert
await expect(page.getByRole('heading', { name: /order confirmed/i })).toBeVisible()
})
})
E2E Selector Rules:
getByRole— always first choicegetByLabel— for form inputsgetByText— for static contentgetByTestId— last resort when no accessible selector exists- CSS selectors — never
Risk-Based Test Prioritization (ISTQB §5.1.5 + §5.2)
Before writing tests, classify each area by:
- Risk likelihood (how often does this area change?)
- Risk impact (what fails if this is broken? payments > UI polish)
Execute in this order:
- High likelihood + High impact → full coverage, E2E + integration
- Low likelihood + High impact → integration + boundary tests
- High likelihood + Low impact → smoke tests
- Low likelihood + Low impact → minimal, if time allows
Example for fintech: payment flows, auth, KYC steps = highest risk. Animations, empty states = lowest risk. Tune this for your domain.
Defect Reporting (ISTQB §5.5)
When a test fails and you're logging a bug, include:
Title: [Component] [Behavior] when [condition]
Severity: Critical / Major / Minor / Cosmetic
Steps to Reproduce:
1. Navigate to /page
2. Click X
3. Enter Y
Expected: Z
Actual: W
Test that catches it: [link to spec file + test name]
Root Cause (if known): Error / Defect / Environmental
The distinction matters (ISTQB §1.2.3):
- Error = developer mistake (e.g., wrong logic)
- Defect = the bug in the code (e.g., off-by-one)
- Failure = what the user sees (e.g., wrong total shown)
Static Testing Checklist (ISTQB §3) — PR Review
Before merging any frontend PR, confirm:
- No unused variables or imports (ESLint clean)
- No hardcoded strings that should be i18n keys
- Accessibility attributes present (aria-label, role)
- No
console.logleft in production code - Component props typed (no
any) - Error boundaries in place for async data sections
Key Commands
# Run all unit/integration tests
pnpm test
# Watch mode during development
pnpm test:watch
# Run a specific test file
pnpm test src/components/Button/index.test.tsx
# Coverage report
pnpm test:coverage
# Run E2E tests
pnpm playwright test
# Run specific E2E spec
pnpm playwright test e2e/tests/checkout.spec.ts
# Run E2E in headed mode (debugging)
pnpm playwright test --headed
Collaboration-Based Testing (ISTQB §4.5)
When working with designers and engineers on user stories:
ATDD approach: Before any code is written, define acceptance criteria in Given/When/Then format. These become your Playwright tests.
Given a logged-in user with sufficient balance
When they tap "Send Money" and complete the flow
Then the transaction appears in history with correct amount and status
INVEST check for stories before testing:
- Independent: test can run without other test state
- Negotiable: acceptance criteria written collaboratively
- Valuable: tests a real user need, not an implementation detail
- Estimable: you can estimate testing effort
- Small: one user story = one focused test scenario
- Testable: acceptance criteria exist and are unambiguous