// Component tests for usa-select import './index.ts'; describe('USA Select Component Tests', () => { const basicOptions = [ { value: '', label: 'Select an option' }, { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, { value: 'option3', label: 'Option 3' }, ]; const stateOptions = [ { value: '', label: 'Select a state' }, { value: 'AL', label: 'Alabama' }, { value: 'CA', label: 'California' }, { value: 'FL', label: 'Florida' }, { value: 'NY', label: 'New York' }, { value: 'TX', label: 'Texas' }, ]; it('should render select with default properties', () => { cy.mount(``); cy.get('usa-select').should('exist'); cy.get('usa-select select').should('have.class', 'usa-select'); }); it('should render options when provided', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = basicOptions; }); cy.get('select option').should('have.length', 4); cy.get('select option').first().should('contain.text', 'Select an option'); cy.get('select option').last().should('contain.text', 'Option 3'); }); it('should handle option selection', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; }); // Select California cy.get('select').select('CA'); cy.get('select').should('have.value', 'CA'); // Select Florida cy.get('select').select('FL'); cy.get('select').should('have.value', 'FL'); }); it('should emit change events', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; const changeSpy = cy.stub(); select.addEventListener('change', changeSpy); cy.get('select').select('NY'); cy.then(() => { expect(changeSpy).to.have.been.called; }); }); }); it('should handle disabled state', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = basicOptions; }); cy.get('select').should('be.disabled'); cy.get('select').should('have.class', 'usa-select--disabled'); }); it('should handle required state', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; }); cy.get('select').should('have.attr', 'required'); cy.get('select').should('have.attr', 'aria-required', 'true'); }); it('should handle error state', () => { cy.mount(` `); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; }); cy.get('select').should('have.class', 'usa-select--error'); cy.get('select').should('have.attr', 'aria-invalid', 'true'); cy.get('.usa-error-message').should('contain.text', 'Please select a valid state'); }); it('should handle success state', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; select.value = 'CA'; }); cy.get('select').should('have.class', 'usa-select--success'); }); it('should handle small size variant', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = basicOptions; }); cy.get('select').should('have.class', 'usa-select--small'); }); it('should handle large size variant', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = basicOptions; }); cy.get('select').should('have.class', 'usa-select--large'); }); it('should handle multiple selection', () => { const colorOptions = [ { value: '', label: 'Select colors' }, { value: 'red', label: 'Red' }, { value: 'blue', label: 'Blue' }, { value: 'green', label: 'Green' }, ]; cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = colorOptions; }); cy.get('select').should('have.attr', 'multiple'); // Select multiple options (Cypress handles this automatically for multi-select) cy.get('select').select(['red', 'blue']); cy.get('select').should('have.value', 'red'); // First selected value }); it('should handle option groups', () => { const groupedOptions = [ { value: '', label: 'Select a location' }, { optgroup: 'West Coast', options: [ { value: 'ca', label: 'California' }, { value: 'or', label: 'Oregon' }, ], }, { optgroup: 'East Coast', options: [ { value: 'ny', label: 'New York' }, { value: 'fl', label: 'Florida' }, ], }, ]; cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = groupedOptions; }); cy.get('optgroup').should('have.length', 2); cy.get('optgroup').first().should('have.attr', 'label', 'West Coast'); cy.get('optgroup').last().should('have.attr', 'label', 'East Coast'); }); it('should handle disabled options', () => { const planOptions = [ { value: '', label: 'Select a plan' }, { value: 'basic', label: 'Basic Plan' }, { value: 'premium', label: 'Premium Plan', disabled: true }, { value: 'enterprise', label: 'Enterprise Plan' }, ]; cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = planOptions; }); cy.get('option[value="premium"]').should('have.attr', 'disabled'); }); it('should be keyboard accessible', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; }); // Tab to select cy.get('select').focus(); cy.focused().should('have.attr', 'id', 'test-select'); // Arrow down to navigate options (browser behavior) cy.focused().type('{downarrow}'); // Space or Enter to open dropdown (browser behavior) cy.focused().type(' '); }); it('should handle search/type-ahead functionality', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; }); // Focus and type to find option starting with 'C' cy.get('select').focus(); cy.focused().type('c'); // Should select California cy.get('select').should('have.value', 'CA'); // Type 'f' to find Florida cy.focused().type('f'); cy.get('select').should('have.value', 'FL'); }); it('should handle form integration', () => { cy.mount(`
`); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; const form = win.document.getElementById('test-form') as HTMLFormElement; const submitSpy = cy.stub(); form.addEventListener('submit', (e) => { e.preventDefault(); const formData = new FormData(form); submitSpy(formData.get('user-state')); }); // Select an option and submit cy.get('select').select('TX'); cy.get('button[type="submit"]').click(); cy.then(() => { expect(submitSpy).to.have.been.calledWith('TX'); }); }); }); it('should handle custom option rendering', () => { const priorityOptions = [ { value: '', label: 'Select priority' }, { value: 'low', label: '🟢 Low Priority' }, { value: 'medium', label: '🟡 Medium Priority' }, { value: 'high', label: '🔴 High Priority' }, ]; cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = priorityOptions; }); cy.get('option[value="low"]').should('contain.text', '🟢 Low Priority'); cy.get('option[value="high"]').should('contain.text', '🔴 High Priority'); }); it('should handle aria attributes', () => { cy.mount(`
Choose the state where you currently live
`); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; }); cy.get('select') .should('have.attr', 'aria-labelledby', 'select-label') .should('have.attr', 'aria-describedby', 'select-hint'); }); it('should handle focus and blur events', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = basicOptions; const focusSpy = cy.stub(); const blurSpy = cy.stub(); select.addEventListener('focus', focusSpy); select.addEventListener('blur', blurSpy); cy.get('select').focus(); cy.get('select').blur(); cy.then(() => { expect(focusSpy).to.have.been.called; expect(blurSpy).to.have.been.called; }); }); }); it('should handle validation on blur', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = stateOptions; }); // Focus and blur without selecting (should trigger validation) cy.get('select').focus().blur(); cy.get('select').should('have.attr', 'aria-invalid', 'true'); // Select an option and blur (should clear validation) cy.get('select').select('CA').blur(); cy.get('select').should('not.have.attr', 'aria-invalid', 'true'); }); it('should be accessible', () => { cy.mount(`
Select your state of residence
`); cy.window().then((win) => { const select = win.document.getElementById('state-select') as any; select.options = stateOptions; }); cy.injectAxe(); cy.checkAccessibility(); }); it('should handle custom CSS classes', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = basicOptions; }); cy.get('usa-select').should('have.class', 'custom-select-class'); cy.get('select').should('have.class', 'usa-select'); }); it('should handle dynamic option updates', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; // Initial options select.options = basicOptions; cy.get('select option').should('have.length', 4); // Update options select.options = stateOptions; cy.get('select option').should('have.length', 6); }); }); it('should handle placeholder option correctly', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('test-select') as any; select.options = basicOptions.slice(1); // Remove empty option since placeholder handles it }); cy.get('select option').first().should('contain.text', 'Choose an option'); cy.get('select option').first().should('have.attr', 'value', ''); cy.get('select option').first().should('be.disabled'); }); // Responsive Layout Tests describe('Mobile Responsive Behavior', () => { beforeEach(() => { cy.viewport(375, 667); // iPhone SE }); it('should display properly on mobile with touch targets', () => { cy.mount(`
`); cy.window().then((win) => { const select = win.document.getElementById('mobile-select') as any; select.options = stateOptions; }); // Select should be at least 44px high for touch targets cy.get('select') .should('be.visible') .and(($select) => { const height = $select.outerHeight(); expect(height).to.be.at.least(44); }); }); it('should handle mobile form layout', () => { cy.mount(`
`); cy.window().then((win) => { const stateSelect = win.document.getElementById('mobile-state') as any; const citySelect = win.document.getElementById('mobile-city') as any; stateSelect.options = stateOptions; citySelect.options = [ { value: '', label: 'Select a city' }, { value: 'nyc', label: 'New York City' }, { value: 'la', label: 'Los Angeles' }, ]; }); // Form should stack vertically on mobile cy.get('.usa-form-group').should('have.length', 2); cy.get('.usa-form-group').each(($group) => { cy.wrap($group) .should('have.css', 'width') .and('match', /375px|100%/); }); }); it('should handle touch interactions properly', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('touch-select') as any; select.options = stateOptions; }); // Touch events should work on mobile cy.get('select').trigger('touchstart').trigger('touchend').should('be.focused'); }); it('should handle mobile error states', () => { cy.mount(`
`); cy.window().then((win) => { const select = win.document.getElementById('error-select') as any; select.options = stateOptions; }); cy.get('.usa-error-message').should('be.visible'); cy.get('select').should('have.class', 'usa-select--error'); }); it('should handle mobile success states', () => { cy.mount(`
`); cy.window().then((win) => { const select = win.document.getElementById('success-select') as any; select.options = stateOptions; select.value = 'CA'; }); cy.get('select').should('have.class', 'usa-select--success'); }); }); describe('Tablet Responsive Behavior', () => { beforeEach(() => { cy.viewport(768, 1024); // iPad }); it('should display form in two-column layout on tablet', () => { cy.mount(`
`); cy.window().then((win) => { const stateSelect = win.document.getElementById('tablet-state') as any; const citySelect = win.document.getElementById('tablet-city') as any; stateSelect.options = stateOptions; citySelect.options = [ { value: '', label: 'Select a city' }, { value: 'nyc', label: 'New York City' }, { value: 'la', label: 'Los Angeles' }, ]; }); // Check grid layout on tablet cy.get('.tablet\\:grid-col-6').should('have.length', 2); cy.get('.tablet\\:grid-col-6').each(($col) => { cy.wrap($col).should('have.css', 'width').and('not.equal', '768px'); }); }); it('should handle tablet touch and hover states', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('tablet-select') as any; select.options = stateOptions; }); // Should work with both touch and mouse cy.get('select').trigger('touchstart').trigger('touchend').should('be.focused'); cy.get('select').trigger('mouseover').should('have.focus'); }); it('should handle tablet large size variant', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('tablet-large') as any; select.options = stateOptions; }); cy.get('select').should('have.class', 'usa-select--large'); }); }); describe('Desktop Responsive Behavior', () => { beforeEach(() => { cy.viewport(1200, 800); // Desktop }); it('should display full desktop form layout', () => { cy.mount(`
`); cy.window().then((win) => { const stateSelect = win.document.getElementById('desktop-state') as any; const citySelect = win.document.getElementById('desktop-city') as any; const zipSelect = win.document.getElementById('desktop-zip') as any; stateSelect.options = stateOptions; citySelect.options = [ { value: '', label: 'Select a city' }, { value: 'nyc', label: 'New York City' }, { value: 'la', label: 'Los Angeles' }, ]; zipSelect.options = [ { value: '', label: 'Select ZIP' }, { value: '10001', label: '10001' }, { value: '90210', label: '90210' }, ]; }); // Check three-column layout on desktop cy.get('.desktop\\:grid-col-4').should('have.length', 3); }); it('should handle keyboard navigation efficiently on desktop', () => { cy.mount(`
`); cy.window().then((win) => { const selects = ['first-select', 'second-select', 'third-select']; selects.forEach((id) => { const select = win.document.getElementById(id) as any; select.options = stateOptions; }); }); // Tab through selects cy.get('select').first().focus(); cy.focused() .should('have.attr', 'id') .and('match', /first-select/); cy.focused().tab(); cy.focused() .should('have.attr', 'id') .and('match', /second-select/); cy.focused().tab(); cy.focused() .should('have.attr', 'id') .and('match', /third-select/); }); it('should handle desktop hover states', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('hover-select') as any; select.options = stateOptions; }); cy.get('select').trigger('mouseover').should('have.css', 'cursor', 'pointer'); }); it('should handle desktop focus indicators', () => { cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('focus-select') as any; select.options = stateOptions; }); cy.get('select') .focus() .should('have.focus') .should('have.css', 'outline-width') .and('not.equal', '0px'); }); }); describe('Large Desktop Responsive Behavior', () => { beforeEach(() => { cy.viewport(1440, 900); // Large Desktop }); it('should maintain proper spacing on large screens', () => { cy.mount(`
`); cy.window().then((win) => { const selects = ['large-state', 'large-city', 'large-zip', 'large-country']; selects.forEach((id) => { const select = win.document.getElementById(id) as any; select.options = stateOptions; }); }); // Should have proper four-column layout cy.get('.desktop\\:grid-col-3').should('have.length', 4); // Container should be properly centered cy.get('.grid-container').should('have.css', 'max-width'); }); }); describe('Responsive Edge Cases', () => { it('should handle viewport transitions smoothly', () => { cy.mount(`
`); cy.window().then((win) => { const select = win.document.getElementById('transition-select') as any; select.options = stateOptions; }); // Test mobile to tablet transition cy.viewport(375, 667); cy.get('usa-select').should('be.visible'); cy.viewport(768, 1024); cy.get('usa-select').should('be.visible'); cy.viewport(1200, 800); cy.get('usa-select').should('be.visible'); }); it('should handle long option text at different screen sizes', () => { const longOptions = [ { value: '', label: 'Select a very long option name that might wrap' }, { value: 'long1', label: 'This is a very long option name that might cause layout issues' }, { value: 'long2', label: 'Another extremely long option text that could break layouts' }, ]; cy.mount(``); cy.window().then((win) => { const select = win.document.getElementById('long-text-select') as any; select.options = longOptions; }); // Test at different viewports const viewports = [ [320, 568], // Small mobile [768, 1024], // Tablet [1200, 800], // Desktop ]; viewports.forEach(([width, height]) => { cy.viewport(width, height); cy.get('select').should('be.visible'); // Should not cause horizontal overflow cy.get('select').then(($select) => { expect($select[0].scrollWidth).to.be.at.most($select[0].clientWidth + 5); }); }); }); it('should handle dynamic content updates at different screen sizes', () => { cy.mount(``); const scenarios = [ { viewport: [375, 667], options: basicOptions }, { viewport: [768, 1024], options: stateOptions }, { viewport: [1200, 800], options: [...stateOptions, ...basicOptions] }, ]; scenarios.forEach(({ viewport, options }) => { cy.viewport(viewport[0], viewport[1]); cy.window().then((win) => { const select = win.document.getElementById('dynamic-select') as any; select.options = options; }); cy.get('select option').should('have.length', options.length); cy.get('select').should('be.visible'); }); }); it('should maintain accessibility at all screen sizes', () => { cy.mount(`
This is a hint for the select
`); cy.window().then((win) => { const select = win.document.getElementById('accessible-select') as any; select.options = stateOptions; }); const viewports = [ [375, 667], // Mobile [768, 1024], // Tablet [1200, 800], // Desktop ]; viewports.forEach(([width, height]) => { cy.viewport(width, height); cy.injectAxe(); cy.checkAccessibility(); // Check focus indicators work at all sizes cy.get('select') .focus() .should('have.focus') .should('have.css', 'outline-width') .and('not.equal', '0px'); }); }); }); });