// Component tests for usa-collection
import './index.ts';
import {
testRapidClicking,
testRapidKeyboardInteraction,
COMMON_BUG_PATTERNS,
} from '../../cypress/support/rapid-interaction-tests.ts';
describe('Collection 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-collection').should('exist');
cy.get('usa-collection').should('be.visible');
});
it('should handle rapid clicking without visual glitches', () => {
cy.mount('');
// Rapid clicking without waiting - simulates real user behavior
cy.get('usa-collection').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-collection').click().click(); // Immediate second click
cy.wait(1000); // Wait for animations
// Should be in consistent state
cy.get('usa-collection').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-collection',
clickCount: 15,
description: 'event listener duplication',
});
});
it('should handle race condition patterns', () => {
cy.mount('');
// Test for race conditions during state changes
cy.get('usa-collection').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-collection').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-collection').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-collection').click().trigger('mouseenter').trigger('mouseleave').focus().blur();
cy.wait(500);
// No console errors should have occurred
cy.get('@consoleError').should('not.have.been.called');
});
// Responsive Layout Testing (Critical Gap Fix)
describe('Responsive Layout Testing', () => {
const sampleCollectionItems = [
{
id: 1,
title: 'Public Health Initiative',
description:
'A comprehensive program to improve community health outcomes through preventive care and education.',
image: '/images/health-initiative.jpg',
date: '2024-01-15',
category: 'Health',
agency: 'Department of Health',
},
{
id: 2,
title: 'Education Reform Program',
description:
'Modernizing educational standards and curriculum to prepare students for 21st-century careers.',
image: '/images/education-reform.jpg',
date: '2024-02-01',
category: 'Education',
agency: 'Department of Education',
},
{
id: 3,
title: 'Infrastructure Development',
description:
'Upgrading roads, bridges, and public transportation to support economic growth and safety.',
image: '/images/infrastructure.jpg',
date: '2024-02-15',
category: 'Infrastructure',
agency: 'Department of Transportation',
},
{
id: 4,
title: 'Environmental Protection Plan',
description:
'Implementing sustainable practices and conservation efforts to protect natural resources.',
image: '/images/environment.jpg',
date: '2024-03-01',
category: 'Environment',
agency: 'Environmental Protection Agency',
},
{
id: 5,
title: 'Economic Development Strategy',
description:
'Supporting small businesses and entrepreneurs to create jobs and stimulate local economies.',
image: '/images/economic-development.jpg',
date: '2024-03-15',
category: 'Economy',
agency: 'Department of Commerce',
},
{
id: 6,
title: 'Technology Innovation Hub',
description:
'Fostering technological advancement and digital literacy across all communities.',
image: '/images/technology.jpg',
date: '2024-04-01',
category: 'Technology',
agency: 'Office of Science and Technology',
},
];
const viewports = [
{ name: 'Mobile Portrait', width: 375, height: 667 },
{ name: 'Mobile Landscape', width: 667, height: 375 },
{ name: 'Tablet Portrait', width: 768, height: 1024 },
{ name: 'Tablet Landscape', width: 1024, height: 768 },
{ name: 'Desktop', width: 1200, height: 800 },
{ name: 'Large Desktop', width: 1920, height: 1080 },
];
describe('Basic Responsive Behavior', () => {
viewports.forEach((viewport) => {
it(`should render correctly on ${viewport.name} (${viewport.width}x${viewport.height})`, () => {
cy.viewport(viewport.width, viewport.height);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('responsive-collection') as any;
collection.items = sampleCollectionItems;
});
// Basic visibility test
cy.get('usa-collection').should('be.visible');
cy.get('.usa-collection').should('be.visible');
// Collection should fit within viewport
cy.get('usa-collection').should(($el) => {
expect($el[0].scrollWidth).to.be.at.most($el[0].clientWidth + 20);
});
// Collection items should be visible
cy.get('.usa-collection__item').should('be.visible');
// Accessibility at all sizes
cy.injectAxe();
cy.checkAccessibility();
});
});
it('should handle viewport orientation changes', () => {
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('orientation-collection') as any;
collection.items = sampleCollectionItems;
});
// Portrait tablet
cy.viewport(768, 1024);
cy.get('.usa-collection').should('be.visible');
cy.get('.usa-collection__item').should('be.visible');
// Landscape tablet
cy.viewport(1024, 768);
cy.get('.usa-collection').should('be.visible');
cy.get('.usa-collection__item').should('be.visible');
// Component should adapt without breaking
cy.injectAxe();
cy.checkAccessibility();
});
});
describe('Mobile Grid Layout', () => {
it('should display single column layout on mobile', () => {
cy.viewport(375, 667); // iPhone SE
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('mobile-grid') as any;
collection.items = sampleCollectionItems;
});
// Should display items in single column
cy.get('.usa-collection')
.should('have.css', 'grid-template-columns')
.and('match', /1fr|none/);
// Items should stack vertically
cy.get('.usa-collection__item').should('have.length', sampleCollectionItems.length);
// Each item should be full width
cy.get('.usa-collection__item').each(($item) => {
const parentWidth = $item.parent().width();
const itemWidth = $item.width();
expect(itemWidth).to.be.at.least(parentWidth! * 0.9); // Allow for some padding
});
});
it('should handle mobile item content overflow', () => {
const longContentItems = sampleCollectionItems.map((item) => ({
...item,
title: `${item.title} - This is an extremely long title that should wrap properly on mobile devices without causing layout issues`,
description: `${item.description} Additional content that tests how the collection item handles very long descriptions on mobile devices. This content should wrap appropriately and not cause horizontal scrolling or layout breakage.`,
}));
cy.viewport(320, 568); // Very narrow mobile
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('mobile-overflow') as any;
collection.items = longContentItems;
});
// Long content should not cause horizontal overflow
cy.get('usa-collection').should(($el) => {
expect($el[0].scrollWidth).to.be.at.most($el[0].clientWidth + 30);
});
// Text should wrap properly
cy.get('.usa-collection__title').should('be.visible');
cy.get('.usa-collection__description').should('be.visible');
});
it('should handle touch interactions on mobile', () => {
cy.viewport(375, 667);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('mobile-touch') as any;
collection.items = sampleCollectionItems;
collection.interactive = true;
});
// Test touch targets are at least 44px
cy.get('.usa-collection__item').each(($item) => {
const rect = $item[0].getBoundingClientRect();
expect(Math.min(rect.width, rect.height)).to.be.at.least(44);
});
// Simulate touch on collection item
cy.get('.usa-collection__item')
.first()
.trigger('touchstart', { touches: [{ clientX: 100, clientY: 100 }] })
.trigger('touchend', { changedTouches: [{ clientX: 100, clientY: 100 }] });
cy.get('.usa-collection').should('be.visible');
});
it('should adapt spacing for mobile screens', () => {
cy.viewport(375, 667);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('mobile-spacing') as any;
collection.items = sampleCollectionItems;
});
// Items should have appropriate mobile spacing
cy.get('.usa-collection__item').should('have.css', 'margin-bottom');
// Collection should use mobile-optimized padding
cy.get('.usa-collection').should('be.visible');
});
});
describe('Tablet Grid Layout', () => {
it('should display two-column layout on tablet portrait', () => {
cy.viewport(768, 1024);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('tablet-portrait-grid') as any;
collection.items = sampleCollectionItems;
});
// Should display in 2-column grid
cy.get('.usa-collection')
.should('have.css', 'grid-template-columns')
.and('include', 'repeat(2');
// Items should be arranged in 2 columns
cy.get('.usa-collection__item').should('have.length', sampleCollectionItems.length);
// Each item should be roughly half width
cy.get('.usa-collection__item')
.first()
.should(($item) => {
const parentWidth = $item.parent().width();
const itemWidth = $item.width();
expect(itemWidth).to.be.lessThan(parentWidth! * 0.6); // Should be less than 60% (allowing for gaps)
});
});
it('should display three-column layout on tablet landscape', () => {
cy.viewport(1024, 768);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('tablet-landscape-grid') as any;
collection.items = sampleCollectionItems;
});
// Should display in 3-column grid
cy.get('.usa-collection')
.should('have.css', 'grid-template-columns')
.and('include', 'repeat(3');
// Items should be arranged efficiently
cy.get('.usa-collection__item').should('be.visible');
});
it('should handle tablet-specific interactions', () => {
cy.viewport(768, 1024);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('tablet-interactions') as any;
collection.items = sampleCollectionItems;
collection.interactive = true;
});
// Test hover states on tablet
cy.get('.usa-collection__item').first().trigger('mouseover');
cy.get('.usa-collection__item').first().should('be.visible');
// Test click interactions
cy.get('.usa-collection__item').first().click();
cy.get('.usa-collection').should('be.visible');
});
});
describe('Desktop Grid Layout', () => {
it('should display three-column layout on desktop', () => {
cy.viewport(1200, 800);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('desktop-grid') as any;
collection.items = sampleCollectionItems;
});
// Should display in 3-column grid
cy.get('.usa-collection')
.should('have.css', 'grid-template-columns')
.and('include', 'repeat(3');
// All items should be visible
cy.get('.usa-collection__item').should('have.length', sampleCollectionItems.length);
// Grid should fit comfortably
cy.get('.usa-collection').should('be.visible');
});
it('should display four-column layout on large desktop', () => {
cy.viewport(1920, 1080);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('large-desktop-grid') as any;
collection.items = sampleCollectionItems;
});
// Should display in 4-column grid on large screens
cy.get('.usa-collection')
.should('have.css', 'grid-template-columns')
.and('include', 'repeat(4');
// Items should be efficiently arranged
cy.get('.usa-collection__item').should('be.visible');
});
it('should handle large datasets efficiently on desktop', () => {
const largeDataset = Array.from({ length: 24 }, (_, i) => ({
id: i + 1,
title: `Item ${i + 1}`,
description: `Description for item ${i + 1} with enough content to test layout.`,
image: `/images/item-${i + 1}.jpg`,
date: `2024-${String(Math.floor(i / 12) + 1).padStart(2, '0')}-${String((i % 12) + 1).padStart(2, '0')}`,
category: ['Health', 'Education', 'Infrastructure', 'Environment'][i % 4],
agency: `Agency ${Math.floor(i / 6) + 1}`,
}));
cy.viewport(1920, 1080);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('large-dataset') as any;
collection.items = largeDataset;
collection.pagination = { pageSize: 12, showPageSizes: true };
});
// Should render efficiently
cy.get('.usa-collection__item').should('have.length.at.most', 12);
// Pagination should work if enabled
if (largeDataset.length > 12) {
cy.get('.usa-pagination').should('be.visible');
}
});
it('should handle desktop hover and focus states', () => {
cy.viewport(1200, 800);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('desktop-hover') as any;
collection.items = sampleCollectionItems;
collection.interactive = true;
});
// Test hover effects
cy.get('.usa-collection__item').first().trigger('mouseover');
cy.get('.usa-collection__item').first().should('be.visible');
// Test focus states
cy.get('.usa-collection__item').first().focus();
cy.focused().should('exist');
// Test keyboard navigation
cy.focused().tab();
cy.focused().should('exist');
});
});
describe('Responsive Collection Features', () => {
it('should handle responsive image loading', () => {
viewports.forEach((viewport) => {
cy.viewport(viewport.width, viewport.height);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById(
`responsive-images-${viewport.width}`
) as any;
collection.items = sampleCollectionItems;
collection.lazyLoading = true;
});
// Images should be loaded appropriately for viewport
cy.get('.usa-collection__image').should('be.visible');
// Images should not cause layout shift
cy.get('.usa-collection').should('be.visible');
});
});
it('should maintain item aspect ratios across viewports', () => {
const testViewports = [
{ width: 375, height: 667, expectedColumns: 1 },
{ width: 768, height: 1024, expectedColumns: 2 },
{ width: 1200, height: 800, expectedColumns: 3 },
];
testViewports.forEach((vp) => {
cy.viewport(vp.width, vp.height);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById(`aspect-ratios-${vp.width}`) as any;
collection.items = sampleCollectionItems;
});
// Items should maintain consistent aspect ratios
cy.get('.usa-collection__item').each(($item) => {
const rect = $item[0].getBoundingClientRect();
expect(rect.width).to.be.greaterThan(0);
expect(rect.height).to.be.greaterThan(0);
});
});
});
it('should handle responsive filtering and search', () => {
cy.viewport(375, 667); // Mobile
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('responsive-filter') as any;
collection.items = sampleCollectionItems;
collection.filterable = true;
collection.searchable = true;
});
// Filter functionality should work on mobile
cy.get('.usa-collection__filter').should('be.visible');
// Change to desktop
cy.viewport(1200, 800);
// Filter should persist and be visible
cy.get('.usa-collection__filter').should('be.visible');
});
it('should handle responsive sorting', () => {
cy.viewport(768, 1024); // Tablet
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('responsive-sort') as any;
collection.items = sampleCollectionItems;
collection.sortable = true;
});
// Sort controls should be visible and functional
cy.get('.usa-collection__sort').should('be.visible');
// Test sorting
cy.get('.usa-collection__sort select').select('date');
cy.get('.usa-collection__item').should('be.visible');
// Change to mobile
cy.viewport(375, 667);
// Sort should still be accessible
cy.get('.usa-collection__sort').should('be.visible');
});
});
describe('Responsive Edge Cases', () => {
it('should handle collections with mixed content types', () => {
const mixedContentItems = [
{
id: 1,
title: 'Short Title',
description: 'Short description.',
image: '/images/short.jpg',
},
{
id: 2,
title:
'This is an Extremely Long Title That Should Test Text Wrapping and Layout Behavior',
description:
'This is a very long description that contains multiple sentences and should test how the collection item handles varying amounts of content. It includes additional details that would normally require more space and should wrap appropriately.',
image: '/images/long.jpg',
},
{
id: 3,
title: 'Medium Length Title Here',
description:
'A moderately sized description that falls between the short and long examples.',
image: '/images/medium.jpg',
},
];
cy.viewport(768, 1024);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('mixed-content') as any;
collection.items = mixedContentItems;
});
// All items should align properly despite different content lengths
cy.get('.usa-collection__item').should('have.length', 3);
cy.get('.usa-collection__item').each(($item) => {
cy.wrap($item).should('be.visible');
});
// No layout breaking should occur
cy.get('.usa-collection').should('be.visible');
});
it('should handle empty collection state responsively', () => {
viewports.forEach((viewport) => {
cy.viewport(viewport.width, viewport.height);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById(
`empty-collection-${viewport.width}`
) as any;
collection.items = [];
});
// Should show empty state properly
cy.get('usa-collection').should('be.visible');
// No layout issues with empty state
cy.get('usa-collection').should(($el) => {
expect($el[0].scrollWidth).to.be.at.most($el[0].clientWidth + 10);
});
});
});
it('should handle dynamic content updates during viewport changes', () => {
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById('dynamic-collection') as any;
collection.items = sampleCollectionItems.slice(0, 3);
});
// Start on desktop (3 columns)
cy.viewport(1200, 800);
cy.get('.usa-collection__item').should('have.length', 3);
// Add more items while transitioning to mobile
cy.window().then((win) => {
const collection = win.document.getElementById('dynamic-collection') as any;
collection.items = sampleCollectionItems; // All 6 items
});
cy.viewport(375, 667); // Switch to mobile (1 column)
cy.get('.usa-collection__item').should('have.length', 6);
// Layout should adapt to new content and viewport
cy.get('.usa-collection').should('be.visible');
});
it('should maintain accessibility across all responsive states', () => {
const testViewports = [
{ width: 375, height: 667 },
{ width: 768, height: 1024 },
{ width: 1200, height: 800 },
];
testViewports.forEach((viewport) => {
cy.viewport(viewport.width, viewport.height);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById(
`a11y-collection-${viewport.width}`
) as any;
collection.items = sampleCollectionItems;
});
// Verify semantic structure at all sizes
cy.get('.usa-collection')
.should('have.attr', 'role')
.or('match', /list|grid/);
// Items should be accessible
cy.get('.usa-collection__item').should('be.visible');
// Check keyboard navigation works
cy.get('.usa-collection__item').first().focus();
cy.focused().tab();
// Run accessibility test
cy.injectAxe();
cy.checkAccessibility();
});
});
it('should handle collection with very few items responsively', () => {
const fewItems = sampleCollectionItems.slice(0, 2);
viewports.forEach((viewport) => {
cy.viewport(viewport.width, viewport.height);
cy.mount(``);
cy.window().then((win) => {
const collection = win.document.getElementById(`few-items-${viewport.width}`) as any;
collection.items = fewItems;
});
// Should handle few items gracefully
cy.get('.usa-collection__item').should('have.length', 2);
cy.get('.usa-collection__item').should('be.visible');
// Layout should still look good
cy.get('.usa-collection').should('be.visible');
});
});
});
});
});