// 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(`
`);
// 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(`
`);
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');
});
});