// Component tests for usa-icon import './index.ts'; import { testRapidClicking, testRapidKeyboardInteraction, COMMON_BUG_PATTERNS } from '../../cypress/support/rapid-interaction-tests.ts'; describe('Icon Component Tests', () => { beforeEach(() => { // Set up console error tracking cy.window().then((win) => { cy.stub(win.console, 'error').as('consoleError'); }); }); it('should render component with default properties', () => { cy.mount(''); cy.get('usa-icon').should('exist'); cy.get('usa-icon').should('be.visible'); }); it('should handle rapid clicking without visual glitches', () => { cy.mount(''); // Rapid clicking without waiting - simulates real user behavior cy.get('usa-icon').as('component'); // Multiple rapid clicks cy.get('@component') .click() .click() .click() .click() .click(); cy.wait(500); // Let events settle // Component should remain functional cy.get('@component').should('exist'); cy.get('@component').should('be.visible'); }); it('should handle interaction during CSS transitions', () => { cy.mount(''); // Click during potential transitions cy.get('usa-icon') .click() .click(); // Immediate second click cy.wait(1000); // Wait for animations // Should be in consistent state cy.get('usa-icon').should('exist'); }); // Stress tests using utility functions describe('Stress Testing', () => { it('should handle event listener duplication pattern', () => { cy.mount(''); // Test for event listener duplication testRapidClicking({ selector: 'usa-icon', clickCount: 15, description: 'event listener duplication' }); }); it('should handle race condition patterns', () => { cy.mount(''); // Test for race conditions during state changes cy.get('usa-icon').as('component'); // Rapid interactions that might cause race conditions cy.get('@component') .click() .click() .trigger('focus') .trigger('blur') .click(); cy.wait(1000); // Wait for all async operations // Component should still be functional cy.get('@component').should('exist'); cy.get('@component').should('be.visible'); }); }); // Accessibility testing - critical for government components it('should be accessible', () => { cy.mount(''); cy.injectAxe(); cy.checkAccessibility(); }); // Test that component maintains accessibility after interactions it('should maintain accessibility after rapid interactions', () => { cy.mount(''); // Perform various rapid interactions cy.get('usa-icon') .click() .focus() .blur() .click() .click(); cy.wait(500); // Accessibility should still be intact cy.injectAxe(); cy.checkAccessibility(); }); // Performance regression test it('should not cause memory leaks with rapid mounting/unmounting', () => { // This catches memory leaks and cleanup issues for (let i = 0; i < 5; i++) { cy.mount(''); cy.get('usa-icon').should('exist'); // Cypress automatically cleans up between mounts } }); // Console error test - should not generate any JavaScript errors it('should not generate console errors during interactions', () => { cy.mount(''); // Various interactions that might cause errors cy.get('usa-icon') .click() .trigger('mouseenter') .trigger('mouseleave') .focus() .blur(); cy.wait(500); // No console errors should have occurred cy.get('@consoleError').should('not.have.been.called'); }); // NEW (Oct 2025): Icon Visibility & Rendering Tests describe('Icon Visibility & Rendering', () => { it('should render icon with visible SVG element', () => { cy.mount(''); cy.get('usa-icon').should('exist'); cy.get('usa-icon svg').should('exist'); cy.get('usa-icon svg').should('have.class', 'usa-icon'); cy.get('usa-icon svg').should('have.class', 'usa-icon--size-5'); }); it('should render mail icon (not email)', () => { cy.mount(''); cy.get('usa-icon svg').should('exist'); cy.get('usa-icon svg use').should('have.attr', 'href').and('include', '#mail'); }); it('should render multiple icon sizes correctly', () => { const sizes = ['3', '4', '5', '6', '7', '8', '9']; sizes.forEach(size => { cy.mount(``); cy.get('usa-icon svg').should('have.class', `usa-icon--size-${size}`); }); }); it('should render common USWDS icons without errors', () => { const commonIcons = ['mail', 'phone', 'search', 'menu', 'close', 'info', 'warning', 'help']; commonIcons.forEach(iconName => { cy.mount(``); cy.get('usa-icon').should('exist'); cy.get('usa-icon svg').should('exist'); cy.get('usa-icon svg use').should('have.attr', 'href').and('include', `#${iconName}`); }); }); it('should handle sprite URL correctly', () => { cy.mount(''); cy.get('usa-icon svg use').should('have.attr', 'href', '/img/sprite.svg#flag'); }); it('should render decorative icons correctly', () => { cy.mount(''); cy.get('usa-icon svg').should('have.attr', 'aria-hidden', 'true'); cy.get('usa-icon svg').should('not.have.attr', 'aria-label'); }); it('should render semantic icons with aria-label', () => { cy.mount(''); cy.get('usa-icon svg').should('have.attr', 'aria-label', 'Important information'); cy.get('usa-icon svg').should('have.attr', 'aria-hidden', 'false'); }); }); // NEW (Oct 2025): Icon Gallery Visual Tests describe('Icon Gallery Rendering', () => { it('should render icon gallery with multiple icons', () => { const iconGallery = `
`; cy.mount(iconGallery); cy.get('usa-icon').should('have.length', 6); cy.get('usa-icon svg').should('have.length', 6); cy.get('usa-icon svg').each($svg => { expect($svg).to.have.class('usa-icon'); }); }); it('should render all icon categories correctly', () => { const categories = { communication: ['mail', 'phone', 'chat'], navigation: ['arrow_forward', 'arrow_back', 'menu'], actions: ['search', 'edit', 'delete'], status: ['check_circle', 'error', 'warning'], file: ['file_download', 'file_upload', 'folder'], social: ['facebook', 'twitter', 'github'] }; Object.entries(categories).forEach(([category, icons]) => { const galleryHtml = `
${icons.map(icon => ``).join('')}
`; cy.mount(galleryHtml); cy.get('usa-icon').should('have.length', icons.length); cy.get('usa-icon svg').should('have.length', icons.length); }); }); it('should render 10+ icons without performance issues', () => { const sampleIcons = [ 'mail', 'phone', 'search', 'menu', 'close', 'info', 'warning', 'help', 'star', 'flag', 'home', 'settings', 'lock', 'person', 'calendar_today' ]; const galleryHtml = `
${sampleIcons.map(icon => ``).join('')}
`; const startTime = Date.now(); cy.mount(galleryHtml); const endTime = Date.now(); cy.get('usa-icon').should('have.length', sampleIcons.length); cy.get('usa-icon svg').should('have.length', sampleIcons.length); // Should render quickly (within 500ms) expect(endTime - startTime).to.be.lessThan(500); }); it('should maintain consistent sizing across all icons in gallery', () => { cy.mount(`
`); cy.get('usa-icon svg').each($svg => { expect($svg).to.have.class('usa-icon--size-5'); }); }); it('should render icons with proper accessibility in gallery', () => { cy.mount(`
`); cy.injectAxe(); cy.checkAccessibility(); }); }); // NEW (Oct 2025): Icon Naming Regression Tests describe('Icon Naming Convention Tests', () => { it('should use "mail" icon name (USWDS standard)', () => { cy.mount(''); cy.get('usa-icon svg use').should('have.attr', 'href').and('include', '#mail'); }); it('should not use deprecated "email" icon name', () => { // This documents that "email" is not a valid USWDS icon cy.mount(''); // Should render fallback icon (circle) cy.get('usa-icon svg path').should('have.attr', 'd') .and('include', 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10'); }); it('should verify all common icons use correct USWDS names', () => { const correctNames = { 'mail': '#mail', // NOT 'email' 'phone': '#phone', 'search': '#search', 'menu': '#menu', 'close': '#close' }; Object.entries(correctNames).forEach(([iconName, expectedHref]) => { cy.mount(``); cy.get('usa-icon svg use').should('have.attr', 'href').and('include', expectedHref); }); }); }); });