lecture 11 of 150 completed

File uploads and downloads

Both are common in real apps and awkward to test. Here is the working approach for each, and what to assert.

Upload and download appear in almost every business app: import a CSV of contacts, export an invoice, attach a document. Both are more awkward to test than they look, for the same reason: the file dialog belongs to the operating system, not the page, and no browser automation tool can click it.

Uploads.

You do not click "Browse". You attach the file to the input element directly. Modern Cypress has a command for it:

cy.get('[data-testid="import-input"]').selectFile('cypress/fixtures/contacts.csv');

The file lives in cypress/fixtures. Cypress attaches it to the input exactly as if the user had picked it, and the app's upload handler runs normally.

For a drag-and-drop area, say so:

cy.get('[data-testid="dropzone"]').selectFile('cypress/fixtures/contacts.csv', {
  action: 'drag-drop',
});

Now the part that matters more than the syntax: what do you assert?

Not that the filename appears on screen. That only proves the app read the name. Assert the effect:

cy.get('[data-testid="import-input"]').selectFile('cypress/fixtures/contacts.csv');
cy.get('[data-testid="import-confirm"]').click();
cy.get('[data-testid="contact-row"]').should('have.length', 12);
cy.contains('[data-testid="contact-row"]', 'Dana Fox').should('be.visible');

Twelve rows, and a name that was inside the file. That test fails if the parser breaks, which is the bug you are actually worried about.

Test the bad files too. A CSV with a missing column, a file that is not a CSV at all, an empty file. Those paths are where import bugs live, and they are usually untested.

Downloads.

The browser saves the file to disk, and your assertion has to look at the filesystem rather than the page. Cypress puts downloads in cypress/downloads by default:

cy.get('[data-testid="export"]').click();
cy.readFile('cypress/downloads/contacts.csv').should('contain', 'Dana Fox');

cy.readFile retries until the file exists, so it handles the delay while the download completes.

Two warnings from experience.

Downloads do not work the same headlessly in every browser. This is a common source of "passes locally, fails in CI". If a download test is fragile on your build server, checking the export endpoint with cy.request instead is often more reliable and tests the same logic.

Clean up between runs. A stale file from yesterday's run will make today's test pass even if nothing downloaded. Delete the folder before the suite, or assert on content unique to this run.

Remember this: attach the file directly, then assert on what the file did, not on its name.

Reference: .selectFile().