// Component tests for usa-radio import './index.ts'; describe('USA Radio Component Tests', () => { it('should render radio button with default properties', () => { cy.mount(` Option 1 `); cy.get('usa-radio').should('exist'); cy.get('usa-radio input[type="radio"]').should('have.class', 'usa-radio__input'); cy.get('usa-radio .usa-radio__label').should('contain.text', 'Option 1'); }); it('should handle radio button selection in a group', () => { cy.mount(`
Choose one option Option 1 Option 2 Option 3
`); // Initially none selected cy.get('input[name="choice"]:checked').should('have.length', 0); // Select option 2 cy.get('#radio2 input').check(); cy.get('#radio2 input').should('be.checked'); cy.get('#radio1 input').should('not.be.checked'); cy.get('#radio3 input').should('not.be.checked'); // Select option 3 (should deselect option 2) cy.get('#radio3 input').check(); cy.get('#radio3 input').should('be.checked'); cy.get('#radio1 input').should('not.be.checked'); cy.get('#radio2 input').should('not.be.checked'); }); it('should handle clicking on label', () => { cy.mount(` Click this label `); // Click on label should select radio cy.get('.usa-radio__label').click(); cy.get('input[type="radio"]').should('be.checked'); }); it('should emit change events', () => { cy.mount(` Test change event `); cy.window().then((win) => { const radio = win.document.getElementById('test-radio') as any; const changeSpy = cy.stub(); radio.addEventListener('change', changeSpy); cy.get('input[type="radio"]').check(); cy.then(() => { expect(changeSpy).to.have.been.called; }); }); }); it('should handle initial checked state', () => { cy.mount(`
Option 1 Option 2 (Preselected) Option 3
`); cy.get('#radio2 input').should('be.checked'); cy.get('#radio1 input').should('not.be.checked'); cy.get('#radio3 input').should('not.be.checked'); }); it('should handle disabled state', () => { cy.mount(`
Available option Disabled option
`); cy.get('#radio2 input').should('be.disabled'); cy.get('#radio2 .usa-radio__label').should('have.class', 'usa-radio__label--disabled'); // Should not be selectable cy.get('#radio2 .usa-radio__label').click({ force: true }); cy.get('#radio2 input').should('not.be.checked'); // Other options should still work cy.get('#radio1 input').check(); cy.get('#radio1 input').should('be.checked'); }); it('should handle required state', () => { cy.mount(`
Required selection Option 1 Option 2
`); cy.get('#radio1 input').should('have.attr', 'required'); cy.get('#radio2 input').should('have.attr', 'required'); cy.get('#radio1 input').should('have.attr', 'aria-required', 'true'); cy.get('#radio2 input').should('have.attr', 'aria-required', 'true'); }); it('should handle error state', () => { cy.mount(`
Option 1 Option 2
`); cy.get('#radio1 input').should('have.attr', 'aria-invalid', 'true'); cy.get('#radio2 input').should('have.attr', 'aria-invalid', 'true'); cy.get('.usa-radio').should('have.class', 'usa-radio--error'); cy.get('.usa-error-message').should('contain.text', 'Please select an option'); }); it('should handle large size variant', () => { cy.mount(` Large radio button `); cy.get('.usa-radio').should('have.class', 'usa-radio--large'); }); it('should handle small size variant', () => { cy.mount(` Small radio button `); cy.get('.usa-radio').should('have.class', 'usa-radio--small'); }); it('should be keyboard accessible', () => { cy.mount(`
Keyboard navigation test Option 1 Option 2 Option 3
`); // Tab to first radio cy.get('#radio1 input').focus(); cy.focused().should('have.attr', 'name', 'keyboard-group'); cy.focused().should('have.attr', 'value', 'option1'); // Arrow down to next radio cy.focused().type('{downarrow}'); cy.focused().should('have.attr', 'value', 'option2'); cy.get('#radio2 input').should('be.checked'); // Arrow up to previous radio cy.focused().type('{uparrow}'); cy.focused().should('have.attr', 'value', 'option1'); cy.get('#radio1 input').should('be.checked'); // Space to select current radio cy.focused().type(' '); cy.get('#radio1 input').should('be.checked'); }); it('should handle form integration', () => { cy.mount(`
Preferred contact method Email Phone Mail
`); cy.window().then((win) => { 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('contact-method')); }); // Select phone option cy.get('#contact-phone input').check(); // Submit form cy.get('button[type="submit"]').click(); cy.then(() => { expect(submitSpy).to.have.been.calledWith('phone'); }); }); }); it('should handle multiple radio groups independently', () => { cy.mount(`
Size preference Small Medium Large
Color preference Red Blue Green
`); // Select from both groups independently cy.get('#size-medium input').check(); cy.get('#color-red input').check(); // Both should be selected cy.get('#size-medium input').should('be.checked'); cy.get('#color-red input').should('be.checked'); // Selecting another from same group should deselect previous cy.get('#size-large input').check(); cy.get('#size-large input').should('be.checked'); cy.get('#size-medium input').should('not.be.checked'); // Other group should remain unchanged cy.get('#color-red input').should('be.checked'); }); it('should handle tile variant', () => { cy.mount(` Tile radio button with more content `); cy.get('.usa-radio').should('have.class', 'usa-radio--tile'); }); it('should handle description text', () => { cy.mount(` Option with description `); cy.get('.usa-radio__description').should( 'contain.text', 'This option includes additional explanatory text' ); }); it('should handle aria attributes', () => { cy.mount(`
Additional context for this choice Radio with ARIA attributes
`); cy.get('input[type="radio"]').should('have.attr', 'aria-describedby', 'radio-description'); }); it('should handle focus and blur events', () => { cy.mount(` Focus test radio `); cy.window().then((win) => { const radio = win.document.getElementById('test-radio') as any; const focusSpy = cy.stub(); const blurSpy = cy.stub(); radio.addEventListener('focus', focusSpy); radio.addEventListener('blur', blurSpy); cy.get('input[type="radio"]').focus(); cy.get('input[type="radio"]').blur(); cy.then(() => { expect(focusSpy).to.have.been.called; expect(blurSpy).to.have.been.called; }); }); }); it('should handle validation on blur', () => { cy.mount(`
Required option 1 Required option 2
`); // Focus and blur without selecting (should trigger validation) cy.get('#radio1 input').focus().blur(); cy.get('input[name="validation-group"]').should('have.attr', 'aria-invalid', 'true'); // Select an option (should clear validation) cy.get('#radio2 input').check(); cy.get('input[name="validation-group"]').should('not.have.attr', 'aria-invalid', 'true'); }); it('should be accessible', () => { cy.mount(`
Payment Method Credit Card
Visa, MasterCard, American Express accepted
Bank Transfer
`); cy.injectAxe(); cy.checkAccessibility(); }); it('should handle custom CSS classes', () => { cy.mount(` Custom styled radio `); cy.get('usa-radio').should('have.class', 'custom-radio-class'); cy.get('.usa-radio').should('exist'); }); it('should handle programmatic selection', () => { cy.mount(`
Option 1 Option 2
`); cy.window().then((win) => { const radio2 = win.document.getElementById('radio2') as any; // Set checked programmatically radio2.checked = true; cy.get('#radio2 input').should('be.checked'); cy.get('#radio1 input').should('not.be.checked'); }); }); it('should handle nested fieldsets properly', () => { cy.mount(`
Contact Information
Preferred Contact Time Morning (9 AM - 12 PM) Afternoon (12 PM - 5 PM)
Contact Method Email Phone
`); // Each group should work independently cy.get('#time-afternoon input').check(); cy.get('#method-email input').check(); cy.get('#time-afternoon input').should('be.checked'); cy.get('#method-email input').should('be.checked'); cy.get('#time-morning input').should('not.be.checked'); cy.get('#method-phone input').should('not.be.checked'); }); // Responsive Layout Tests describe('Mobile Responsive Behavior', () => { beforeEach(() => { cy.viewport(375, 667); // iPhone SE }); it('should display radio groups properly stacked on mobile', () => { cy.mount(`
Preferred Notification Method Email notifications SMS text messages Phone calls Physical mail
`); // Radio buttons should stack vertically on mobile cy.get('usa-radio').should('have.length', 4); // Touch targets should be at least 44px high cy.get('usa-radio').each(($radio) => { cy.wrap($radio).should(($el) => { const height = $el.outerHeight(); expect(height).to.be.at.least(44); }); }); // Labels should be readable and not wrap awkwardly cy.get('.usa-radio__label').each(($label) => { cy.wrap($label).should('be.visible'); }); }); it('should handle mobile touch interactions', () => { cy.mount(`
Select Payment Method Credit Card Debit Card Bank Transfer
`); // Touch interaction should work cy.get('#mobile-credit .usa-radio__label').trigger('touchstart').trigger('touchend'); cy.get('#mobile-credit input').should('be.checked'); // Large size should be especially touch-friendly cy.get('.usa-radio').should('have.class', 'usa-radio--large'); }); it('should handle mobile tile variant', () => { cy.mount(`
Choose Your Plan Basic Plan - $9.99/month Premium Plan - $19.99/month Enterprise Plan - $49.99/month
`); // Tile variants should be properly sized for mobile cy.get('.usa-radio').should('have.class', 'usa-radio--tile'); // Description text should be visible cy.get('.usa-radio__description').should('be.visible'); cy.get('.usa-radio__description').first().should('contain.text', 'Basic features'); // Tiles should stack vertically on mobile cy.get('usa-radio').should('have.length', 3); }); it('should handle mobile error states', () => { cy.mount(`
Required Selection Please select one option Option 1 Option 2
`); cy.get('.usa-error-message').should('be.visible'); cy.get('input[type="radio"]').should('have.attr', 'aria-invalid', 'true'); // Error message should be readable on mobile cy.get('.usa-error-message').should('have.css', 'font-size'); }); it('should handle mobile form layout with multiple radio groups', () => { cy.mount(`
Shipping Options Standard Shipping (5-7 days) Express Shipping (2-3 days) Overnight Shipping
Gift Wrapping Yes, please gift wrap No gift wrapping needed
`); // Multiple fieldsets should stack properly on mobile cy.get('.usa-fieldset').should('have.length', 2); // Select from each group independently cy.get('#shipping-express input').check(); cy.get('#gift-yes input').check(); cy.get('#shipping-express input').should('be.checked'); cy.get('#gift-yes input').should('be.checked'); }); }); describe('Tablet Responsive Behavior', () => { beforeEach(() => { cy.viewport(768, 1024); // iPad }); it('should display radio groups in grid layout on tablet', () => { cy.mount(`
Preferred Contact Method Email Phone Mail
Preferred Contact Time Morning (9 AM - 12 PM) Afternoon (12 PM - 5 PM) Evening (5 PM - 8 PM)
`); // 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 tile variant with grid', () => { cy.mount(`
Select Service Level
Basic Service
Standard Service
Premium Service
`); // Tiles should be in horizontal layout on tablet cy.get('.tablet\\:grid-col-4').should('have.length', 3); cy.get('.usa-radio--tile').should('have.length', 3); // Standard should be preselected cy.get('#tablet-standard input').should('be.checked'); // Should handle both touch and hover cy.get('#tablet-premium .usa-radio__label').trigger('mouseover').click(); cy.get('#tablet-premium input').should('be.checked'); cy.get('#tablet-standard input').should('not.be.checked'); }); it('should handle tablet accessibility with mixed interaction', () => { cy.mount(`
Account Type Personal Account Business Account Nonprofit Account
`); // Should work with both touch and keyboard cy.get('#tablet-personal input').focus(); cy.focused().type('{downarrow}'); cy.get('#tablet-business input').should('be.checked'); // Touch interaction should also work cy.get('#tablet-nonprofit .usa-radio__label').trigger('touchstart').trigger('touchend'); cy.get('#tablet-nonprofit input').should('be.checked'); }); }); describe('Desktop Responsive Behavior', () => { beforeEach(() => { cy.viewport(1200, 800); // Desktop }); it('should display radio groups in multi-column layout', () => { cy.mount(`
Notification Preferences Email notifications SMS notifications Push notifications No notifications
Privacy Settings Public profile Friends only Private profile
Theme Preference Light theme Dark theme Automatic
`); // Check three-column layout on desktop cy.get('.desktop\\:grid-col-4').should('have.length', 3); // Test independent group selection cy.get('#desktop-sms input').check(); cy.get('#privacy-friends input').check(); cy.get('#theme-auto input').check(); cy.get('#desktop-sms input').should('be.checked'); cy.get('#privacy-friends input').should('be.checked'); cy.get('#theme-auto input').should('be.checked'); }); it('should handle desktop tile variant in grid layout', () => { cy.mount(`
Choose Your Subscription Plan
Starter Plan
$9.99/month
Professional Plan
$19.99/month
Business Plan
$39.99/month
Enterprise Plan
Custom pricing
`); // Four-column layout on desktop cy.get('.desktop\\:grid-col-3').should('have.length', 4); cy.get('.usa-radio--tile').should('have.length', 4); // Should handle hover states cy.get('#desktop-professional .usa-radio__label') .trigger('mouseover') .should('have.css', 'cursor', 'pointer'); // Select and verify cy.get('#desktop-business input').check(); cy.get('#desktop-business input').should('be.checked'); }); it('should handle desktop keyboard navigation efficiently', () => { cy.mount(`
Accessibility Test Option 1 Option 2 Option 3 Option 4
`); // Keyboard navigation should work efficiently cy.get('#desktop-option1 input').focus(); cy.focused().should('have.attr', 'value', 'option1'); // Arrow navigation cy.focused().type('{downarrow}'); cy.focused().should('have.attr', 'value', 'option2'); cy.get('#desktop-option2 input').should('be.checked'); cy.focused().type('{downarrow}'); cy.focused().should('have.attr', 'value', 'option3'); cy.get('#desktop-option3 input').should('be.checked'); // Arrow up cy.focused().type('{uparrow}'); cy.focused().should('have.attr', 'value', 'option2'); cy.get('#desktop-option2 input').should('be.checked'); }); it('should handle desktop hover and focus states', () => { cy.mount(`
Interactive States Test Hover and focus test
`); // Hover state cy.get('#desktop-hover .usa-radio__label') .trigger('mouseover') .should('have.css', 'cursor', 'pointer'); // Focus state cy.get('#desktop-hover input') .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(`
User Preferences
Email updates Phone calls
SMS messages Physical mail
Account Settings Public Account Private Account
`); // Container should be properly centered cy.get('.grid-container').should('have.css', 'max-width'); // Radio groups should have adequate spacing cy.get('.usa-fieldset').should('have.length', 2); // Tile variants should be properly sized cy.get('.usa-radio--tile').should('have.length', 2); }); it('should handle large desktop complex forms', () => { cy.mount(`
Account Type Individual Business Non-profit
Service Level Basic Standard Premium
Billing Cycle Monthly Quarterly Annual
Add-ons Priority Support Training Package No Add-ons
`); // Four-column layout cy.get('.desktop\\:grid-col-3').should('have.length', 4); // Test selections across all groups cy.get('#large-business input').check(); cy.get('#large-premium input').check(); cy.get('#large-annual input').check(); cy.get('#large-support input').check(); // All should be independently selected cy.get('#large-business input').should('be.checked'); cy.get('#large-premium input').should('be.checked'); cy.get('#large-annual input').should('be.checked'); cy.get('#large-support input').should('be.checked'); }); }); describe('Responsive Edge Cases', () => { it('should handle viewport transitions smoothly', () => { cy.mount(`
Responsive Test Option 1 Option 2
`); // Test mobile to tablet transition cy.viewport(375, 667); cy.get('#transition-option1 input').check(); cy.get('#transition-option1 input').should('be.checked'); cy.viewport(768, 1024); cy.get('#transition-option1 input').should('be.checked'); cy.viewport(1200, 800); cy.get('#transition-option1 input').should('be.checked'); }); it('should handle long label text at different screen sizes', () => { const longLabels = [ 'This is a very long radio button label that might wrap to multiple lines on smaller screens', 'Another extremely long label that tests how the component handles text wrapping and layout at various viewport sizes', 'A third long label to test multiple radio buttons with extensive text content', ]; cy.mount(`
Long Text Test ${longLabels[0]} ${longLabels[1]} ${longLabels[2]}
`); // Test at different viewports const viewports = [ [320, 568], // Small mobile [768, 1024], // Tablet [1200, 800], // Desktop ]; viewports.forEach(([width, height]) => { cy.viewport(width, height); // Labels should be visible and readable cy.get('.usa-radio__label').each(($label) => { cy.wrap($label).should('be.visible'); }); // Should not cause horizontal overflow cy.get('.usa-fieldset').then(($fieldset) => { expect($fieldset[0].scrollWidth).to.be.at.most($fieldset[0].clientWidth + 5); }); }); }); it('should handle dynamic radio group updates', () => { cy.mount(`
Dynamic Group Initial Option
`); const scenarios = [ { viewport: [375, 667], options: 2 }, { viewport: [768, 1024], options: 4 }, { viewport: [1200, 800], options: 6 }, ]; scenarios.forEach(({ viewport, options }) => { cy.viewport(viewport[0], viewport[1]); // Simulate adding more options based on viewport cy.window().then((win) => { const fieldset = win.document.getElementById('dynamic-fieldset'); // Clear existing options except first const existingRadios = fieldset?.querySelectorAll('usa-radio'); if (existingRadios) { for (let i = 1; i < existingRadios.length; i++) { existingRadios[i].remove(); } } // Add options based on viewport for (let i = 2; i <= options; i++) { const radio = win.document.createElement('usa-radio'); radio.id = `dynamic-option-${i}`; radio.setAttribute('name', 'dynamic-group'); radio.setAttribute('value', `option${i}`); radio.textContent = `Option ${i}`; fieldset?.appendChild(radio); } }); cy.get('usa-radio').should('have.length', options); cy.get('#dynamic-initial input').check(); cy.get('#dynamic-initial input').should('be.checked'); }); }); it('should maintain accessibility at all screen sizes', () => { cy.mount(`
Accessible Radio Group Option 1
Additional information for option 1
Option 2 (Required)
`); const viewports = [ [375, 667], // Mobile [768, 1024], // Tablet [1200, 800], // Desktop ]; viewports.forEach(([width, height]) => { cy.viewport(width, height); cy.injectAxe(); cy.checkAccessibility(); // Check ARIA attributes cy.get('#accessible-1 input').should('have.attr', 'aria-describedby', 'option1-hint'); cy.get('#accessible-2 input') .should('have.attr', 'required') .should('have.attr', 'aria-required', 'true'); // Check keyboard navigation cy.get('#accessible-1 input').focus().should('have.focus'); }); }); it('should handle radio groups in responsive forms', () => { cy.mount(`
Responsive Form Section 1 Form Option 1 Form Option 2
Responsive Form Section 2 Tile Option 1 Tile Option 2
`); const viewports = [ [375, 667], // Mobile (stacked) [768, 1024], // Tablet (stacked) [1200, 800], // Desktop (side by side) ]; viewports.forEach(([width, height]) => { cy.viewport(width, height); // All radio groups should work regardless of layout cy.get('#form-radio-1 input').check(); cy.get('#form-radio-3 input').check(); cy.get('#form-radio-1 input').should('be.checked'); cy.get('#form-radio-3 input').should('be.checked'); // Switch selections cy.get('#form-radio-2 input').check(); cy.get('#form-radio-4 input').check(); cy.get('#form-radio-2 input').should('be.checked'); cy.get('#form-radio-4 input').should('be.checked'); cy.get('#form-radio-1 input').should('not.be.checked'); cy.get('#form-radio-3 input').should('not.be.checked'); }); }); }); });