/**
* @fileoverview Table Timing and Initialization Regression Tests
*
* These tests specifically target timing issues that could affect the table component:
* 1. Sortable header click timing (critical for interactive tables)
* 2. USWDS initialization timing
* 3. Sort button creation timing
* 4. Multiple header interaction 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('Table Timing and Initialization Regression Tests', () => {
describe('Single-Click Requirement (USWDS Integration)', () => {
it('should sort table on FIRST click of sortable header', () => {
cy.mount(`
Test Table
| Name |
Age |
Email |
| John Doe |
30 |
john@example.com |
| Alice Smith |
25 |
alice@example.com |
| Bob Johnson |
35 |
bob@example.com |
`);
cy.wait(500);
// CRITICAL: First click should immediately sort
cy.get('th[data-sortable]').first().find('.usa-table__header__button').click();
cy.wait(100);
// Should be sorted (aria-sort attribute set)
cy.get('th[data-sortable]').first().should('have.attr', 'aria-sort');
});
it('should work immediately after component initialization', () => {
cy.mount(`
Immediate Test
| Product |
Price |
| Widget |
$10 |
| Gadget |
$20 |
`);
// Minimal wait - component should be ready quickly
cy.wait(300);
// USWDS should have created sort buttons
cy.get('.usa-table__header__button').should('exist');
// Click immediately after initialization
cy.get('.usa-table__header__button').first().click();
cy.wait(100);
// Should work on first try
cy.get('th[data-sortable]').first().should('have.attr', 'aria-sort');
});
it('should toggle sort correctly on each click (no skipped clicks)', () => {
cy.mount(`
Toggle Test
| Item |
| Apple |
| Banana |
`);
cy.wait(500);
const header = cy.get('th[data-sortable]').first();
const button = cy.get('.usa-table__header__button').first();
// Click 1: Should sort ascending
button.click();
cy.wait(100);
header.should('have.attr', 'aria-sort', 'ascending');
// Click 2: Should sort descending
button.click();
cy.wait(100);
header.should('have.attr', 'aria-sort', 'descending');
// Click 3: Should sort ascending again
button.click();
cy.wait(100);
header.should('have.attr', 'aria-sort', 'ascending');
});
});
describe('Table-Specific Timing Tests', () => {
it('should create sort buttons on initialization', () => {
cy.mount(`
Button Test
| Column A |
Column B |
Column C |
| Data |
Value |
Info |
`);
cy.wait(500);
// USWDS should create buttons for all sortable headers
cy.get('.usa-table__header__button').should('have.length', 3);
// Buttons should have proper attributes
cy.get('.usa-table__header__button').first().should('have.attr', 'tabindex', '0');
cy.get('.usa-table__header__button').first().should('have.attr', 'title');
});
it('should update sort labels immediately', () => {
cy.mount(`
Label Test
| Status |
| Active |
| Inactive |
`);
cy.wait(500);
// Initial state - unsorted
cy.get('th[data-sortable]').first().should('have.attr', 'aria-label').and('include', 'unsorted');
// Click to sort
cy.get('.usa-table__header__button').first().click();
cy.wait(100);
// Label should update immediately
cy.get('th[data-sortable]').first().should('have.attr', 'aria-label').and('include', 'sorted');
});
it('should handle multiple sortable headers independently', () => {
cy.mount(`
Multiple Test
| First |
Second |
Third |
| A |
1 |
X |
| B |
2 |
Y |
`);
cy.wait(500);
// Click first header
cy.get('.usa-table__header__button').eq(0).click();
cy.wait(100);
// First should be sorted, others unsorted
cy.get('th[data-sortable]').eq(0).should('have.attr', 'aria-sort');
cy.get('th[data-sortable]').eq(1).should('not.have.attr', 'aria-sort');
// Click second header
cy.get('.usa-table__header__button').eq(1).click();
cy.wait(100);
// Second should be sorted, first should be reset
cy.get('th[data-sortable]').eq(1).should('have.attr', 'aria-sort');
cy.get('th[data-sortable]').eq(0).should('not.have.attr', 'aria-sort');
});
it('should visually reorder rows on sort', () => {
cy.mount(`
Reorder Test
| Name |
| Zebra |
| Apple |
| Mango |
`);
cy.wait(500);
// Get initial first row
cy.get('tbody tr').first().find('th').should('contain', 'Zebra');
// Sort ascending (alphabetically)
cy.get('.usa-table__header__button').first().click();
cy.wait(200);
// First row should now be "Apple" (alphabetically first)
cy.get('tbody tr').first().find('th').should('contain', 'Apple');
// Sort descending
cy.get('.usa-table__header__button').first().click();
cy.wait(200);
// First row should now be "Zebra" (alphabetically last)
cy.get('tbody tr').first().find('th').should('contain', 'Zebra');
});
it('should handle numeric sorting', () => {
cy.mount(`
Numeric Test
| Name |
Value |
| Item A |
100 |
| Item B |
25 |
| Item C |
5 |
`);
cy.wait(500);
// Click numeric column header
cy.get('th[data-sortable]').find('.usa-table__header__button').click();
cy.wait(200);
// Should sort numerically: 5, 25, 100 (ascending)
cy.get('tbody tr').eq(0).find('td').should('contain', '5');
cy.get('tbody tr').eq(1).find('td').should('contain', '25');
cy.get('tbody tr').eq(2).find('td').should('contain', '100');
});
});
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-table__header__button').should('exist');
cy.get('.usa-table__announcement-region[aria-live="polite"]').should('exist');
// Sort button should work immediately
cy.get('.usa-table__header__button').click();
cy.wait(100);
cy.get('th[data-sortable]').should('have.attr', 'aria-sort');
});
it('should not duplicate event handlers on rapid property changes', () => {
cy.mount(`
`);
const table = cy.get('#handler-test');
cy.wait(500);
// Rapidly change properties (could trigger initialization multiple times)
table.then(($table) => {
const tableEl = $table[0] as any;
tableEl.striped = true;
tableEl.borderless = true;
tableEl.striped = false;
tableEl.borderless = false;
});
cy.wait(200);
// Component should still work correctly (no duplicate handlers)
cy.get('.usa-table__header__button').click();
cy.wait(100);
cy.get('th[data-sortable]').should('have.attr', 'aria-sort');
});
});
describe('Accessibility Features', () => {
it('should have correct ARIA attributes', () => {
cy.mount(`
`);
cy.wait(500);
// Header should have aria-label
cy.get('th[data-sortable]').should('have.attr', 'aria-label');
// Button should have title
cy.get('.usa-table__header__button').should('have.attr', 'title');
// Live region should exist
cy.get('.usa-table__announcement-region[aria-live="polite"]').should('exist');
});
it('should update ARIA attributes when sorted', () => {
cy.mount(`
ARIA Update
| Status |
| Active |
`);
cy.wait(500);
// Initial state - unsorted
cy.get('th[data-sortable]').should('not.have.attr', 'aria-sort');
// Sort
cy.get('.usa-table__header__button').click();
cy.wait(100);
// Should update aria-sort
cy.get('th[data-sortable]').should('have.attr', 'aria-sort', 'ascending');
// Sort again
cy.get('.usa-table__header__button').click();
cy.wait(100);
// Should update to descending
cy.get('th[data-sortable]').should('have.attr', 'aria-sort', 'descending');
});
it('should announce sort changes to screen readers', () => {
cy.mount(`
Announcement Test
| Priority |
| High |
`);
cy.wait(500);
// Live region should be empty initially
cy.get('.usa-table__announcement-region').should('have.text', '');
// Sort
cy.get('.usa-table__header__button').click();
cy.wait(200);
// Live region should contain announcement
cy.get('.usa-table__announcement-region')
.invoke('text')
.should('include', 'sorted')
.and('include', 'ascending');
});
});
describe('Keyboard Interaction', () => {
it('should be keyboard navigable', () => {
cy.mount(`
`);
cy.wait(500);
// Sort button should be focusable
cy.get('.usa-table__header__button').should('have.attr', 'tabindex', '0');
// Focus button
cy.get('.usa-table__header__button').focus();
// Should be focused
cy.get('.usa-table__header__button').should('have.focus');
// Press Enter to sort
cy.get('.usa-table__header__button').type('{enter}');
cy.wait(100);
// Should sort
cy.get('th[data-sortable]').should('have.attr', 'aria-sort');
});
});
describe('Data-Sort-Value Attribute', () => {
it('should respect data-sort-value for custom sort order', () => {
cy.mount(`
Sort Value Test
| Size |
| Large |
| Small |
| Medium |
`);
cy.wait(500);
// Sort ascending
cy.get('.usa-table__header__button').click();
cy.wait(200);
// Should sort by data-sort-value: 1 (Small), 2 (Medium), 3 (Large)
cy.get('tbody tr').eq(0).find('th').should('contain', 'Small');
cy.get('tbody tr').eq(1).find('th').should('contain', 'Medium');
cy.get('tbody tr').eq(2).find('th').should('contain', 'Large');
});
});
});