- Add comprehensive test suite covering all Claude Explain functionality: - Basic pane opening and consent flow - no-ai directive detection - API interactions and error handling - Options/customization features - Caching behavior and persistence - Compilation state handling - State persistence across page loads - Fix caching bug in explain-view.ts: - Cache was incorrectly implemented as instance variable, losing cached explanations when panes were closed/reopened - Made cache static to persist across all pane instances (matches consent persistence pattern) - Fixes failing "Caching and reload" Cypress test - Aligns implementation with documented behavior: "shared across all explain views in the session" - Add test utilities and helpers: - Monaco editor content manipulation using clipboard events - Claude Explain specific helpers moved to test file - General utilities remain in utils.ts - Performance optimizations: - Clear Cypress intercepts in afterEach to prevent O(n²) degradation - Use :visible selectors to avoid GoldenLayout template elements - Proper mock setup timing to prevent race conditions - Add comprehensive README with lessons learned and best practices All tests use fake test data (test_first, focus_a, etc.) to clearly distinguish from production values and prevent accidental API calls.
5.3 KiB
Cypress E2E Tests
This directory contains end-to-end tests for Compiler Explorer using Cypress.
Running Tests
Starting Compiler Explorer for Testing
First, start a local Compiler Explorer instance with a clean configuration:
npm run dev -- --language c++ --no-local
The --no-local flag is important as it ensures your setup is clean of any local properties.
Running Cypress Tests
In another terminal:
# Run all Cypress tests
npm run cypress
# Run specific test file
npm run cypress -- run --spec "cypress/e2e/claude-explain.cy.ts"
# Open Cypress interactive UI (recommended for development)
npm run cypress:open
When using the interactive UI, choose "E2E Testing" and select your browser.
Important Testing Patterns & Lessons Learned
1. Always Use :visible Selectors
GoldenLayout creates template elements that exist in the DOM but aren't visible. Always use :visible to avoid selecting template elements:
// ❌ Bad - might select template elements
cy.get('.explain-content').should('contain', 'text');
// ✅ Good - only selects visible elements
cy.get('.explain-content:visible').should('contain', 'text');
2. Performance: Clear Intercepts in afterEach
Cypress intercepts accumulate across tests causing O(n²) performance degradation. Always clear them:
import {clearAllIntercepts} from '../support/utils';
afterEach(() => {
// Use the utility function to clear intercepts
clearAllIntercepts();
// Or manually clear them:
cy.state('routes', []);
cy.state('aliases', {});
// ... other cleanup
});
3. Mock Setup Timing is Critical
Always set up API mocks BEFORE any action that might trigger requests:
// ❌ Bad - pane constructor might make requests before mocks are ready
openClaudeExplainPane();
mockClaudeExplainAPI();
// ✅ Good - mocks ready before pane opens
mockClaudeExplainAPI();
openClaudeExplainPane();
4. Wait for Async DOM Updates
Don't just wait for API calls - wait for the actual DOM changes:
// ❌ Bad - API completes but DOM might not be updated yet
cy.wait('@getOptions');
cy.get('.dropdown').select('value');
// ✅ Good - wait for specific DOM state
cy.wait('@getOptions');
cy.get('.dropdown option[value="loading"]').should('not.exist');
cy.get('.dropdown').select('value');
5. Use Test Data, Not Production Values
Always use clearly fake test data to:
- Prevent confusion with real values
- Make it obvious when viewing test output
- Ensure tests never accidentally hit production APIs
// Use values like: test_first, test_second, focus_a, focus_b
// Not: beginner, expert, assembly, optimization
6. Helper Functions for Common Patterns
Extract common test patterns to helpers, but keep them in the test file if they're specific to one feature:
// In test file for feature-specific helpers
function openClaudeExplainPaneWithOptions() {
mockClaudeExplainAPI();
openClaudeExplainPane();
cy.wait('@getOptions');
waitForDropdownsToLoad();
}
// In utils.ts for general helpers
export function setMonacoEditorContent(content: string) { ... }
7. State Persistence Between Pane Instances
Be aware that some state might be static (shared between instances) while other state is per-instance:
- Static state (like consent, cache) persists when closing/reopening panes
- Instance state is lost when panes close
- This affects how you structure tests for features that should persist
8. Block Production APIs
Always block production API calls in tests to catch configuration issues:
cy.intercept('https://api.compiler-explorer.com/**', {
statusCode: 500,
body: {error: 'BLOCKED PRODUCTION API'}
}).as('blockedProduction');
Common Issues & Solutions
Tests Getting Progressively Slower
- Cause: Intercept accumulation
- Solution: Clear intercepts in
afterEachusingclearAllIntercepts()from utils or manually withcy.state('routes', [])
"Element not found" Despite Being Visible
- Cause: Selecting template elements from GoldenLayout
- Solution: Use
:visiblepseudo-selector
API Mocks Not Working
- Cause: Mock setup after the request is made
- Solution: Set up mocks before opening panes or triggering actions
Dropdown Selection Failing
- Cause: Trying to select before async population completes
- Solution: Wait for loading indicators to disappear first
State Not Persisting in Tests
- Cause: Not understanding static vs instance variables
- Solution: Check if the feature uses static state that should persist
Test Organization
- Keep feature-specific test helpers in the test file itself
- Only put truly reusable utilities in
support/utils.ts - Use descriptive helper function names that indicate what they do
- Group related tests in
describeblocks - Use consistent test data across all tests in a feature
Debugging Tips
- Use
cy.log()to debug what values you're actually getting - Check the Cypress command log for unexpected API calls
- Look for console errors that might indicate JavaScript issues
- Use
.then()to inspect element state at specific points - Check network tab for requests hitting production instead of mocks