// Component tests for usa-banner
import './index.ts';
describe('USA Banner Component Tests', () => {
it('should render banner with default properties', () => {
cy.mount(``);
cy.get('usa-banner').should('exist');
cy.get('.usa-banner').should('exist');
cy.get('.usa-banner__header').should('exist');
cy.get('.usa-banner__content').should('exist');
});
it('should display default header text', () => {
cy.mount(``);
cy.get('.usa-banner__header-text').should(
'contain.text',
'An official website of the United States government'
);
cy.get('.usa-banner__header-action').should('contain.text', "Here's how you know");
});
it('should display flag image with proper attributes', () => {
cy.mount(``);
cy.get('.usa-banner__header-flag')
.should('have.attr', 'src', '/img/us_flag_small.png')
.should('have.attr', 'alt', 'U.S. flag');
});
it('should handle custom header text', () => {
cy.mount(`
`);
cy.get('.usa-banner__header-text').should('contain.text', 'Custom government website');
cy.get('.usa-banner__header-action').should('contain.text', 'Learn more');
cy.get('.usa-banner__button-text').should('contain.text', 'Learn more');
});
it('should handle custom image sources', () => {
cy.mount(`
`);
cy.get('.usa-banner__header-flag')
.should('have.attr', 'src', '/custom/flag.png')
.should('have.attr', 'alt', 'Custom flag');
cy.get('.usa-banner__icon').first().should('have.attr', 'src', '/custom/dotgov.svg');
cy.get('.usa-banner__icon').last().should('have.attr', 'src', '/custom/https.svg');
});
it('should toggle banner content on button click', () => {
cy.mount(``);
// Initially collapsed
cy.get('.usa-banner__content').should('have.attr', 'hidden');
cy.get('.usa-accordion__button').should('have.attr', 'aria-expanded', 'false');
// Click to expand
cy.get('.usa-accordion__button').click();
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
cy.get('.usa-accordion__button').should('have.attr', 'aria-expanded', 'true');
// Click to collapse
cy.get('.usa-accordion__button').click();
cy.get('.usa-banner__content').should('have.attr', 'hidden');
cy.get('.usa-accordion__button').should('have.attr', 'aria-expanded', 'false');
});
it('should toggle banner content with keyboard navigation', () => {
cy.mount(``);
// Initially collapsed
cy.get('.usa-banner__content').should('have.attr', 'hidden');
// Focus and press Enter
cy.get('.usa-accordion__button').focus();
cy.focused().type('{enter}');
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
// Press Space to toggle
cy.focused().type(' ');
cy.get('.usa-banner__content').should('have.attr', 'hidden');
});
it('should emit banner-toggle events', () => {
cy.mount(``);
cy.window().then((win) => {
const banner = win.document.getElementById('test-banner') as any;
const toggleSpy = cy.stub();
banner.addEventListener('banner-toggle', toggleSpy);
// Expand banner
cy.get('.usa-accordion__button').click();
cy.then(() => {
expect(toggleSpy).to.have.been.calledWith(
Cypress.sinon.match.hasNested('detail.expanded', true)
);
});
// Collapse banner
cy.get('.usa-accordion__button').click();
cy.then(() => {
expect(toggleSpy).to.have.been.calledWith(
Cypress.sinon.match.hasNested('detail.expanded', false)
);
});
});
});
it('should handle expanded property', () => {
cy.mount(``);
// Should start expanded
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
cy.get('.usa-accordion__button').should('have.attr', 'aria-expanded', 'true');
});
it('should display .gov guidance content', () => {
cy.mount(``);
cy.get('.usa-banner__guidance')
.first()
.within(() => {
cy.get('.usa-banner__icon').should('have.attr', 'alt', 'Dot gov');
cy.get('strong').should('contain.text', 'Official websites use .gov');
cy.get('p').should('contain.text', 'A .gov website belongs to an official government');
});
});
it('should display HTTPS guidance content', () => {
cy.mount(``);
cy.get('.usa-banner__guidance')
.last()
.within(() => {
cy.get('.usa-banner__icon').should('have.attr', 'alt', 'Https');
cy.get('strong').should('contain.text', 'Secure .gov websites use HTTPS');
cy.get('p').should('contain.text', 'A lock');
cy.get('.usa-banner__lock-image').should('exist');
});
});
it('should have proper ARIA controls', () => {
cy.mount(``);
cy.get('.usa-accordion__button').should('have.attr', 'aria-controls', 'gov-banner-default');
cy.get('.usa-banner__content').should('have.id', 'gov-banner-default');
});
it('should handle lock icon SVG accessibility', () => {
cy.mount(``);
cy.get('.usa-banner__lock-image').within(() => {
cy.get('title').should('have.id', 'banner-lock-title');
cy.get('desc').should('have.id', 'banner-lock-description');
cy.get('svg')
.should('have.attr', 'aria-labelledby', 'banner-lock-title banner-lock-description')
.should('have.attr', 'role', 'img');
});
});
it('should prevent default on keyboard events', () => {
cy.mount(``);
cy.window().then((win) => {
const button = win.document.querySelector('.usa-accordion__button') as HTMLElement;
let preventDefaultCalled = false;
const originalPreventDefault = Event.prototype.preventDefault;
Event.prototype.preventDefault = function () {
preventDefaultCalled = true;
originalPreventDefault.call(this);
};
button.focus();
cy.focused().type(' ');
cy.then(() => {
expect(preventDefaultCalled).to.be.true;
Event.prototype.preventDefault = originalPreventDefault;
});
});
});
it('should have proper responsive grid structure', () => {
cy.mount(``);
cy.get('.usa-banner__inner').should('exist');
cy.get('.grid-col-auto').should('exist');
cy.get('.grid-col-fill').should('exist');
cy.get('.grid-row').should('exist');
cy.get('.tablet\\:grid-col-6').should('have.length', 2);
});
it('should handle programmatic expansion', () => {
cy.mount(``);
cy.window().then((win) => {
const banner = win.document.getElementById('test-banner') as any;
// Expand programmatically
banner.expanded = true;
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
cy.get('.usa-accordion__button').should('have.attr', 'aria-expanded', 'true');
// Collapse programmatically
banner.expanded = false;
cy.get('.usa-banner__content').should('have.attr', 'hidden');
cy.get('.usa-accordion__button').should('have.attr', 'aria-expanded', 'false');
});
});
it('should handle multiple banners independently', () => {
cy.mount(`
`);
// Expand first banner
cy.get('#banner1 .usa-accordion__button').click();
cy.get('#banner1 .usa-banner__content').should('not.have.attr', 'hidden');
cy.get('#banner2 .usa-banner__content').should('have.attr', 'hidden');
// Expand second banner
cy.get('#banner2 .usa-accordion__button').click();
cy.get('#banner2 .usa-banner__content').should('not.have.attr', 'hidden');
cy.get('#banner1 .usa-banner__content').should('not.have.attr', 'hidden');
});
it('should handle banner button focus states', () => {
cy.mount(``);
cy.get('.usa-accordion__button').focus();
cy.focused().should('have.class', 'usa-accordion__button');
// Should be keyboard accessible
cy.focused().should('be.visible');
});
it('should handle image loading errors gracefully', () => {
cy.mount(`
`);
// Banner should still render even with missing images
cy.get('.usa-banner').should('exist');
cy.get('.usa-banner__header-flag').should('exist');
cy.get('.usa-accordion__button').should('be.visible');
});
it('should handle content visibility toggle animation', () => {
cy.mount(``);
// Toggle should happen smoothly
cy.get('.usa-accordion__button').click();
cy.get('.usa-banner__content').should('not.have.attr', 'hidden').should('be.visible');
cy.get('.usa-accordion__button').click();
cy.get('.usa-banner__content').should('have.attr', 'hidden');
});
it('should maintain proper semantics for screen readers', () => {
cy.mount(``);
// Header action should be aria-hidden for screen readers
cy.get('.usa-banner__header-action').should('have.attr', 'aria-hidden', 'true');
// Button text should be visible to screen readers
cy.get('.usa-banner__button-text').should('be.visible');
});
it('should handle dynamic property updates', () => {
cy.mount(``);
cy.window().then((win) => {
const banner = win.document.getElementById('test-banner') as any;
// Update header text
banner.headerText = 'Updated government website';
cy.get('.usa-banner__header-text').should('contain.text', 'Updated government website');
// Update action text
banner.actionText = 'Click to learn';
cy.get('.usa-banner__header-action').should('contain.text', 'Click to learn');
cy.get('.usa-banner__button-text').should('contain.text', 'Click to learn');
});
});
it('should handle touch interactions', () => {
cy.mount(``);
// Touch should work like click
cy.get('.usa-accordion__button').trigger('touchstart').trigger('touchend');
// Note: Actual touch behavior may be handled by click events
cy.get('.usa-accordion__button').click(); // Verify normal interaction still works
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
});
it('should handle mobile responsive behavior', () => {
cy.mount(``);
// Set mobile viewport
cy.viewport(375, 667);
cy.get('.usa-banner').should('be.visible');
cy.get('.grid-col-fill').should('exist');
cy.get('.tablet\\:grid-col-auto').should('exist');
// Banner should still be functional on mobile
cy.get('.usa-accordion__button').should('be.visible').click();
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
});
it('should handle edge case keyboard events', () => {
cy.mount(``);
cy.get('.usa-accordion__button').focus();
// Should not respond to other keys
cy.focused().type('{esc}');
cy.get('.usa-banner__content').should('have.attr', 'hidden');
cy.focused().type('a');
cy.get('.usa-banner__content').should('have.attr', 'hidden');
// Should only respond to Enter and Space
cy.focused().type('{enter}');
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
});
it('should maintain USWDS class structure', () => {
cy.mount(``);
// Verify all required USWDS classes are present
cy.get('.usa-banner').should('exist');
cy.get('.usa-accordion').should('exist');
cy.get('.usa-banner__header').should('exist');
cy.get('.usa-banner__inner').should('exist');
cy.get('.usa-banner__header-flag').should('exist');
cy.get('.usa-banner__header-text').should('exist');
cy.get('.usa-banner__header-action').should('exist');
cy.get('.usa-accordion__button').should('exist');
cy.get('.usa-banner__button').should('exist');
cy.get('.usa-banner__content').should('exist');
cy.get('.usa-accordion__content').should('exist');
cy.get('.usa-banner__guidance').should('have.length', 2);
cy.get('.usa-banner__icon').should('have.length', 2);
cy.get('.usa-media-block__img').should('have.length', 2);
cy.get('.usa-media-block__body').should('have.length', 2);
});
it('should be accessible', () => {
cy.mount(`
Government Website
Welcome to our official government website.
`);
cy.injectAxe();
// Test with banner expanded
cy.checkAccessibility();
// Test with banner collapsed
cy.get('.usa-accordion__button').click();
cy.checkAccessibility();
});
it('should handle custom CSS classes', () => {
cy.mount(`
`);
cy.get('usa-banner').should('have.class', 'custom-banner-class');
cy.get('.usa-banner').should('exist');
});
it('should handle content security requirements', () => {
cy.mount(``);
// Content should include required government messaging
cy.get('.usa-banner__guidance').should('contain.text', 'Official websites use .gov');
cy.get('.usa-banner__guidance').should('contain.text', 'Secure .gov websites use HTTPS');
cy.get('.usa-banner__guidance').should(
'contain.text',
'Share sensitive information only on official'
);
});
// Form Integration Robustness Testing (Medium Priority Gap Fix)
describe('Form Integration', () => {
it('should not interfere with form submission when banner toggle is clicked', () => {
let formSubmitted = false;
let bannerToggled = false;
cy.mount(`
`);
cy.window().then((win) => {
const form = win.document.getElementById('banner-form-test') as HTMLFormElement;
const banner = win.document.getElementById('form-banner') as any;
form.addEventListener('submit', (e: Event) => {
e.preventDefault();
formSubmitted = true;
});
banner.addEventListener('banner-toggle', () => {
bannerToggled = true;
});
});
// Fill out form
cy.get('#username').type('testuser');
cy.get('#email').type('test@example.gov');
// Banner toggle should not trigger form submission
cy.get('.usa-accordion__button')
.click()
.then(() => {
expect(bannerToggled).to.be.true;
expect(formSubmitted).to.be.false;
});
// Form submission should work independently
cy.get('button[type="submit"]')
.click()
.then(() => {
expect(formSubmitted).to.be.true;
});
});
it('should handle banner close action without affecting form validation', () => {
let formValidationTriggered = false;
let bannerActionTriggered = false;
cy.mount(`
`);
cy.window().then((win) => {
const form = win.document.getElementById('validation-form-test') as HTMLFormElement;
const banner = win.document.getElementById('validation-banner') as any;
const requiredField = win.document.getElementById('required-field') as HTMLInputElement;
form.addEventListener('submit', (e: Event) => {
e.preventDefault();
if (!form.checkValidity()) {
formValidationTriggered = true;
}
});
banner.addEventListener('banner-toggle', () => {
bannerActionTriggered = true;
});
});
// Banner actions should not trigger form validation
cy.get('.usa-accordion__button')
.click()
.then(() => {
expect(bannerActionTriggered).to.be.true;
expect(formValidationTriggered).to.be.false;
});
// Form validation should work independently when form is actually submitted
cy.get('button[type="submit"]')
.click()
.then(() => {
expect(formValidationTriggered).to.be.true;
});
});
it('should maintain proper focus management in form contexts', () => {
cy.mount(`
`);
// Tab navigation should work properly through form elements
cy.get('#before-banner').focus().tab();
cy.focused().should('have.class', 'usa-accordion__button');
// Banner interaction should not break tab flow
cy.get('.usa-accordion__button').click();
cy.get('.usa-accordion__button').tab();
cy.focused().should('have.id', 'after-banner');
});
it('should handle banner expansion without triggering form auto-submission', () => {
let autoSubmitTriggered = false;
cy.mount(`
`);
cy.window().then((win) => {
const form = win.document.getElementById('auto-submit-test') as HTMLFormElement;
form.addEventListener('submit', (e: Event) => {
e.preventDefault();
autoSubmitTriggered = true;
});
});
// Banner expansion should not cause implicit form submission
cy.get('.usa-accordion__button').click();
cy.get('input[name="single-field"]').type('test text{enter}');
cy.wait(100).then(() => {
// Form auto-submission from single field + enter should still work
expect(autoSubmitTriggered).to.be.true;
});
// Reset and test banner doesn't interfere
cy.window().then(() => {
autoSubmitTriggered = false;
});
cy.get('.usa-accordion__button').click(); // Toggle banner
cy.wait(100).then(() => {
expect(autoSubmitTriggered).to.be.false; // Banner toggle should not trigger submission
});
});
it('should handle keyboard navigation within nested form structures', () => {
cy.mount(`
`);
// Keyboard navigation should work through complex nested structures
cy.get('.usa-accordion__button').focus();
cy.focused().type('{enter}'); // Expand banner
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
// Tab should move to next form element
cy.get('.usa-accordion__button').tab();
cy.focused().should('have.id', 'nested-input');
// Shift+tab should return to banner button
cy.focused().tab({ shift: true });
cy.focused().should('have.class', 'usa-accordion__button');
});
it('should preserve form data during banner interactions', () => {
cy.mount(`
`);
// Verify initial data
cy.get('#preserve-field').should('have.value', 'important data');
cy.get('#preserve-textarea').should('have.value', 'Important message');
// Banner interactions should not affect form data
cy.get('.usa-accordion__button').click();
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
cy.get('#preserve-field').should('have.value', 'important data');
cy.get('#preserve-textarea').should('have.value', 'Important message');
// Collapse banner
cy.get('.usa-accordion__button').click();
cy.get('.usa-banner__content').should('have.attr', 'hidden');
// Data should still be preserved
cy.get('#preserve-field').should('have.value', 'important data');
cy.get('#preserve-textarea').should('have.value', 'Important message');
});
it('should handle banner in multi-step form contexts', () => {
let currentStep = 1;
let formNavigated = false;
cy.mount(`
`);
cy.window().then((win) => {
const nextButton = win.document.getElementById('next-step') as HTMLButtonElement;
nextButton.addEventListener('click', () => {
currentStep = 2;
formNavigated = true;
const step1 = win.document.getElementById('step-1') as HTMLElement;
const step2 = win.document.getElementById('step-2') as HTMLElement;
step1.style.display = 'none';
step2.style.display = 'block';
});
});
// Banner should work independently of multi-step navigation
cy.get('.usa-accordion__button').click();
cy.get('.usa-banner__content').should('not.have.attr', 'hidden');
// Form step navigation should work
cy.get('#next-step')
.click()
.then(() => {
expect(formNavigated).to.be.true;
});
cy.get('#step-2').should('be.visible');
cy.get('#step-1').should('not.be.visible');
// Banner should still be functional after form navigation
cy.get('.usa-accordion__button').click();
cy.get('.usa-banner__content').should('have.attr', 'hidden');
});
it('should respect form validation state during banner interactions', () => {
cy.mount(`
`);
// Invalid state
cy.get('#email-validation').type('invalid@email.com').blur();
cy.window().then((win) => {
const input = win.document.getElementById('email-validation') as HTMLInputElement;
if (!input.checkValidity()) {
const errorDiv = win.document.getElementById('email-error') as HTMLElement;
errorDiv.style.display = 'block';
input.setAttribute('aria-invalid', 'true');
}
});
// Banner interaction should not affect validation state
cy.get('.usa-accordion__button').click();
cy.get('#email-validation').should('have.attr', 'aria-invalid', 'true');
cy.get('#email-error').should('be.visible');
// Valid input should clear validation state independently
cy.get('#email-validation').clear().type('valid@agency.gov').blur();
cy.window().then((win) => {
const input = win.document.getElementById('email-validation') as HTMLInputElement;
if (input.checkValidity()) {
const errorDiv = win.document.getElementById('email-error') as HTMLElement;
errorDiv.style.display = 'none';
input.removeAttribute('aria-invalid');
}
});
cy.get('#email-validation').should('not.have.attr', 'aria-invalid');
cy.get('#email-error').should('not.be.visible');
});
});
});