
Intermediate
This post covers an in-depth solution for Cypress. Decent knowledge of JavaScript and ES6+ is recommended.
Cypress is an end-to-end testing framework designed to provide coverage for front-end UIs. Learning how to automate tests has its learning curve, but the benefits grow with your application’s complexity.
I was tasked to test a process that involves uploading multiple types of files. In particular, application/pdf
and image/*
mime-type files. Doing this with Cypress seems simple enough, but at the time of this article, Cypress does not officially support native file uploads.
I could get into why this process isn’t natively supported, but that’s a topic for another post. Since Cypress has limited functions for this, I had to do some good old Google searching.
I came across this Stack Overflow post. Let’s go over the solution.
The Solution
I implemented the solution like this:
const uploadFile = (fileName, fileType = '', selector) => { cy.get(selector).then(subject => { cy.fixture(fileName, 'base64') .then(Cypress.Blob.base64StringToBlob) .then(blob => { const el = subject[0] const testFile = new File([blob], fileName, { type: fileType }) const dataTransfer = new DataTransfer() dataTransfer.items.add(testFile) el.files = dataTransfer.files console.log(el.files) }) })
You can also add it as a Cypress command, but I already had a utils.js
file. This code will open an image fixture, read the file and translate it to base64
. It also utilizes the DataTransfer
API, which is supported natively. No need for external libraries.
Image fixtures must be stored in the fixtures
folder. I had an /assets/
folder where I stored test images and PDFs.
This should read the file, and give you a FileList
object. To confirm this, run console.log(el.files)
like in the example above.
Forcing UI input change
Some of us need the UI to know that the file has been uploaded. Often times, there is a UI indicator that tells the user the upload was successful. In my case, my JavaScript code relied on the UI state reflecting multiple successful file uploads.
You can force a change event on the file object like this:
// force input event so UI state updates cy.get(selector).trigger('change', { force: true })
Add this under the function to force the UI to reflect an uploaded file.
Using this helper in your tests
This function is mostly standalone, as long as you don’t need any extra info or logic. Here’s an example Cypress test:
import utils from './your-utils-file' const fileName = 'images/fileName.png' const successPrompt = 'Your file has been uploaded!' it('Completes file upload', () => { utils.uploadFile(fileName, 'image/png', '#file-upload') cy.get('#success-message').contains(successPrompt) })
That’s pretty much it! Finding the solution wasn’t easy, and it took me hours of research to find something that worked the way I wanted it to, so I hope this at least gets you part of the way there. Hopefully in the future Cypress will make this whole process easier. For now, this is the most straightforward method I’ve come across.
Thanks for the post. What would i put in the file type for an excel file?
You can view a list of file types and extensions here. For Excel, it looks like it’s application/vnd.ms-excel
Thank you for this. I was trying to use cypress-file-upload, but it was uploading corrupt JPEGs. Your code worked perfectly in Cypress 5.3.0.
What about if you were hitting an endpoint? How can you trigger a file upload?
Awesome – thanks a lot for this
Note that as of 9.3.0 this is natively supported via cy.selectFile
Good to know that they fixed this!