Notio follows integration-first testing principles based on Rainer Hahnekamp's research:
- Integration Tests: 60-70% of test suite (PRIMARY)
- E2E Tests: 20-30% of test suite (SECONDARY)
- Unit Tests: 10-20% of test suite (MINIMAL - edge cases only)
Integration tests provide:
- ✅ High confidence in real user workflows
- ✅ Tests that survive refactoring
- ✅ Natural coverage of implementation details
- ✅ Better cost/benefit ratio
For practical examples and copy-paste templates, see: Constitutional Testing Quickstart Guide
This guide includes:
- Integration test templates for musical components
- E2E test patterns with Playwright
- Unit test examples for complex algorithms
- Accessibility testing with jest-axe and @axe-core/playwright
# Integration and unit tests (Jest)
yarn test # Run all Jest tests in watch mode
yarn test --coverage # Generate coverage report (100% required)
yarn test:a11y # Run accessibility integration tests only
# E2E tests (Playwright)
yarn test:e2e # Run E2E tests in all browsers
yarn test:e2e:headed # Run with visible browser windows
yarn test:e2e:debug # Run with Playwright Inspector
yarn test:e2e:chromium # Run in Chrome only
yarn test:e2e:firefox # Run in Firefox only
yarn test:e2e:webkit # Run in Safari onlynotio/
├── src/
│ ├── __integration__/ # Integration tests (60-70%)
│ │ ├── musical-components/ # ColorKey, MusicalStaff, etc.
│ │ ├── accessibility/ # WCAG 2.1 AA compliance tests
│ │ └── workflows/ # User journey tests
│ └── __test__/ # Unit tests (10-20%)
│ └── utils/ # Edge cases for complex algorithms
└── e2e/ # E2E tests (20-30%)
├── critical-paths/ # Core user workflows
└── performance/ # Latency and rendering benchmarks
Write integration tests for:
- ✅ React component behavior and interactions
- ✅ ColorKey + MusicalStaff integration
- ✅ MIDI input/output handling
- ✅ Accessibility (WCAG 2.1 AA with jest-axe)
- ✅ Most user workflows
Example: Testing ColorKey component with user interactions
import { render, screen } from '@testing-library/react';
import { axe } from 'jest-axe';
import ColorKey from '../ColorKey';
it('should allow selecting notes and be accessible', async () => {
const { container } = render(<ColorKey />);
// Test behavior
const cButton = screen.getByRole('button', { name: /C/i });
await userEvent.click(cButton);
expect(cButton).toHaveClass('selected');
// Test accessibility
const results = await axe(container);
expect(results).toHaveNoViolations();
});Write E2E tests for:
- ✅ Complete user journeys (end-to-end workflows)
- ✅ Cross-browser compatibility (Chrome, Firefox, Safari)
- ✅ Performance metrics (audio latency <100ms, rendering <200ms)
- ✅ Accessibility in real browsers (color contrast)
Example: Testing complete notation creation workflow
const { test, expect } = require('@playwright/test');
const AxeBuilder = require('@axe-core/playwright').default;
test('should create musical notation without accessibility violations', async ({ page }) => {
await page.goto('/');
// Complete workflow
await page.click('[aria-label="Select C"]');
await page.click('[aria-label="Add to staff"]');
// Verify result
const staff = page.locator('[role="region"][aria-label="Musical notation"]');
await expect(staff).toContainText('C');
// Verify accessibility
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});Write unit tests ONLY for:
- ✅ Complex algorithm edge cases
- ✅ Mathematical calculations (intervals, frequencies)
- ✅ Boundary conditions (0, max, min values)
- ✅ Error handling for exceptional inputs
Example: Testing musical interval calculations
describe('calculateInterval', () => {
it('should handle octave intervals', () => {
expect(calculateInterval('C4', 'C5')).toBe(12);
});
it('should throw error for invalid notes', () => {
expect(() => calculateInterval('invalid', 'C4'))
.toThrow('Invalid note format');
});
});Notio uses a multi-layered accessibility strategy:
# ESLint with jsx-a11y/strict rules
yarn lintimport { axe } from 'jest-axe';
it('should be accessible', async () => {
const { container } = render(<Component />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});const AxeBuilder = require('@axe-core/playwright').default;
test('should be accessible in real browser', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);
});Open browser console in development mode to see real-time accessibility audits.
Notio requires 100% code coverage (enforced in jest.config.js):
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
}Generate coverage reports:
yarn test --coverageView HTML report: open coverage/lcov-report/index.html
Verify your test suite meets constitutional requirements:
node scripts/validate-test-distribution.jsExpected output:
Integration Tests: X (60-70%)
E2E Tests: Y (20-30%)
Unit Tests: Z (10-20%)
✅ PASS: Test distribution meets constitutional requirements!
- Test user behavior, not implementation details
- Use semantic queries (
getByRole,getByLabelText) - Test accessibility in every integration/E2E test
- Mock external dependencies (Firebase, Tone.js)
- Keep E2E tests under 30 seconds each
- Test simple getters/setters
- Use test IDs or CSS class selectors
- Duplicate integration test coverage in unit tests
- Test obvious code without edge cases
- Write brittle tests that break on refactoring
Tests run automatically on:
- Every commit (pre-commit hook)
- Every pull request (GitHub Actions)
- Before deployment (Netlify build)
All tests must pass with 100% coverage before merging.
E2E tests validate constitutional performance requirements:
- ✅ Audio latency: <100ms (MIDI → Tone.js playback)
- ✅ Notation rendering: <200ms (VexFlow canvas rendering)
- ✅ Page load: <5 seconds (full application initialization)
- Check for unresolved promises
- Ensure
awaitis used with async operations - Verify mock timers are advanced properly
- Check browser versions in
playwright.config.js - Verify web server starts successfully (
webServer.timeout) - Ensure audio testing flags are set in
launchOptions
- Run
yarn test --coverageto see uncovered lines - Add integration tests for uncovered workflows
- Ensure mocks properly simulate edge cases
- Constitutional Quickstart Guide
- Constitution v2.0.0
- Feature Specification
- Rainer Hahnekamp's Integration Testing Principles
- Jest Documentation
- Playwright Documentation
- React Testing Library
- jest-axe
- @axe-core/playwright
Questions? See quickstart.md for practical examples.