/**
* @fileoverview File Input Timing and Initialization Regression Tests
*
* These tests specifically target timing issues that could affect the file input component:
* 1. Double-click requirement (requestAnimationFrame timing fix)
* 2. Race condition in setupEventHandlers
* 3. USWDS initialization timing
*
* These tests run in a real browser with actual USWDS JavaScript, catching issues
* that unit tests in jsdom cannot detect.
*/
import './index.ts';
describe('File Input Timing and Initialization Regression Tests', () => {
describe('Single-Click Requirement (USWDS Integration)', () => {
it('should activate drag target on FIRST interaction', () => {
cy.mount(`
`);
// Wait for component to initialize and USWDS to transform
cy.wait(500);
// CRITICAL: First interaction should immediately work
// Check that USWDS has created the drag target
cy.get('.usa-file-input__target').should('exist');
cy.get('.usa-file-input__target').should('be.visible');
});
it('should work immediately after component initialization', () => {
cy.mount(`
`);
// Minimal wait - component should be ready quickly
cy.wait(300);
// Should have USWDS-created elements immediately
cy.get('.usa-file-input__target').should('exist');
cy.get('.usa-file-input__choose').should('exist');
});
it('should handle file selection on first try', () => {
cy.mount(`
`);
cy.wait(500);
// Create a test file
const fileName = 'test-file.txt';
const fileContent = 'test content';
// Trigger file selection - should work on FIRST try
cy.get('.usa-file-input__input').selectFile({
contents: Cypress.Buffer.from(fileContent),
fileName: fileName,
mimeType: 'text/plain',
}, { force: true });
cy.wait(200);
// File should be selected and preview shown
cy.get('.usa-file-input__preview').should('exist');
cy.get('.usa-file-input__preview-heading').should('contain', 'Selected file');
});
});
describe('File Input-Specific Timing Tests', () => {
it('should show drag instructions immediately', () => {
cy.mount(`
`);
cy.wait(500);
// Drag instructions should be visible immediately
cy.get('.usa-file-input__drag-text').should('be.visible');
});
it('should handle multiple file selection', () => {
cy.mount(`
`);
cy.wait(500);
// Select multiple files - should work on first try
cy.get('.usa-file-input__input').selectFile([
{
contents: Cypress.Buffer.from('file 1'),
fileName: 'file1.txt',
},
{
contents: Cypress.Buffer.from('file 2'),
fileName: 'file2.txt',
},
], { force: true });
cy.wait(200);
// Both files should be shown in preview
cy.get('.usa-file-input__preview').should('exist');
});
it('should handle file replacement (USWDS pattern for file removal)', () => {
cy.mount(`
`);
cy.wait(500);
// Select a file
cy.get('.usa-file-input__input').selectFile({
contents: Cypress.Buffer.from('test'),
fileName: 'remove-me.txt',
}, { force: true });
cy.wait(200);
// Verify file was selected
cy.get('.usa-file-input__preview').should('exist');
cy.get('.usa-file-input__preview-heading').should('contain', 'Selected file');
// USWDS file-input doesn't have individual file removal buttons
// Files are cleared only by selecting new files via the file input
// The "Change file" text is just a visual indicator - clicking the input itself triggers file selection
// Select a different file (this clears the previous one automatically per USWDS behavior)
cy.get('.usa-file-input__input').selectFile({
contents: Cypress.Buffer.from('new content'),
fileName: 'new-file.txt',
}, { force: true });
cy.wait(200);
// Old file should be replaced with new file
cy.get('.usa-file-input__preview').should('exist');
cy.get('.usa-file-input__preview').should('contain', 'new-file.txt');
cy.get('.usa-file-input__preview').should('not.contain', 'remove-me.txt');
});
it('should validate file types immediately', () => {
cy.mount(`
`);
cy.wait(500);
// Instructions should show accepted file types
cy.get('.usa-file-input__box').should('exist');
});
});
describe('USWDS Initialization Timing', () => {
it('should initialize USWDS after DOM is ready', () => {
cy.mount(`
`);
// Wait for initialization
cy.wait(500);
// USWDS-created elements should exist and be functional
cy.get('.usa-file-input__target').should('exist');
cy.get('.usa-file-input__target').should('be.visible');
cy.get('.usa-file-input__box').should('exist');
cy.get('.usa-file-input__input').should('exist');
});
it('should not duplicate event handlers on rapid property changes', () => {
cy.mount(`
`);
const fileInput = cy.get('#handler-test');
cy.wait(500);
// Rapidly change properties (could trigger initialization multiple times)
fileInput.then(($input) => {
const input = $input[0] as any;
input.disabled = true;
input.required = true;
input.disabled = false;
input.required = false;
});
cy.wait(200);
// Component should still work correctly (no duplicate handlers)
cy.get('.usa-file-input__input').selectFile({
contents: Cypress.Buffer.from('test'),
fileName: 'test.txt',
}, { force: true });
cy.wait(100);
cy.get('.usa-file-input__preview').should('exist');
});
});
describe('Disabled State', () => {
it('should handle disabled state correctly', () => {
cy.mount(`
`);
cy.wait(500);
// Input should be disabled
cy.get('.usa-file-input__input').should('be.disabled');
// Drop zone (main container) should have disabled class
// USWDS adds the disabled class to the drop zone element (.usa-file-input)
cy.get('.usa-file-input').should('have.class', 'usa-file-input--disabled');
});
it('should toggle disabled state dynamically', () => {
cy.mount(`
`);
const fileInput = cy.get('#toggle-disabled-test');
cy.wait(500);
// Initially enabled - input should work
cy.get('.usa-file-input__input').should('not.be.disabled');
// Disable the file input
fileInput.then(($input) => {
const input = $input[0] as any;
input.disabled = true;
});
cy.wait(100);
// Should be disabled
cy.get('.usa-file-input__input').should('be.disabled');
});
});
describe('File Type Restrictions', () => {
it('should respect accept attribute', () => {
cy.mount(`
`);
cy.wait(500);
// Input should have accept attribute
cy.get('.usa-file-input__input').should('have.attr', 'accept', '.jpg,.png,.gif');
});
it('should handle dynamic accept changes', () => {
cy.mount(`
`);
const fileInput = cy.get('#dynamic-accept-test');
cy.wait(500);
// Change accept attribute
fileInput.then(($input) => {
const input = $input[0] as any;
input.accept = '.pdf';
});
cy.wait(100);
// Accept should update
cy.get('.usa-file-input__input').should('have.attr', 'accept', '.pdf');
});
});
describe('Multiple Files', () => {
it('should handle multiple attribute', () => {
cy.mount(`
`);
cy.wait(500);
// Input should have multiple attribute
cy.get('.usa-file-input__input').should('have.attr', 'multiple');
});
it('should toggle multiple dynamically', () => {
cy.mount(`
`);
const fileInput = cy.get('#toggle-multiple-test');
cy.wait(500);
// Initially single file
cy.get('.usa-file-input__input').should('not.have.attr', 'multiple');
// Enable multiple
fileInput.then(($input) => {
const input = $input[0] as any;
input.multiple = true;
});
cy.wait(100);
// Should have multiple attribute
cy.get('.usa-file-input__input').should('have.attr', 'multiple');
});
});
describe('Required State', () => {
it('should handle required attribute', () => {
cy.mount(`
`);
cy.wait(500);
// Input should be required
cy.get('.usa-file-input__input').should('have.attr', 'required');
});
});
});