Testing File Uploads With Cypress.io

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.

Comments

Leave a Reply to Anonymous Cancel reply

  • Anonymous

    Posted on

    Thanks for the post. What would i put in the file type for an excel file?

    • brandon parker

      Posted on

      You can view a list of file types and extensions here. For Excel, it looks like it’s application/vnd.ms-excel

  • Anonymous

    Posted on

    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.

  • Anonymous

    Posted on

    What about if you were hitting an endpoint? How can you trigger a file upload?

  • Anonymous

    Posted on

    Awesome – thanks a lot for this

  • Anonymous

    Posted on

    Note that as of 9.3.0 this is natively supported via cy.selectFile

    • brandon parker

      Posted on

      Good to know that they fixed this!