QA How-To
Cypress file upload: Examples
Copy practical Cypress file upload example patterns for PDFs, generated CSV, hidden inputs, multiple files, drag and drop, errors, processing, and CI.
24 min read | 3,123 words
TL;DR
A reliable Cypress file upload example calls .selectFile() with a project-relative path or file descriptor, waits on a pre-registered request alias, and asserts the final UI state. Use action: 'drag-drop' for drop zones and arrays for multiple files.
Key Takeaways
- Register upload intercepts before selecting a file because many controls upload on change.
- Use project-relative paths for stable fixtures and descriptors with Cypress.Buffer for generated content.
- Re-query the input after selectFile or assert visible state instead of relying on the yielded subject.
- Treat FileList, request, processing, and final UI assertions as different boundaries.
- Use drag-drop action for drop targets and arrays for inputs that support multiple selection.
- Keep reusable helpers small so business triggers and outcomes remain visible in each test.
This cypress file upload example collection shows copy-ready patterns for real upload controls: a PDF input, generated CSV, multiple selection, hidden inputs, drag-and-drop, metadata, client rejection, server errors, and post-upload processing. Every example uses Cypress's built-in .selectFile() command.
The examples deliberately assert different boundaries. Some inspect the browser's FileList, some observe the request, and others verify the final product state. Use the narrowest example that matches your application, then replace selectors, endpoints, and fixtures with your owned contracts.
TL;DR
cy.intercept('POST', '/api/documents').as('createDocument')
cy.get('input[name="document"]')
.selectFile('cypress/fixtures/documents/sample.pdf')
cy.wait('@createDocument')
.its('response.statusCode')
.should('eq', 201)
cy.contains('sample.pdf').should('be.visible')
| Example need | Input to .selectFile() |
|---|---|
| Repository fixture | 'cypress/fixtures/documents/sample.pdf' |
| Generated file | { contents: Cypress.Buffer.from(text), fileName, mimeType } |
| Several files | [pathOne, pathTwo] |
| Drag target | file, { action: 'drag-drop' } |
| Raw fixture alias | cy.fixture(path, null).as(name), then '@name' |
Paths are resolved from the project root. The application must expose a native file input or handle browser drag events.
1. Minimal Cypress File Upload Example
Use this example when the application uploads only after a user chooses a file and clicks Submit.
describe('document upload', () => {
beforeEach(() => {
cy.visit('/documents/new')
})
it('uploads a PDF', () => {
cy.intercept('POST', '/api/documents').as('uploadDocument')
cy.get('input[name="document"]')
.selectFile('cypress/fixtures/documents/sample.pdf')
cy.get('[data-cy="selected-file"]')
.should('contain.text', 'sample.pdf')
cy.contains('button', 'Upload').click()
cy.wait('@uploadDocument')
.its('response.statusCode')
.should('eq', 201)
cy.contains('[role="status"]', 'Upload complete')
.should('be.visible')
})
})
This sequence is reliable because the request route is registered before any upload trigger, the file is selected by a project-relative path, and the test waits on a named business request. The visible assertions show that both selection and response handling worked.
If selection itself sends the request, remove the Submit click. Keep the intercept before .selectFile(). If the application uses a presigned URL, alias the initialization call and the completion notification separately.
Avoid chaining a dependent command directly after .selectFile(). Although the command yields the original subject, Cypress marks it unsafe to rely on that subject later. Re-query the input or, preferably, assert the visible filename and status.
For the API observation fundamentals used here, see the Cypress cy.intercept examples.
2. Cypress File Upload Example with FileList Assertions
Inspect input.files when the contract includes filename, MIME type, or selection count. This assertion diagnoses browser-side metadata before a request begins.
it('creates the expected browser File object', () => {
cy.visit('/profile')
cy.get('input[name="avatar"]')
.selectFile('cypress/fixtures/images/avatar.png')
cy.get<HTMLInputElement>('input[name="avatar"]').then(($input) => {
const files = $input[0].files
expect(files).to.have.length(1)
expect(files?.[0].name).to.eq('avatar.png')
expect(files?.[0].type).to.eq('image/png')
expect(files?.[0].size).to.be.greaterThan(0)
})
})
The generic documents the expected element type for TypeScript. Optional chaining handles the DOM FileList type, although a real file input after successful selection should have a file.
This example does not prove upload completion. Use it for client-side behaviors such as preview generation, filename rendering, and immediate validation. Add a server or UI outcome assertion when the scenario claims the document was uploaded.
Do not assert an exact binary size unless the fixture size is itself a controlled boundary. A routine success test should avoid coupling to incidental bytes. MIME type can also depend on the supplied descriptor or filename inference, so make it explicit in dynamic examples.
A useful failure message names the boundary. expect(files, 'selected files') is clearer than an eventual error from reading files[0] when no file was selected.
3. Generate a CSV Upload in Memory
Generate readable text inputs when only a few rows differ between scenarios. Cypress.Buffer.from() supplies the bytes, and the descriptor supplies browser metadata.
const createCsv = (rows: string[][]) => {
return [
['email', 'role'],
...rows,
]
.map((row) => row.join(','))
.join('\n')
}
it('imports two team members', () => {
const csv = createCsv([
['avery@example.test', 'viewer'],
['jordan@example.test', 'editor'],
])
cy.intercept('POST', '/api/team/import').as('importTeam')
cy.visit('/team/import')
cy.get('input[name="teamFile"]').selectFile({
contents: Cypress.Buffer.from(csv),
fileName: 'team.csv',
mimeType: 'text/csv',
lastModified: new Date('2026-01-15T00:00:00Z').valueOf(),
})
cy.contains('button', 'Import').click()
cy.wait('@importTeam').then(({ response }) => {
expect(response?.statusCode).to.eq(202)
expect(response?.body).to.include({ acceptedRows: 2 })
})
cy.contains('2 team members queued').should('be.visible')
})
The helper is intentionally small. It is not a general CSV serializer because fields containing commas, quotes, or newlines need proper escaping. Use a CSV library in Node or committed fixtures for complex formats.
A fixed lastModified avoids time-dependent assertions. Most products should ignore that browser property, but setting it makes the file descriptor deterministic.
Generated content is especially useful for table-driven validation. Keep a representative set of rows visible in each test so reviewers understand why the import should pass or fail.
4. Upload Multiple Files and Assert the Queue
The default selection action supports arrays only when the input has multiple.
it('adds three files to the attachment queue', () => {
cy.visit('/tickets/42')
cy.get('input[name="attachments"]').selectFile([
'cypress/fixtures/documents/readme.txt',
'cypress/fixtures/images/screenshot.png',
{
contents: Cypress.Buffer.from('step,result\nlogin,passed'),
fileName: 'results.csv',
mimeType: 'text/csv',
},
])
cy.get('[data-cy="attachment-row"]').should('have.length', 3)
cy.get('[data-cy="attachment-row"]').then(($rows) => {
const text = [...$rows].map((row) => row.textContent)
expect(text).to.deep.include.members([
'readme.txt',
'screenshot.png',
'results.csv',
])
})
})
The assertion treats the queue as unordered. Use a strict order only when order affects product behavior. Concurrent upload completion can legitimately differ across environments.
For an uploaded result, alias the endpoint and inspect all matching calls:
cy.intercept('POST', '/api/tickets/42/attachments').as('uploadAttachment')
cy.contains('button', 'Upload all').click()
cy.get('@uploadAttachment.all').should('have.length', 3)
cy.get('[data-cy="attachment-row"][data-status="ready"]')
.should('have.length', 3)
Alias .all is valuable for repeated calls. If the application sends one multipart request containing every file instead, expect one call and assert the final three records. Match the transport your product actually owns rather than forcing a specific implementation.
5. Select a File Through a Hidden Input
Many React components style a label or button and hide the native input. Target the real input. Use force only if it is intentionally non-actionable because of that design.
it('uploads an avatar through a custom control', () => {
cy.intercept('POST', '/api/profile/avatar').as('uploadAvatar')
cy.visit('/profile')
cy.get('[data-cy="avatar-input"]')
.should('have.attr', 'type', 'file')
.selectFile(
'cypress/fixtures/images/avatar.png',
{ force: true },
)
cy.wait('@uploadAvatar')
.its('response.statusCode')
.should('eq', 200)
cy.get('[data-cy="avatar-preview"]')
.should('have.attr', 'alt', 'Selected profile image')
})
The type assertion guards against a selector accidentally matching a wrapper. A stable test ID is appropriate for a hidden implementation element that has no visible text.
If a <label> is correctly associated, test through it without force:
cy.contains('label', 'Choose profile image')
.selectFile('cypress/fixtures/images/avatar.png')
Do not click the styled button and attempt to control the native operating-system picker. Cypress intentionally runs inside the browser automation boundary. Selecting through the input is the supported route.
If force makes a disabled control accept a file, the test may hide a real defect. Assert the form is enabled and ready before selection.
6. Drag and Drop One or More Files
Pass action: 'drag-drop' to simulate the browser drag sequence on a drop target.
it('drops an image into the editor', () => {
cy.intercept('POST', '/api/editor/assets').as('uploadAsset')
cy.visit('/editor/new')
cy.get('[data-cy="asset-dropzone"]').selectFile(
{
contents: 'cypress/fixtures/images/diagram.png',
fileName: 'architecture-diagram.png',
mimeType: 'image/png',
},
{ action: 'drag-drop' },
)
cy.wait('@uploadAsset')
cy.contains('[data-cy="asset-card"]', 'architecture-diagram.png')
.should('be.visible')
})
A descriptor can read contents from a path while overriding the presented filename. This is useful when several cases can reuse identical safe bytes.
For multiple drops:
cy.get('[data-cy="asset-dropzone"]').selectFile(
[
'cypress/fixtures/images/diagram.png',
'cypress/fixtures/images/screenshot.png',
],
{ action: 'drag-drop' },
)
cy.get('[data-cy="asset-card"]').should('have.length', 2)
Unlike the default input selection, a drag target is not required to be an <input type="file">. It must, however, be the element that handles the application's drag events. Some applications listen on document, for which cy.document().selectFile(file, { action: 'drag-drop' }) is supported.
Assert the drop result, not internal drag-event counters. Component-level tests are better for exact hover classes and event callback sequences.
7. Reuse a Raw Fixture Through an Alias
Selecting a path is the simplest method for files already on disk. An alias is useful when the test needs raw bytes for another assertion or needs to modify content before selection.
Use null encoding to preserve bytes:
beforeEach(() => {
cy.fixture('documents/sample.pdf', null).as('samplePdf')
})
it('selects binary fixture bytes', () => {
cy.visit('/documents/new')
cy.get('input[name="document"]').selectFile({
contents: '@samplePdf',
fileName: 'sample.pdf',
mimeType: 'application/pdf',
})
cy.contains('sample.pdf').should('be.visible')
})
Current .selectFile() accepts aliases that resolve to file contents. The explicit metadata prevents the alias name from becoming the user-facing filename.
Do not load very large files this way. cy.fixture() reads, transfers, and caches the content. Passing the project-relative file path directly to .selectFile() avoids that extra fixture workflow.
For JSON, default cy.fixture() parsing can change formatting when the object is re-encoded. Use null when exact original bytes matter. If the scenario is about JSON semantics rather than byte formatting, generate a clear object with JSON.stringify().
The Cypress cy.fixture guide covers encoding, caching, and fixture organization in more depth.
8. Assert Client-Side Type Rejection
This example verifies that the UI blocks an unsupported selection and shows a useful message.
it('rejects a text file when only PDFs are supported', () => {
cy.intercept('POST', '/api/resumes').as('uploadResume')
cy.visit('/resumes/new')
cy.get('input[name="resume"]').selectFile({
contents: Cypress.Buffer.from('plain text is not a PDF'),
fileName: 'notes.txt',
mimeType: 'text/plain',
})
cy.get('[role="alert"]')
.should('contain.text', 'Choose a PDF file')
cy.contains('button', 'Upload').should('be.disabled')
})
The disabled button is a stronger completion signal than an immediate negative request assertion. It shows the application finished validation and cannot submit. A direct service test must still prove that bypassing client validation does not let an invalid file through.
Test recovery in the same user journey:
cy.get('input[name="resume"]')
.selectFile('cypress/fixtures/documents/sample.pdf')
cy.get('[role="alert"]').should('not.exist')
cy.contains('button', 'Upload').should('be.enabled')
Use a truly valid PDF fixture for the replacement. Renaming text to .pdf only tests filename logic and can cause the server to reject the second step for the right reason.
Client and server messages may differ. Assert customer-facing language at the UI and machine-readable error codes in API tests.
9. Stub Server Rejection and Verify Recovery
A deterministic stub lets you exercise rare server failures without corrupting fixture data.
it('recovers from an expired upload session', () => {
cy.intercept(
{ method: 'POST', url: '/api/uploads', times: 1 },
{
statusCode: 403,
body: {
code: 'UPLOAD_SESSION_EXPIRED',
message: 'Create a new upload session',
},
},
).as('expiredUpload')
cy.visit('/documents/new')
cy.get('input[name="document"]')
.selectFile('cypress/fixtures/documents/sample.pdf')
cy.wait('@expiredUpload')
cy.contains('The upload session expired').should('be.visible')
cy.contains('button', 'Try again').should('be.visible')
})
If clicking retry requests a new session, define the fallback route before the one-time override because normal overlapping intercepts are evaluated in reverse definition order:
cy.intercept('POST', '/api/uploads', {
statusCode: 201,
body: { id: 'upload-42', status: 'ready' },
}).as('successfulUpload')
cy.intercept(
{ method: 'POST', url: '/api/uploads', times: 1 },
{ statusCode: 503, body: { code: 'TEMPORARY_FAILURE' } },
).as('firstFailure')
Then select, wait for @firstFailure, click retry, wait for @successfulUpload, and assert the completed row. This tests recovery instead of treating the error screen as the final outcome.
A stubbed success does not prove object storage or backend processing. Keep an integrated happy path alongside deterministic frontend error cases.
10. Test Post-Upload Processing
Many uploads return 202 Accepted and process asynchronously. Wait on the status contract rather than sleeping.
it('shows a resume after parsing finishes', () => {
cy.intercept('POST', '/api/resumes').as('createResume')
cy.intercept('GET', '/api/resumes/resume-42').as('getResume')
cy.visit('/resumes/new')
cy.get('input[name="resume"]')
.selectFile('cypress/fixtures/documents/sample.pdf')
cy.wait('@createResume').then(({ response }) => {
expect(response?.statusCode).to.eq(202)
expect(response?.body.id).to.eq('resume-42')
})
cy.wait('@getResume')
cy.contains('Parsing resume').should('be.visible')
cy.wait('@getResume').then(({ response }) => {
expect(response?.body.status).to.eq('ready')
})
cy.contains('Resume ready').should('be.visible')
})
This example assumes the application polls twice. If the number of polls is not stable, do not hardcode two waits. Stub a deterministic sequence for a frontend state test, or assert that the visible status eventually becomes ready within a justified command timeout.
For an integrated test, service processing time can vary. Cypress query assertions retry, so a visible-state assertion may be appropriate if the page polls internally:
cy.contains('[role="status"]', 'Resume ready', { timeout: 30000 })
.should('be.visible')
A 30-second timeout should reflect a documented test-environment service objective, not a random increase made after CI failed. Keep exact parser correctness in service tests and use the E2E case to verify orchestration.
11. Reusable Cypress File Upload Example Helper
A small helper can standardize selection while preserving scenario intent.
type UploadFile = {
path: string
expectedName: string
}
const selectDocument = ({ path, expectedName }: UploadFile) => {
cy.get('input[name="document"]').selectFile(path)
cy.get('[data-cy="selected-file"]').should(
'contain.text',
expectedName,
)
}
it('uploads a standard document', () => {
cy.intercept('POST', '/api/documents').as('uploadDocument')
cy.visit('/documents/new')
selectDocument({
path: 'cypress/fixtures/documents/sample.pdf',
expectedName: 'sample.pdf',
})
cy.contains('button', 'Upload').click()
cy.wait('@uploadDocument')
cy.contains('Upload complete').should('be.visible')
})
Do not hide cy.visit(), intercept registration, submission, and final assertions inside one giant uploadEverything() command. Tests become readable only when their business events remain visible.
For a data matrix, use separate tests so failures identify the exact case:
const rejectedFiles = [
{
name: 'script.txt',
mimeType: 'text/plain',
message: 'Choose a PDF file',
},
{
name: 'empty.pdf',
mimeType: 'application/pdf',
message: 'The file is empty',
},
]
rejectedFiles.forEach(({ name, mimeType, message }) => {
it(`rejects ${name}`, () => {
cy.visit('/resumes/new')
cy.get('input[name="resume"]').selectFile({
contents: Cypress.Buffer.from(''),
fileName: name,
mimeType,
})
cy.get('[role="alert"]').should('contain.text', message)
})
})
The text-file case has empty bytes too, so a real application's validation order could report "empty." Give each row content that isolates one boundary. A matrix is useful only when each case proves one intentional rule.
12. Run Upload Examples Reliably in CI
Linux CI filesystems are commonly case-sensitive. A path that works on a case-insensitive local filesystem can fail when Sample.pdf is referenced as sample.pdf. Match fixture casing exactly and run repository checks on Linux before release.
Do not depend on files outside the checked-out project unless the job creates them deterministically. .selectFile() retries a path until it exists, but that is not a substitute for synchronizing an upstream artifact-generation step.
Keep upload credentials and presigned URLs out of logs. If an intercept response includes a signed storage URL, do not print the full object in custom logging. Limit the test identity to an isolated tenant and bucket.
Parallel jobs need collision-free records. A run identifier can prefix server filenames, while each job cleans up its own records through an API. Do not make tests depend on a shared "latest upload."
Record screenshots and videos for failure diagnosis, but ensure fixtures and visible pages contain only synthetic information. For large suites, retain artifacts according to risk rather than indefinitely.
Finally, run the smallest representative browser matrix across supported browsers. Exact parser boundaries, many file sizes, and extensive malformed inputs belong at faster service layers. The Cypress best practices guide can help keep upload coverage focused and maintainable.
13. Diagnose Common Example Failures
If .selectFile() reports the wrong subject, inspect whether the selector resolves to a wrapper, button, or multiple inputs. The default action requires one file input or its associated label. Drag-and-drop can target a different element.
If the file is missing, check the project-relative path and case. If an alias is undefined, ensure cy.fixture(...).as(...) executes before selection and use null encoding for exact binary bytes.
If the wrong MIME type appears, supply a descriptor with explicit fileName and mimeType. Verify the extension and bytes also match in ordinary success cases.
If an upload alias times out, registration may be late, the method or host may differ, or the app may wait for a Submit click. Inspect the network timeline and match the actual owned endpoint. A service worker or direct storage request can change the path you expected.
If the UI remains pending after a 201 or 202, find the next boundary. The app may poll, consume a web socket event, or wait for a background job. Assert that explicit progression.
If a test passes only with a fixed wait, remove the wait and identify the missing event. Use an intercept alias, status element, or retrying query. Upload systems are asynchronous, but their observable contract should still provide a deterministic signal.
14. Design a Boundary-Focused Example Matrix
A file upload matrix should vary one contract at a time. Start with the dimensions the product actually promises: content type, byte size, filename, file count, authentication, processing result, and recovery. Then assign each case to the fastest layer that can prove it.
| Boundary | Browser example | Faster supporting coverage |
|---|---|---|
| Supported PDF succeeds | Select, submit, show ready record | API contract for stored metadata |
| Unsupported text is rejected | Show specific error and allow replacement | Service matrix for type detection |
| Size limit | Show customer message near the input | API tests at limit minus one, limit, and limit plus one |
| Duplicate upload | Show replace, rename, or reject decision | Storage and service idempotency tests |
| Processing failure | Show failed state and retry action | Worker tests for parser error codes |
| Unauthorized upload | Keep protected action unavailable or show access error | API authorization matrix |
| Multiple selection | Render each queued file and its result | Service batch and concurrency tests |
A table prevents dozens of slow E2E cases from accumulating without a reason. One representative Cypress example can prove the complete success journey, while a few focused error examples prove UI recovery. Exact parser and byte boundaries stay close to the service that enforces them.
Write the expected observable result before the automation. "Upload invalid PDF" is incomplete. "The service rejects a zero-byte PDF, the page displays The file is empty beside that filename, and selecting a valid replacement clears the error" is testable.
Keep dimensions independent. A zero-byte .txt file might violate both type and size. If the intended assertion is unsupported type, give it non-empty text. If the intended assertion is empty content, use an allowed filename and MIME type. Clean isolation makes failures explain one rule.
Review the matrix when product policy changes. Remove obsolete examples, update supporting service coverage, and preserve at least one integrated path that reaches real storage and processing.
15. Verify Accessible Upload Feedback
Upload controls must communicate selection, errors, progress, and completion to people who do not rely on visual styling. Cypress examples can verify this public contract without testing implementation-only CSS.
it('announces upload validation and recovery', () => {
cy.visit('/resumes/new')
cy.get('input[name="resume"]')
.should('have.attr', 'aria-describedby', 'resume-help resume-error')
.selectFile({
contents: Cypress.Buffer.from('not a PDF'),
fileName: 'notes.txt',
mimeType: 'text/plain',
})
cy.get('#resume-error')
.should('have.attr', 'role', 'alert')
.and('contain.text', 'Choose a PDF file')
cy.get('input[name="resume"]')
.selectFile('cypress/fixtures/documents/sample.pdf')
cy.get('#resume-error').should('not.exist')
cy.get('[role="status"]')
.should('contain.text', 'sample.pdf selected')
})
Match the application's chosen accessible pattern. An error may use role="alert", an aria-live region, or a described error element. The test should not require every possible technique, only the documented one.
Verify that the visible label is associated with the native input and that keyboard users can reach the activation control. .selectFile() does not operate the native file dialog, so combine upload selection coverage with an accessibility check for label and focus behavior.
For image previews, assert useful alternative text or an intentionally empty alt when the preview is decorative. For progress, avoid announcing every percentage update if the product contract uses broader queued, uploading, and complete states. Excess announcements can be as harmful as missing feedback.
Accessible assertions make the example more stable because roles, labels, and status semantics describe user intent. They also catch failures that a simple filename text check misses.
16. Review an Upload Example Before Merging
A code review should answer five questions. First, does the fixture isolate the intended rule and contain only synthetic safe data? Second, is the selection method appropriate for the real control: input, associated label, or drag target? Third, are network routes registered before every possible trigger? Fourth, does the test prove a user outcome instead of stopping at input.files? Fifth, can setup and cleanup run safely in retries and parallel CI?
Check selectors against the rendered DOM. A visible "Choose file" button may wrap a hidden input, reference it through a label, or only launch a library component. The example should target the standards-based upload surface and assert the visible component behavior.
Check the endpoint model. A direct multipart request, a presigned storage workflow, and an asynchronous processing job require different aliases. Do not wait on /api/uploads and declare success if the application still needs a storage PUT and completion callback.
Check artifact lifecycle. Uploaded objects need a test tenant, collision-safe identity, and cleanup. A screenshot or video must not reveal a signed URL, customer data, or secret query parameters. Binary fixtures need documented provenance and a reason to remain in the repository.
Finally, execute the focused spec in open mode and the same browser profile used by CI. Inspect the Command Log and network sequence. A passing test that waits on the wrong request is still defective. Review for diagnostic failure messages, then run the example with its surrounding spec to expose shared-state assumptions.
Interview Questions and Answers
Q: Show a basic Cypress file upload example.
Select the project-relative path with .selectFile(), register an intercept before the trigger, submit if needed, wait on the alias, and assert the confirmation or uploaded record.
Q: How do you create a file without a fixture?
Pass a descriptor with contents: Cypress.Buffer.from(text), fileName, and mimeType. Set lastModified when metadata must be deterministic.
Q: Why use null encoding for a binary fixture alias?
It yields raw bytes rather than parsing or re-encoding the file. Exact bytes matter for PDF, image, ZIP, and formatting-sensitive uploads.
Q: How do you assert several upload requests?
Alias the route and retrieve @alias.all after the application completes. Assert the count only if the product sends one request per file.
Q: How do you test asynchronous processing?
Observe creation and status boundaries, then assert the final ready state. Avoid arbitrary sleeps and use a timeout tied to the test service's documented behavior.
Q: Should a helper perform the complete upload flow?
Usually no. A helper can select a file and assert local selection, while visit, network setup, submission, and business outcome remain visible in the test.
Q: What changes for CI?
Use exact case-sensitive paths, deterministic project files, isolated records, least-privilege credentials, sanitized artifacts, and cleanup that works across parallel jobs.
Common Mistakes
- Resolving paths from the spec directory rather than the Cypress project root.
- Omitting metadata for generated bytes and then asserting inferred values.
- Loading large binary files into aliases when a direct path is sufficient.
- Registering the request alias after
.selectFile()starts an auto-upload. - Chaining subject-dependent commands after
.selectFile()instead of re-querying. - Asserting only the browser
FileListfor a scenario named "uploads." - Forcing a hidden or disabled control without verifying it is the intended input.
- Assuming every multi-file UI sends one request per file.
- Using fixed waits for background processing.
- Building a data matrix whose rows accidentally violate several rules.
- Hiding all business actions inside a custom command.
- Leaving parallel-run uploads in a shared environment.
Conclusion
A useful cypress file upload example is short, observable, and matched to one product boundary. Use a direct path for stable files, descriptors for generated content, arrays for multiple files, aliases for reusable raw bytes, and drag-drop action for drop zones.
Copy the closest example, replace placeholder routes and selectors, and add the final assertion that matters to your users. Keep exact byte and validation matrices in service tests, while Cypress verifies the complete browser workflow and recovery behavior.
Interview Questions and Answers
Walk through a reliable Cypress upload test.
I register the owned request aliases, visit the page, select a project-relative fixture, submit if required, inspect the response, and assert the final visible record. This covers selection, transport, and UI handling without fixed waits.
How do you test an in-memory file?
I create bytes with Cypress.Buffer.from() and pass a descriptor with fileName and mimeType to selectFile. I use a fixed lastModified only when metadata is part of the contract.
Why re-query after selectFile?
Cypress yields the original subject but documents it as unsafe for later subject-dependent chaining. Re-querying or asserting application state avoids stale-subject assumptions.
How do you handle multiple upload calls?
I alias the route and inspect @alias.all when the implementation intentionally sends one call per file. If the app sends one batch request, I assert one call and the resulting records instead.
How do you test a presigned upload workflow?
I observe the initialization request, storage upload, and completion notification as separate boundaries. I stub third-party storage only through an owned contract for deterministic UI cases and retain one integrated path.
How do you make upload examples stable in CI?
I use exact project-relative casing, checked-in or deterministically generated files, isolated run identifiers, restricted credentials, and explicit cleanup. I replace sleeps with network or status signals.
What belongs outside the Cypress upload matrix?
Exact byte boundaries, large malformed corpora, parser correctness, malware scanning, and sustained throughput belong mainly in service, security, or performance layers. Cypress keeps representative end-to-end behavior.
Frequently Asked Questions
What is a simple Cypress file upload example?
Call cy.get('input[type=file]').selectFile('cypress/fixtures/file.pdf'), submit if the app requires it, wait on a pre-registered upload alias, and assert the completion state.
How do I upload a generated CSV in Cypress?
Build the CSV string, convert it with Cypress.Buffer.from(), and pass a descriptor containing contents, fileName, and mimeType to .selectFile().
How do I check the selected filename?
Re-query the input and inspect $input[0].files[0].name, or assert the application's visible selected-file label. The latter also verifies rendering behavior.
Can selectFile use a fixture alias?
Yes. Load binary fixtures with null encoding, assign an alias, and pass that alias as contents or directly when appropriate. Prefer a direct path for large files.
How do I test server upload errors?
Stub the upload endpoint with a representative status and machine-readable error, then assert the customer-facing message and recovery action. Keep separate integrated coverage for real enforcement.
How do I wait for file processing in Cypress?
Observe the processing status request or use a retrying visible-state assertion with a justified timeout. Do not use a fixed sleep for parsing or scanning.
Why does an upload test pass locally but fail in Linux CI?
Check case-sensitive fixture paths, missing generated files, shared server records, and environment-specific endpoints. Linux commonly reveals filename case mismatches hidden by local filesystems.