/** * @fileoverview Tooltip Timing and Initialization Regression Tests * * These tests specifically target timing issues that could affect the tooltip component: * 1. Hover/focus timing (critical for tooltips) * 2. Show/hide timing * 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('Tooltip Timing and Initialization Regression Tests', () => { describe('Hover/Focus Timing (Critical for Tooltips)', () => { it('should show tooltip on FIRST hover', () => { cy.mount(` `); cy.wait(500); // Hover over trigger - tooltip should show on FIRST hover cy.get('.usa-tooltip__trigger').trigger('mouseover'); cy.wait(100); // Tooltip body should be visible cy.get('.usa-tooltip__body').should('have.class', 'is-set'); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); }); it('should show tooltip on FIRST focus', () => { cy.mount(` `); cy.wait(500); // Focus trigger - tooltip should show on FIRST focus cy.get('.usa-tooltip__trigger').focus(); cy.wait(100); // Tooltip body should be visible cy.get('.usa-tooltip__body').should('have.class', 'is-set'); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); }); it('should work immediately after component initialization', () => { cy.mount(` `); // Minimal wait - component should be ready quickly cy.wait(300); // Tooltip structure should exist cy.get('.usa-tooltip').should('exist'); cy.get('.usa-tooltip__trigger').should('exist'); cy.get('.usa-tooltip__body').should('exist'); }); }); describe('Tooltip Show/Hide Timing', () => { it('should hide tooltip on mouseleave', () => { cy.mount(` `); cy.wait(500); // Show tooltip cy.get('.usa-tooltip__trigger').trigger('mouseover'); cy.wait(100); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); // USWDS listens on wrapper's mouseleave, not trigger's mouseout cy.get('.usa-tooltip').trigger('mouseleave'); cy.wait(100); // Tooltip should be hidden cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'true'); }); it('should hide tooltip on blur', () => { cy.mount(` `); cy.wait(500); // Show tooltip via focus cy.get('.usa-tooltip__trigger').focus(); cy.wait(100); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); // Hide tooltip on blur cy.get('.usa-tooltip__trigger').blur(); cy.wait(100); // Tooltip should be hidden cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'true'); }); it('should toggle tooltip on repeated hover', () => { cy.mount(` `); cy.wait(500); // First hover - show cy.get('.usa-tooltip__trigger').trigger('mouseover'); cy.wait(100); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); // Mouseleave on wrapper - hide cy.get('.usa-tooltip').trigger('mouseleave'); cy.wait(100); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'true'); // Second hover - show again (no double-hover bug) cy.get('.usa-tooltip__trigger').trigger('mouseover'); cy.wait(100); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); }); }); describe('Tooltip Positioning', () => { it('should have data-position attribute set to top by default', () => { cy.mount(` `); cy.wait(500); // Trigger should have data-position="top" cy.get('.usa-tooltip__trigger').should('have.attr', 'data-position', 'top'); // Note: USWDS dynamically positions based on viewport space // Actual position class may differ from data-position in test environment }); it('should respect position attribute on trigger', () => { cy.mount(` `); cy.wait(500); // Trigger should have data-position="bottom" cy.get('.usa-tooltip__trigger').should('have.attr', 'data-position', 'bottom'); // Note: USWDS dynamically adjusts position based on available viewport space // Test environment may position differently than production }); }); describe('USWDS Initialization Timing', () => { it('should initialize USWDS after DOM is ready', () => { cy.mount(` `); // Wait for initialization cy.wait(500); // USWDS-created elements should exist and be functional cy.get('.usa-tooltip').should('exist'); cy.get('.usa-tooltip__trigger').should('exist'); cy.get('.usa-tooltip__body').should('exist'); // Trigger should have proper attributes cy.get('.usa-tooltip__trigger').should('have.attr', 'data-position'); }); it('should not duplicate event handlers on rapid property changes', () => { cy.mount(` `); const tooltip = cy.get('#handler-test'); cy.wait(500); // Verify trigger exists before property changes cy.get('.usa-tooltip__trigger').should('exist'); // Rapidly change properties (could trigger initialization multiple times) // Note: Changing 'text' or 'label' property, not 'title' which could cause re-render tooltip.then(($tooltip) => { const tooltipEl = $tooltip[0] as any; tooltipEl.text = 'Changed 1'; tooltipEl.text = 'Changed 2'; tooltipEl.text = 'Changed 3'; }); cy.wait(200); // Component should still work correctly (no duplicate handlers) cy.get('.usa-tooltip__trigger').trigger('mouseover'); cy.wait(100); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); }); }); describe('Multiple Tooltips', () => { it('should handle multiple tooltips independently', () => { cy.mount(`
`); cy.wait(500); // Show first tooltip cy.get('#tooltip-1 .usa-tooltip__trigger').trigger('mouseover'); cy.wait(100); // First should be visible, second should be hidden cy.get('#tooltip-1 .usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); cy.get('#tooltip-2 .usa-tooltip__body').should('have.attr', 'aria-hidden', 'true'); // Show second tooltip cy.get('#tooltip-2 .usa-tooltip__trigger').trigger('mouseover'); cy.wait(100); // Second should be visible cy.get('#tooltip-2 .usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); }); }); describe('Keyboard Interaction', () => { it('should hide tooltip on Escape key', () => { cy.mount(` `); cy.wait(500); // Show tooltip cy.get('.usa-tooltip__trigger').focus(); cy.wait(100); cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); // Press Escape cy.get('body').type('{esc}'); cy.wait(100); // Tooltip should be hidden cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'true'); }); }); describe('Accessibility Features', () => { it('should have correct ARIA attributes', () => { cy.mount(` `); cy.wait(500); // Trigger should have aria-describedby cy.get('.usa-tooltip__trigger').should('have.attr', 'aria-describedby'); // Body should have role="tooltip" cy.get('.usa-tooltip__body').should('have.attr', 'role', 'tooltip'); // Body should start as aria-hidden="true" cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'true'); }); it('should update ARIA attributes when shown', () => { cy.mount(` `); cy.wait(500); // Initial state cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'true'); // Show tooltip cy.get('.usa-tooltip__trigger').trigger('mouseover'); cy.wait(100); // Should update aria-hidden cy.get('.usa-tooltip__body').should('have.attr', 'aria-hidden', 'false'); }); }); });