// Component tests for usa-header import './index.ts'; describe('USA Header Component Tests', () => { it('should render header with default properties', () => { cy.mount(``); cy.get('usa-header').should('exist'); cy.get('.usa-header').should('exist'); cy.get('.usa-navbar').should('exist'); cy.get('.usa-nav').should('exist'); }); it('should render logo with text', () => { cy.mount(` `); cy.get('.usa-logo__text a').should('contain.text', 'Government Site'); cy.get('.usa-logo__text a').should('have.attr', 'href', '/'); }); it('should render logo with image', () => { cy.mount(` `); cy.get('img') .should('have.attr', 'src', '/img/logo.png') .should('have.attr', 'alt', 'Site logo'); }); it('should handle custom logo href', () => { cy.mount(` `); cy.get('.usa-logo__text a').should('have.attr', 'href', '/custom-home'); }); it('should render basic header by default', () => { cy.mount(``); cy.get('.usa-header').should('have.class', 'usa-header--basic'); cy.get('.usa-header').should('not.have.class', 'usa-header--extended'); }); it('should render extended header when specified', () => { cy.mount(``); cy.get('.usa-header').should('have.class', 'usa-header--extended'); cy.get('.usa-header').should('not.have.class', 'usa-header--basic'); }); it('should render navigation items', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Home', href: '/', current: true }, { label: 'About', href: '/about' }, { label: 'Services', href: '/services' }, ]; }); cy.get('.usa-nav__primary-item').should('have.length', 3); cy.get('.usa-nav__link').should('contain.text', 'Home'); cy.get('.usa-nav__link').should('contain.text', 'About'); cy.get('.usa-nav__link').should('contain.text', 'Services'); }); it('should handle current page navigation', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Home', href: '/', current: true }, { label: 'About', href: '/about', current: false }, ]; }); cy.get('.usa-nav__link') .first() .should('have.class', 'usa-current') .should('have.attr', 'aria-current', 'page'); cy.get('.usa-nav__link') .last() .should('not.have.class', 'usa-current') .should('have.attr', 'aria-current', ''); }); it('should handle navigation item clicks', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Home', href: '/' }, { label: 'About', href: '/about' }, ]; const navClickSpy = cy.stub(); header.addEventListener('nav-click', navClickSpy); cy.get('.usa-nav__link').contains('About').click(); cy.then(() => { expect(navClickSpy).to.have.been.calledWith( Cypress.sinon.match({ detail: Cypress.sinon.match({ label: 'About', href: '/about', }), }) ); }); }); }); it('should handle navigation items without href', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Custom Action' }, // No href ]; const navClickSpy = cy.stub(); header.addEventListener('nav-click', navClickSpy); cy.get('.usa-nav__link').contains('Custom Action').click(); cy.then(() => { expect(navClickSpy).to.have.been.called; }); }); }); it('should handle submenu navigation', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Services', submenu: [ { label: 'Service 1', href: '/service1' }, { label: 'Service 2', href: '/service2' }, ], }, ]; }); // Should render submenu button cy.get('.usa-accordion__button.usa-nav__link').should('contain.text', 'Services'); cy.get('.usa-nav__submenu').should('exist').should('have.attr', 'hidden'); // Click to expand submenu cy.get('.usa-accordion__button.usa-nav__link').click(); cy.get('.usa-accordion__button.usa-nav__link').should('have.attr', 'aria-expanded', 'true'); cy.get('.usa-nav__submenu').should('not.have.attr', 'hidden'); // Should show submenu items cy.get('.usa-nav__submenu-item').should('have.length', 2); cy.get('.usa-nav__submenu .usa-nav__link').should('contain.text', 'Service 1'); cy.get('.usa-nav__submenu .usa-nav__link').should('contain.text', 'Service 2'); }); it('should close other submenus when opening a new one', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Services', submenu: [{ label: 'Service 1', href: '/service1' }], }, { label: 'Products', submenu: [{ label: 'Product 1', href: '/product1' }], }, ]; }); // Open first submenu cy.get('.usa-accordion__button.usa-nav__link').contains('Services').click(); cy.get('.usa-accordion__button.usa-nav__link') .contains('Services') .should('have.attr', 'aria-expanded', 'true'); // Open second submenu cy.get('.usa-accordion__button.usa-nav__link').contains('Products').click(); cy.get('.usa-accordion__button.usa-nav__link') .contains('Products') .should('have.attr', 'aria-expanded', 'true'); // First submenu should be closed cy.get('.usa-accordion__button.usa-nav__link') .contains('Services') .should('have.attr', 'aria-expanded', 'false'); }); it('should toggle mobile menu', () => { cy.mount(``); // Initially closed cy.get('.usa-nav').should('not.have.class', 'is-visible'); cy.get('.usa-menu-btn').should('have.attr', 'aria-expanded', 'false'); // Click to open cy.get('.usa-menu-btn').click(); cy.get('.usa-nav').should('have.class', 'is-visible'); cy.get('.usa-menu-btn').should('have.attr', 'aria-expanded', 'true'); // Click to close cy.get('.usa-menu-btn').click(); cy.get('.usa-nav').should('not.have.class', 'is-visible'); cy.get('.usa-menu-btn').should('have.attr', 'aria-expanded', 'false'); }); it('should emit mobile menu toggle events', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; const toggleSpy = cy.stub(); header.addEventListener('mobile-menu-toggle', toggleSpy); cy.get('.usa-menu-btn').click(); cy.then(() => { expect(toggleSpy).to.have.been.calledWith( Cypress.sinon.match.hasNested('detail.open', true) ); }); cy.get('.usa-menu-btn').click(); cy.then(() => { expect(toggleSpy).to.have.been.calledWith( Cypress.sinon.match.hasNested('detail.open', false) ); }); }); }); it('should close mobile menu with nav close button', () => { cy.mount(``); // Open mobile menu cy.get('.usa-menu-btn').click(); cy.get('.usa-nav').should('have.class', 'is-visible'); // Close with nav close button cy.get('.usa-nav__close').click(); cy.get('.usa-nav').should('not.have.class', 'is-visible'); }); it('should close mobile menu when clicking outside', () => { cy.mount(`
Outside content
`); // Open mobile menu cy.get('.usa-menu-btn').click(); cy.get('.usa-nav').should('have.class', 'is-visible'); // Click outside cy.get('#outside-content').click(); cy.get('.usa-nav').should('not.have.class', 'is-visible'); }); it('should render search when enabled', () => { cy.mount(``); cy.get('.usa-nav__secondary').should('exist'); cy.get('.usa-search').should('exist'); cy.get('#header-search-field').should('exist'); cy.get('.usa-search button[type="submit"]').should('exist'); }); it('should handle search form submission', () => { cy.mount(` `); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; const searchSpy = cy.stub(); header.addEventListener('header-search', searchSpy); cy.get('#header-search-field') .should('have.attr', 'placeholder', 'Search government') .type('test query'); cy.get('.usa-search button[type="submit"]').click(); cy.then(() => { expect(searchSpy).to.have.been.calledWith( Cypress.sinon.match.hasNested('detail.query', 'test query') ); }); }); }); it('should handle search form submission with Enter key', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; const searchSpy = cy.stub(); header.addEventListener('header-search', searchSpy); cy.get('#header-search-field').type('enter search{enter}'); cy.then(() => { expect(searchSpy).to.have.been.calledWith( Cypress.sinon.match.hasNested('detail.query', 'enter search') ); }); }); }); it('should have proper accessibility attributes', () => { cy.mount(``); cy.get('header').should('have.attr', 'role', 'banner'); cy.get('nav').should('have.attr', 'aria-label', 'Primary navigation'); cy.get('.usa-menu-btn').should('have.attr', 'aria-controls', 'header-nav'); cy.get('nav').should('have.id', 'header-nav'); cy.get('.usa-nav__close').should('have.attr', 'aria-label', 'Close'); }); it('should include skip navigation link', () => { cy.mount(``); cy.get('.usa-skipnav') .should('exist') .should('contain.text', 'Skip to main content') .should('have.attr', 'href', '#main-content'); }); it('should handle complex navigation structure', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Home', href: '/', current: true }, { label: 'Services', submenu: [ { label: 'Web Services', href: '/services/web' }, { label: 'Data Services', href: '/services/data' }, ], }, { label: 'Contact', href: '/contact' }, { label: 'Resources', submenu: [ { label: 'Documentation', href: '/resources/docs' }, { label: 'API Reference', href: '/resources/api' }, { label: 'Support', href: '/resources/support' }, ], }, ]; }); // Should render all top-level items cy.get('.usa-nav__primary-item').should('have.length', 4); // Should handle current page cy.get('.usa-nav__link').contains('Home').should('have.class', 'usa-current'); // Should handle multiple submenus cy.get('.usa-accordion__button.usa-nav__link').should('have.length', 2); // Test first submenu cy.get('.usa-accordion__button.usa-nav__link').contains('Services').click(); cy.get('.usa-nav__submenu-item').should('have.length', 2); // Test second submenu (should close first) cy.get('.usa-accordion__button.usa-nav__link').contains('Resources').click(); cy.get('.usa-nav__submenu-item').should('have.length', 3); }); it('should handle programmatic mobile menu state', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; // Open programmatically header.mobileMenuOpen = true; cy.get('.usa-nav').should('have.class', 'is-visible'); cy.get('.usa-menu-btn').should('have.attr', 'aria-expanded', 'true'); // Close programmatically header.mobileMenuOpen = false; cy.get('.usa-nav').should('not.have.class', 'is-visible'); cy.get('.usa-menu-btn').should('have.attr', 'aria-expanded', 'false'); }); }); it('should handle custom secondary content', () => { cy.mount(`
Custom secondary content
`); cy.get('.custom-secondary').should('contain.text', 'Custom secondary content'); }); it('should handle keyboard navigation in submenus', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Services', submenu: [ { label: 'Service 1', href: '/service1' }, { label: 'Service 2', href: '/service2' }, ], }, ]; }); // Tab to submenu button cy.get('.usa-accordion__button.usa-nav__link').focus(); cy.focused().should('contain.text', 'Services'); // Press Enter to open submenu cy.focused().type('{enter}'); cy.get('.usa-nav__submenu').should('not.have.attr', 'hidden'); // Tab to submenu items cy.focused().tab(); cy.focused().should('contain.text', 'Service 1'); }); it('should handle search accessibility', () => { cy.mount(``); cy.get('.usa-search').should('have.attr', 'role', 'search'); cy.get('.usa-sr-only').should('contain.text', 'Search'); cy.get('#header-search-field').should('have.attr', 'type', 'search'); // Search button should have proper text cy.get('.usa-search__submit-text').should('contain.text', 'Search'); cy.get('.usa-search__submit-icon').should('have.attr', 'aria-hidden', 'true'); }); it('should handle responsive behavior', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [ { label: 'Home', href: '/' }, { label: 'About', href: '/about' }, ]; }); // Set mobile viewport cy.viewport(375, 667); // Menu button should be visible cy.get('.usa-menu-btn').should('be.visible'); // Navigation should be initially hidden cy.get('.usa-nav').should('not.have.class', 'is-visible'); // Should be able to open mobile menu cy.get('.usa-menu-btn').click(); cy.get('.usa-nav').should('have.class', 'is-visible'); }); it('should handle empty navigation gracefully', () => { cy.mount(``); // Should not render navigation list when no items cy.get('.usa-nav__primary').should('not.exist'); // But other elements should still work cy.get('.usa-menu-btn').should('exist'); cy.get('.usa-nav').should('exist'); }); it('should handle event prevention for custom navigation', () => { cy.mount(``); cy.window().then((win) => { const header = win.document.getElementById('test-header') as any; header.navItems = [{ label: 'Custom Action', href: '/custom' }]; // Prevent default navigation header.addEventListener('nav-click', (e: any) => { e.preventDefault(); }); cy.get('.usa-nav__link').contains('Custom Action').click(); // Should not navigate (would need to verify in a different way in real app) cy.url().should('not.include', '/custom'); }); }); it('should cleanup event listeners on disconnect', () => { cy.mount(`
`); cy.window().then((win) => { // Open mobile menu cy.get('.usa-menu-btn').click(); cy.get('.usa-nav').should('have.class', 'is-visible'); // Remove header from DOM container!.removeChild(header); // Click outside should not cause errors cy.get('body').click(); }); }); it('should be accessible', () => { cy.mount(`

Government Website

Welcome to our official website.

`); cy.window().then((win) => { const header = win.document.getElementById('government-header') as any; header.logoText = 'Department of Examples'; header.navItems = [ { label: 'Home', href: '/', current: true }, { label: 'Services', submenu: [ { label: 'Online Services', href: '/services/online' }, { label: 'In-Person Services', href: '/services/in-person' }, ], }, { label: 'About', href: '/about' }, { label: 'Contact', href: '/contact' }, ]; }); cy.injectAxe(); // Test with mobile menu closed cy.checkAccessibility(); // Test with mobile menu open cy.get('.usa-menu-btn').click(); cy.checkAccessibility(); // Test with submenu open cy.get('.usa-accordion__button.usa-nav__link').contains('Services').click(); cy.checkAccessibility(); }); it('should handle custom CSS classes', () => { cy.mount(` `); cy.get('usa-header').should('have.class', 'custom-header-class'); cy.get('.usa-header').should('exist'); }); });