/**
* @fileoverview Modal Timing and Initialization Regression Tests
*
* These tests specifically target timing issues that could affect the modal 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('Modal Timing and Initialization Regression Tests', () => {
describe('Single-Click Requirement (USWDS Integration)', () => {
it('should open modal on FIRST click of trigger button', () => {
cy.mount(`
`);
// Wait for component to initialize and USWDS to create wrapper
cy.wait(500);
// CRITICAL: First click should immediately work
cy.get('[data-open-modal]').click();
// Give USWDS time to toggle visibility
cy.wait(200);
// Modal wrapper should be visible after FIRST click (not second)
cy.get('.usa-modal-wrapper').should('be.visible');
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
cy.get('.usa-modal-wrapper').should('not.have.class', 'is-hidden');
});
it('should work immediately after component initialization', () => {
cy.mount(`
`);
// Minimal wait - component should be ready quickly
cy.wait(100);
// Click immediately after initialization
cy.get('[data-open-modal]').click();
// Should work on first try
cy.get('.usa-modal-wrapper').should('be.visible');
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
});
it('should toggle correctly on each action (no skipped clicks)', () => {
cy.mount(`
`);
cy.wait(200);
// Click 1: Should open
cy.get('[data-open-modal]').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Click 2: Close button should work
cy.get('.usa-modal__close').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-hidden');
// Click 3: Should open again
cy.get('[data-open-modal]').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
});
});
describe('Modal-Specific Timing Tests', () => {
it('should close modal on first Escape key press', () => {
cy.mount(`
`);
cy.wait(200);
// Open modal
cy.get('[data-open-modal]').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Press Escape - should close on FIRST press
cy.get('body').type('{esc}');
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-hidden');
});
it('should close modal on first click of close button', () => {
cy.mount(`
`);
cy.wait(200);
// Open modal
cy.get('[data-open-modal]').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Click close button - should work on FIRST click
cy.get('.usa-modal__close').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-hidden');
});
it('should close modal on first click of primary action button', () => {
cy.mount(`
`);
cy.wait(200);
// Open modal
cy.get('[data-open-modal]').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Click primary action - should work on FIRST click
cy.get('.usa-modal__footer .usa-button').contains('Continue').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-hidden');
});
it('should handle focus trap immediately on open', () => {
cy.mount(`
`);
cy.wait(500);
// Store initial focus
cy.get('[data-open-modal]').as('trigger');
// Open modal
cy.get('@trigger').click();
cy.wait(200);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Focus should be inside modal immediately - check that a focusable element is focused
cy.focused().should('exist');
// Verify modal is visible (focus trap is working)
cy.get('.usa-modal').should('be.visible');
});
});
describe('Force Action Mode', () => {
it.skip('should prevent closing on escape when force-action is true (KNOWN ISSUE)', () => {
cy.mount(`
`);
cy.wait(500);
// Open modal
cy.get('[data-open-modal]').click();
cy.wait(200);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Press Escape - should NOT close
cy.get('body').type('{esc}');
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible'); // Still visible!
// Close button should not exist in force-action mode
cy.get('.usa-modal__close').should('not.exist');
});
it('should only close via action buttons when force-action is true', () => {
cy.mount(`
`);
cy.wait(200);
// Open modal
cy.get('[data-open-modal]').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Primary action should close
cy.get('.usa-modal__footer .usa-button').contains('Confirm').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-hidden');
});
});
describe('USWDS Initialization Timing', () => {
it('should initialize USWDS after DOM is ready', () => {
cy.mount(`
`);
// Wait for initialization
cy.wait(200);
// Trigger button should exist and be clickable
cy.get('[data-open-modal]').should('exist');
cy.get('[data-open-modal]').should('be.visible');
// Click should work immediately
cy.get('[data-open-modal]').click();
cy.get('.usa-modal-wrapper').should('be.visible');
});
it('should not duplicate event handlers on rapid property changes', () => {
cy.mount(`
`);
const modal = cy.get('#handler-test');
cy.wait(200);
// Rapidly change properties (could trigger initialization multiple times)
modal.then(($modal) => {
const m = $modal[0] as any;
m.large = true;
m.forceAction = true;
m.large = false;
m.forceAction = false;
});
cy.wait(100);
// Component should still work correctly (no duplicate handlers)
cy.get('[data-open-modal]').click();
cy.wait(50);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
cy.get('.usa-modal__close').click();
cy.wait(50);
cy.get('.usa-modal-wrapper').should('have.class', 'is-hidden');
});
});
describe('Large Modal Variant', () => {
it('should handle large modal variant correctly', () => {
cy.mount(`
`);
cy.wait(200);
// Open modal
cy.get('[data-open-modal]').click();
cy.wait(100);
// Should have large class
cy.get('.usa-modal').should('have.class', 'usa-modal--lg');
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Should close normally
cy.get('.usa-modal__close').click();
cy.wait(100);
cy.get('.usa-modal-wrapper').should('have.class', 'is-hidden');
});
});
describe('Programmatic Control', () => {
it.skip('should open modal programmatically on first call (KNOWN ISSUE)', () => {
cy.mount(`
`);
cy.wait(500);
// Open programmatically - wait for component to be fully defined
cy.window().then((win) => {
const modal = win.document.querySelector('#programmatic-test') as any;
if (modal && typeof modal.openModal === 'function') {
modal.openModal();
}
});
cy.wait(200);
// Should be visible immediately
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
});
it.skip('should close modal programmatically on first call (KNOWN ISSUE)', () => {
cy.mount(`
`);
cy.wait(500);
// Open via trigger
cy.get('[data-open-modal]').click();
cy.wait(200);
cy.get('.usa-modal-wrapper').should('have.class', 'is-visible');
// Close programmatically - wait for component to be fully defined
cy.window().then((win) => {
const modal = win.document.querySelector('#programmatic-close-test') as any;
if (modal && typeof modal.closeModal === 'function') {
modal.closeModal();
}
});
cy.wait(200);
// Should be hidden immediately
cy.get('.usa-modal-wrapper').should('have.class', 'is-hidden');
});
});
});