/** * @fileoverview Combo Box Timing and Initialization Regression Tests * * These tests specifically target timing issues that could affect the combo box component: * 1. Double-click requirement (requestAnimationFrame timing fix) * 2. Race condition in setupEventHandlers * 3. USWDS initialization timing * * These tests run in a real browser with actual USWDS JavaScript, catching issues * that unit tests in jsdom cannot detect. */ import './index.ts'; describe('Combo Box Timing and Initialization Regression Tests', () => { describe('Single-Click Requirement (USWDS Integration)', () => { it('should open dropdown on FIRST click of combo box input', () => { cy.mount(` `); cy.get('#single-click-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }, { value: '3', label: 'Option 3' }, ]; }); // Wait for component to initialize and USWDS to transform cy.wait(500); // CRITICAL: First click should immediately work cy.get('.usa-combo-box__input').click(); // Dropdown should be visible after FIRST click (not second) cy.get('.usa-combo-box__list').should('be.visible'); }); it('should work immediately after component initialization', () => { cy.mount(` `); cy.get('#immediate-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }, ]; }); // Minimal wait - component should be ready quickly cy.wait(300); // Click immediately after initialization cy.get('.usa-combo-box__input').click(); // Should work on first try cy.get('.usa-combo-box__list').should('be.visible'); }); it('should toggle correctly on each click (no skipped clicks)', () => { cy.mount(` `); cy.get('#toggle-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }, ]; }); cy.wait(500); // Click 1: Should open cy.get('.usa-combo-box__input').click(); cy.wait(100); cy.get('.usa-combo-box__list').should('be.visible'); // Click 2: Should close (click outside or press escape) cy.get('body').type('{esc}'); cy.wait(100); cy.get('.usa-combo-box__list').should('not.be.visible'); // Click 3: Should open again cy.get('.usa-combo-box__input').click(); cy.wait(100); cy.get('.usa-combo-box__list').should('be.visible'); }); }); describe('Combo Box-Specific Timing Tests', () => { it('should filter options on first keypress', () => { cy.mount(` `); cy.get('#filter-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { value: 'apricot', label: 'Apricot' }, ]; }); cy.wait(500); // Type to filter - should work on FIRST keypress cy.get('.usa-combo-box__input').type('ap'); cy.wait(200); // Should show filtered results immediately cy.get('.usa-combo-box__list').should('be.visible'); cy.get('.usa-combo-box__list-option').should('have.length', 2); // Apple and Apricot }); it('should select option on first click', () => { cy.mount(` `); cy.get('#select-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: 'opt1', label: 'Option 1' }, { value: 'opt2', label: 'Option 2' }, ]; }); cy.wait(500); // Open dropdown cy.get('.usa-combo-box__input').click(); cy.wait(100); cy.get('.usa-combo-box__list').should('be.visible'); // Click option - should select on FIRST click cy.get('.usa-combo-box__list-option').first().click(); cy.wait(100); // Input should have selected value cy.get('.usa-combo-box__input').should('have.value', 'Option 1'); cy.get('.usa-combo-box__list').should('not.be.visible'); }); it('should handle keyboard navigation immediately', () => { cy.mount(` `); cy.get('#keyboard-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }, { value: '3', label: 'Option 3' }, ]; }); cy.wait(500); // Open dropdown cy.get('.usa-combo-box__input').click(); cy.wait(100); // Use arrow keys - should work immediately cy.get('.usa-combo-box__input').type('{downarrow}'); cy.wait(50); // First option should be focused cy.get('.usa-combo-box__list-option--focused').should('exist'); }); it('should close dropdown on Escape key', () => { cy.mount(` `); cy.get('#escape-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: '1', label: 'Option 1' }, ]; }); cy.wait(500); // Open dropdown cy.get('.usa-combo-box__input').click(); cy.wait(100); cy.get('.usa-combo-box__list').should('be.visible'); // Press Escape - should close on FIRST press cy.get('.usa-combo-box__input').type('{esc}'); cy.wait(100); cy.get('.usa-combo-box__list').should('not.be.visible'); }); }); describe('USWDS Initialization Timing', () => { it('should initialize USWDS after DOM is ready', () => { cy.mount(` `); cy.get('#init-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: '1', label: 'Option 1' }, ]; }); // Wait for initialization cy.wait(500); // USWDS-created input should exist and be clickable cy.get('.usa-combo-box__input').should('exist'); cy.get('.usa-combo-box__input').should('be.visible'); // Click should work immediately cy.get('.usa-combo-box__input').click(); cy.get('.usa-combo-box__list').should('be.visible'); }); it('should not duplicate event handlers on rapid property changes', () => { cy.mount(` `); const combo = cy.get('#handler-test'); combo.then(($combo) => { const c = $combo[0] as any; c.options = [ { value: '1', label: 'Option 1' }, ]; }); cy.wait(500); // Rapidly change properties (could trigger initialization multiple times) combo.then(($combo) => { const c = $combo[0] as any; c.disabled = true; c.required = true; c.disabled = false; c.required = false; }); cy.wait(200); // Component should still work correctly (no duplicate handlers) cy.get('.usa-combo-box__input').click(); cy.wait(50); cy.get('.usa-combo-box__list').should('be.visible'); cy.get('body').type('{esc}'); cy.wait(50); cy.get('.usa-combo-box__list').should('not.be.visible'); }); }); describe('Disabled State', () => { it('should handle disabled state correctly', () => { cy.mount(` `); cy.get('#disabled-test').then(($combo) => { const combo = $combo[0] as any; combo.options = [ { value: '1', label: 'Option 1' }, ]; }); cy.wait(500); // Input should be disabled cy.get('.usa-combo-box__input').should('be.disabled'); // Should not open on click cy.get('.usa-combo-box__input').click({ force: true }); cy.wait(100); cy.get('.usa-combo-box__list').should('not.be.visible'); }); it('should toggle disabled state dynamically', () => { cy.mount(` `); const combo = cy.get('#toggle-disabled-test'); combo.then(($combo) => { const c = $combo[0] as any; c.options = [ { value: '1', label: 'Option 1' }, ]; }); cy.wait(500); // Initially enabled - should work cy.get('.usa-combo-box__input').click(); cy.wait(100); cy.get('.usa-combo-box__list').should('be.visible'); // Close dropdown cy.get('body').type('{esc}'); cy.wait(100); // Disable the combo box combo.then(($combo) => { const c = $combo[0] as any; c.disabled = true; }); cy.wait(100); // Should be disabled cy.get('.usa-combo-box__input').should('be.disabled'); }); }); describe('Dynamic Options', () => { it('should handle option updates immediately', () => { cy.mount(` `); const combo = cy.get('#dynamic-options-test'); combo.then(($combo) => { const c = $combo[0] as any; c.options = [ { value: '1', label: 'Option 1' }, ]; }); cy.wait(500); // Open and verify initial options cy.get('.usa-combo-box__input').click(); cy.wait(100); cy.get('.usa-combo-box__list-option').should('have.length', 1); // Close dropdown cy.get('body').type('{esc}'); cy.wait(100); // Update options combo.then(($combo) => { const c = $combo[0] as any; c.options = [ { value: '1', label: 'Option 1' }, { value: '2', label: 'Option 2' }, { value: '3', label: 'Option 3' }, ]; }); cy.wait(500); // Open and verify updated options cy.get('.usa-combo-box__input').click(); cy.wait(100); cy.get('.usa-combo-box__list-option').should('have.length', 3); }); }); });