E2E Testing Guide for CLAIRE¶
under construction
This guide covers the End-to-End (E2E) testing system for the CLAIRE Electronic Lab Notebook, focusing on sustainable, CI-friendly testing practices.
Overview¶
The E2E testing system is designed with these principles:
Zero-setup for developers
CI-friendly for GitHub Actions
Schema-agnostic testing approach
Static fixtures checked into repository
NODE_ENV-based test attributes (no manual scripts)
Quick Start¶
# Run all E2E tests with proper config
make test-e2e
# Run only ReviewSubmitPanel tests
make test-e2e-reviewsubmit
# Run with visual UI for debugging
make test-e2e-ui
# Run in debug mode
make test-e2e-debug
Test Structure¶
frontend/tests/
├── e2e/ # E2E tests using Playwright
│ ├── reviewsubmit/ # ReviewSubmitPanel component tests
│ │ ├── display.spec.ts # Data display and rendering
│ │ ├── interaction.spec.ts # UI interactions
│ │ ├── validation.spec.ts # Form validation
│ │ └── edge-cases.spec.ts # Edge cases & performance
│ ├── helpers/ # Test utilities
│ │ ├── page-objects.ts # Page Object Model
│ │ └── test-utils.ts # Test data builders
│ └── test-component.html # Standalone test component
├── fixtures/ # Static test data (checked in)
│ ├── sops/ # SOP schema fixtures
│ │ ├── basic-sop.json # Standard SOP for most tests
│ │ ├── all-field-types-sop.json # All field types
│ │ └── minimal-sop.json # Minimal edge case
│ ├── eln-data/ # ELN form data fixtures
│ │ ├── complete-form.json # Fully filled form
│ │ ├── partial-form.json # Partial form (missing required)
│ │ └── empty-form.json # Empty form
│ └── test-scenarios/ # Test scenarios with metadata
│ ├── sopTest1-complete.json # Complete test scenario
│ └── sopTest1-partial.json # Partial test scenario
└── README.md
Key Features¶
Automatic Test Attributes¶
Test attributes are automatically enabled when NODE_ENV=test:
// In ReviewSubmitPanel.tsx
const testAttrs = process.env.NODE_ENV === 'test' ? {
'data-testid': 'review-submit-panel'
} : {};
return <div {...testAttrs} className="space-y-6">
Benefits:
✅ Zero-setup - Test runners automatically set NODE_ENV=test
✅ Production-safe - No test attributes in production builds
✅ No scripts - No need for complex attribute management
Static Test Fixtures¶
All test data is stored as JSON files checked into the repository:
// Automatic fixture loading
static async loadBasicSOP(): Promise<SOP> {
return basicSOP as SOP;
}
static async loadCompleteELNData(): Promise<ELNFormData> {
return completeELNData as ELNFormData;
}
Benefits:
✅ Consistent across environments
✅ Schema-compliant with SOPTemplateSchema
✅ Version controlled - changes tracked in git
✅ Fast - no API calls or complex setup
Standalone Test Component¶
For fast, isolated testing, we use a standalone HTML file (test-component.html) that mimics the ReviewSubmitPanel structure:
Static HTML with proper data-testid attributes
Interactive features (collapsible cards, etc.)
Representative test data
No dependencies on full application stack
Make Rules Integration¶
The Makefile provides convenient commands that handle environment setup:
test-e2e:
@$(MAKE) config ENV=test # Sets up test config files
@cd frontend && NODE_ENV=test npx playwright test
test-e2e-reviewsubmit:
@$(MAKE) config ENV=test
@cd frontend && NODE_ENV=test npx playwright test tests/e2e/reviewsubmit
test-e2e-integration:
@$(MAKE) config ENV=test
@$(MAKE) start-backend ENV=test & # Real backend integration
@cd frontend && NODE_ENV=test npx playwright test
Test Data Management¶
SOP Fixtures¶
SOP fixtures follow the actual SOPTemplateSchema structure:
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"id": "basic-sop-fixture",
"taskgroups": [
{
"id": "taskgroup_basic_info",
"_schemaType": "TaskGroup",
"children": [
{
"id": "task_patient_data",
"_schemaType": "Task",
"children": [
{
"id": "patient_id",
"_schemaType": "Field",
"type": "string",
"required": true
}
]
}
]
}
]
}
Note: _schemaType fields are being phased out in favor of a more standardized schema structure.
Key Properties:
type: For fields, identifies data type (string, integer, boolean, date, enum, array)children: Array of nested schema elements (indicates containers)parent: Array defining hierarchy relationshipsui_config: Rendering configuration (icons, variants)ordinal: Display order for elementsNo
_schemaType: Structure determined by presence oftype(fields) vschildren(containers) (_schemaType is going away)
ELN Data Fixtures¶
ELN fixtures represent completed form data:
{
"values": {
"patient_id": "PT-001",
"patient_age": 35,
"consent_given": true
},
"metadata": {
"sop_id": "basic-sop-fixture",
"completion_percentage": 100
}
}
Test Scenarios¶
Test scenarios combine SOPs, ELN data, and expected validation results:
{
"name": "Basic Valid Form",
"sop_id": "basic-sop-fixture",
"eln_data": {
"patient_id": "P001",
"additional_fields": "..."
},
"expected_validation": {
"isValid": true,
"missingRequiredFields": [],
"totalFields": 6
}
}
Page Object Model¶
The ReviewSubmitPageObject class abstracts UI interactions:
const reviewPanel = new ReviewSubmitPageObject(page);
// Navigation and setup
await reviewPanel.waitForLoaded();
// Field interactions
const value = await reviewPanel.getFieldValue('Patient ID');
const isRequired = await reviewPanel.isFieldRequired('Patient ID');
// Card interactions
await reviewPanel.toggleCard('Basic Information');
const isExpanded = await reviewPanel.isCardExpanded('Basic Information');
// Validation checks
const summary = await reviewPanel.getValidationSummary();
const missingFields = await reviewPanel.getMissingRequiredFields();
Development Workflow¶
Test-First Development¶
Write tests for new component behavior
Run tests to see them fail
Implement feature to make tests pass
Refactor with confidence that tests catch regressions
Schema-Agnostic Testing¶
Tests follow the same principles as the components:
// ✅ Good - Schema-driven
const isField = (element: any) => Boolean(element.type && FIELD_TYPES.includes(element.type));
const isContainer = (element: any) => Boolean(element.children && element.children.length > 0);
// ❌ Bad - Hardcoded assumptions
const isPatientField = (element: any) => element.id.includes('patient');
const usesSchemaType = (element: any) => element._schemaType === 'Field'; // _schemaType is deprecated
Guidelines:
Identify fields by
element.typeproperty (string, integer, boolean, etc.)Identify containers by
element.childrenarray presenceUse schema structure (
children,parent) for navigationTest validation based on
requiredandvalidationpropertiesNever use
_schemaType(deprecated SAM-only property)Never hardcode field names or specific counts
Adding New Tests¶
Create test file in appropriate subdirectory
Use page objects for UI interactions
Load static fixtures for consistent data
Test multiple scenarios (valid, invalid, edge cases)
Example:
test('renders enum fields as badges', async () => {
const sop = await TestDataBuilder.loadAllFieldTypesSOP();
const elnData = await TestDataBuilder.loadCompleteELNData();
// Use page object for interactions
const fieldValue = await reviewPanel.getFieldValue('Sample Type');
expect(fieldValue).toContain('blood');
});
CI/CD Integration¶
GitHub Actions¶
Tests run efficiently in CI with:
Fast startup using static fixtures
Parallel execution for speed
Proper isolation between test runs
Minimal resource usage
Environment Configuration¶
# In GitHub Actions workflow
- name: Run E2E Tests
run: |
make config ENV=test
cd frontend && NODE_ENV=test npx playwright test
The ENV=test ensures:
Test config files are deployed to
frontend/public/config.jsonBackend configs use test settings
API endpoints point to test environment
Debugging¶
Visual Debugging¶
# Open test in browser with visible actions
make test-e2e-headed
# Use Playwright Inspector for step-by-step debugging
make test-e2e-debug
Test Reports¶
# View detailed HTML report after test run
npx playwright show-report
Common Issues¶
Issue |
Solution |
|---|---|
Test attributes not found |
Ensure |
Fixture not found |
Check file paths in |
Component not rendering |
Verify SOP schema structure in fixtures |
Port mismatch |
Check |
Performance Monitoring¶
Targets¶
Form Load: <2 seconds
Field Interactions: <100ms
Memory Usage: <100MB for form data
Test Execution: <30 seconds for full suite
Monitoring¶
test('loads large forms within performance targets', async () => {
const startTime = performance.now();
await reviewPanel.waitForLoaded();
const loadTime = performance.now() - startTime;
expect(loadTime).toBeLessThan(2000); // 2 second target
});
Best Practices¶
Do ✅¶
Use static fixtures for consistent test data
Follow schema-agnostic principles
Test user workflows, not implementation details
Use page objects for complex interactions
Test validation behavior comprehensively
Don’t ❌¶
Hardcode field names or specific field counts
Rely on external services or APIs
Create overly complex test setup procedures
Test internal component state directly
Skip edge cases and error scenarios
Future Enhancements¶
Potential Additions¶
Visual regression testing with screenshot comparisons
Accessibility testing for WCAG compliance
Cross-browser testing with extended browser matrix
Performance monitoring with continuous tracking
Integration testing with real backend services
Test Data Expansion¶
Complex nested schemas for advanced hierarchy testing
Internationalization support with multi-language data
Real-world scenarios based on production usage patterns
Error simulation for network and API failure cases