Files
compiler-explorer/cypress/support/utils.ts
Matt Godbolt 50ec53d0e7 Add comprehensive Cypress E2E tests for Claude Explain feature (#7751)
- 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.
2025-08-05 16:42:48 -05:00

66 lines
2.3 KiB
TypeScript

import '../../static/global';
export function stubConsoleOutput(win: Cypress.AUTWindow) {
cy.stub(win.console, 'log').as('consoleLog');
cy.stub(win.console, 'warn').as('consoleWarn');
cy.stub(win.console, 'error').as('consoleError');
}
export function assertNoConsoleOutput() {
cy.get('@consoleLog').should('not.be.called');
cy.get('@consoleWarn').should('not.be.called');
cy.get('@consoleError').should('not.be.called');
}
/**
* Clear all network intercepts to prevent accumulation
*/
export function clearAllIntercepts() {
// Clear any existing intercepts by visiting a clean page and resetting
cy.window().then((win: any) => {
// Reset any cached state
win.compilerExplorerOptions = {};
});
}
/**
* Sets content in Monaco editor using a synthetic paste event
* @param content - The code content to set
* @param editorIndex - Which editor to target (default: 0 for first editor)
*/
export function setMonacoEditorContent(content: string, editorIndex = 0) {
// Wait for Monaco editor to be visible in DOM
cy.get('.monaco-editor').should('be.visible');
// Select all and delete existing content
cy.get('.monaco-editor textarea').eq(editorIndex).focus().type('{ctrl}a{del}', {force: true});
// Trigger a paste event with our content
cy.get('.monaco-editor textarea')
.eq(editorIndex)
.then(($element: any) => {
const el = $element[0];
// Create and dispatch a paste event with our data
const pasteEvent = new ClipboardEvent('paste', {
bubbles: true,
cancelable: true,
clipboardData: new DataTransfer(),
});
// Add our text to the clipboard data
pasteEvent.clipboardData?.setData('text/plain', content);
// Dispatch the event
el.dispatchEvent(pasteEvent);
});
// Wait for compilation to complete after content change (if compiler exists)
cy.get('body').then(($body: any) => {
if ($body.find('.compiler-wrapper').length > 0) {
cy.get('.compiler-wrapper').should('not.have.class', 'compiling');
}
// If no compiler wrapper exists yet, that's fine - compilation will happen when compiler is added
});
}