• Stars
    star
    498
  • Rank 88,494 (Top 2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

File upload testing made easy

cypress-file-upload

GitHub license npm version build All Contributors monthly downloads downloads all time

File upload testing made easy.

This package adds a custom Cypress command that allows you to make an abstraction on how exactly you upload files through HTML controls and focus on testing user workflows.

Table of Contents

Installation

The package is distributed via npm and should be installed as one of your project's devDependencies:

npm install --save-dev cypress-file-upload

If you are using TypeScript, ensure your tsconfig.json contains commands' types:

"compilerOptions": {
  "types": ["cypress", "cypress-file-upload"]
}

To be able to use any custom command you need to add it to cypress/support/commands.js like this:

import 'cypress-file-upload';

Then, make sure this commands.js is imported in cypress/support/index.js (it might be commented):

// Import commands.js using ES2015 syntax:
import './commands';

All set now! ๐Ÿ’ฅ

Usage

Now, let's see how we can actually test something. Exposed command has signature like:

cySubject.attachFile(fixture, optionalProcessingConfig);

It is a common practice to put all the files required for Cypress tests inside cypress/fixtures folder and call them as fixtures (or a fixture). The command recognizes cy.fixture format, so usually this is just a file name.

HTML5 file input

cy.get('[data-cy="file-input"]')
  .attachFile('myfixture.json');

Drag-n-drop component

cy.get('[data-cy="dropzone"]')
  .attachFile('myfixture.json', { subjectType: 'drag-n-drop' });

Attaching multiple files

cy.get('[data-cy="file-input"]')
  .attachFile(['myfixture1.json', 'myfixture2.json']);

Note: in previous version you could also attach it chaining the command. It brought flaky behavior with redundant multiple event triggers, and was generally unstable. It might be still working, but make sure to use array instead.

Working with file encodings

In some cases you might need more than just plain JSON cy.fixture. If your file extension is supported out of the box, it should all be just fine.

In case your file comes from some 3rd-party tool, or you already observed some errors in console, you likely need to tell Cypress how to treat your fixture file.

cy.get('[data-cy="file-input"]')
  .attachFile({ filePath: 'test.shp', encoding: 'utf-8' });

Trying to upload a file that does not supported by Cypress by default? Make sure you pass encoding property (see API).

Working with raw file contents

Normally you do not need this. But what the heck is normal anyways :neckbeard:

If you need some custom file preprocessing, you can pass the raw file content:

const special = 'file.spss';

cy.fixture(special, 'binary')
  .then(Cypress.Blob.binaryStringToBlob)
  .then(fileContent => {
    cy.get('[data-cy="file-input"]').attachFile({
      fileContent,
      filePath: special,
      encoding: 'utf-8',
      lastModified: new Date().getTime()
    });
  });

You still need to provide filePath in order to get file's metadata and encoding. For sure this is optional, and you can do it manually:

cy.fixture('file.spss', 'binary')
  .then(Cypress.Blob.binaryStringToBlob)
  .then(fileContent => {
    cy.get('[data-cy="file-input"]').attachFile({
      fileContent,
      fileName: 'whatever',
      mimeType: 'application/octet-stream',
      encoding: 'utf-8',
      lastModified: new Date().getTime(),
    });
  });

Override the file name

cy.get('[data-cy="file-input"]')
  .attachFile({ filePath: 'myfixture.json', fileName: 'customFileName.json' });

Working with empty fixture file

Normally you have to provide non-empty fixture file to test something. If your case isn't normal in that sense, here is the code snippet for you:

cy.get('[data-cy="file-input"]')
  .attachFile({ filePath: 'empty.txt', allowEmpty: true });

Waiting for the upload to complete

Cypress' cy.wait command allows you to pause code execution until some asyncronous action is finished. In case you are testing file upload, you might want to wait until the upload is complete:

// start watching the POST requests
cy.server({ method:'POST' });
// and in particular the one with 'upload_endpoint' in the URL
cy.route({
  method: 'POST',
  url: /upload_endpoint/
}).as('upload');


const fileName = 'upload_1.xlsx';

cy.fixture(fileName, 'binary')
    .then(Cypress.Blob.binaryStringToBlob)
    .then(fileContent => {
      cy.get('#input_upload_file').attachFile({
        fileContent,
        fileName,
        mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        encoding:'utf8',
        lastModified: new Date().getTime()
      })
    })

// wait for the 'upload_endpoint' request, and leave a 2 minutes delay before throwing an error
cy.wait('@upload', { requestTimeout: 120000 });

// stop watching requests
cy.server({ enable: false })

// keep testing the app
// e.g. cy.get('.link_file[aria-label="upload_1"]').contains('(xlsx)');

I wanna see some real-world examples

There is a set of recipes that demonstrates some framework setups along with different test cases. Make sure to check it out when in doubt.

API

Exposed command in a nutshell:

cySubject.attachFile(fixture, processingOpts);

Familiar with TypeScript? It might be easier for you to just look at type definitions.

fixture can be a string path (or array of those), or object (or array of those) that represents your local fixture file and contains following properties:

  • {string} filePath - file path (with extension)
  • {string} fileName - the name of the file to be attached, this allows to override the name provided by filePath
  • {Blob} fileContent - the binary content of the file to be attached
  • {string} mimeType - file MIME type. By default, it gets resolved automatically based on file extension. Learn more about mime
  • {string} encoding - normally cy.fixture resolves encoding automatically, but in case it cannot be determined you can provide it manually. For a list of allowed encodings, see here
  • {number} lastModified - The unix timestamp of the lastModified value for the file. Defaults to current time. Can be generated from new Date().getTime() or Date.now()

processingOpts contains following properties:

  • {string} subjectType - target (aka subject) element kind: 'drag-n-drop' component or plain HTML 'input' element. Defaults to 'input'
  • {boolean} force - same as for cy.trigger, it enforces the event triggers on HTML subject element. Usually this is necessary when you use hidden HTML controls for your file upload. Defaults to false
  • {boolean} allowEmpty - when true, do not throw an error if fileContent is zero length. Defaults to false

Recipes

There is a set of recipes that demonstrates some framework setups along with different test cases. Make sure to check it out when in doubt.

Any contributions are welcome!

Caveats

During the lifetime plugin faced some issues you might need to be aware of:

  • Chrome 73 changes related to HTML file input behavior: #34
  • Force event triggering (same as for cy.trigger) should happen when you use hidden HTML controls: #41
  • Binary fixture has a workarounded encoding: #70
  • Video fixture has a workarounded encoding: #136
  • XML encoded files: #209
  • Shadow DOM compatibility: #74
  • Reading file content after upload: #104

It isn't working! What else can I try?

Here is step-by-step guide:

  1. Check Caveats - maybe there is a tricky thing about exactly your setup
  2. Submit the issue and let us know about you problem
  3. In case you're using a file with encoding and/or extension that is not yet supported by Cypress, make sure you've tried to explicitly set the encoding property (see API)
  4. Comment your issue describing what happened after you've set the encoding

I want to contribute

You have an idea of improvement, or some bugfix, or even a small typo fix? That's ๐Ÿ†’

We really appreciate that and try to share ideas and best practices. Make sure to check out CONTRIBUTING.md before start!

Have something on your mind? Drop an issue or a message in Discussions.

Contributors

Thanks goes to these wonderful people (emoji key):


James Hollowell

๐Ÿ’ป

lunxiao

๐Ÿ›

Oliver O'Donnell

๐Ÿ› ๐Ÿ’ป

Peter Colapietro

๐Ÿ“–

km333

๐Ÿ›

Kevin Mui

๐Ÿ’ป ๐Ÿค” ๐Ÿ‘€

Ben Wurth

๐Ÿ› ๐Ÿ’ป

Andreev Sergey

โš ๏ธ ๐Ÿ’ฌ ๐Ÿ’ก ๐Ÿ’ป

Guts

๐Ÿ’ฌ

maple-leaf

๐Ÿ’ฌ ๐Ÿ’ป

Daniel Mendalka

๐Ÿ’ฌ

Chris Sargent

๐Ÿ’ฌ

Ronak Chovatiya

๐Ÿ’ฌ

Jan Hesters

๐Ÿ’ฌ ๐Ÿ›

John Molakvoรฆ

๐Ÿ’ฌ

Phil Jones

๐Ÿ›

Nicolas Gehring

๐Ÿ›

David Pertiller

๐Ÿ’ฌ ๐Ÿ’ป

Amy

๐Ÿ›

Tomasz Szymczyszyn

๐Ÿ“–

nitzel

๐Ÿ’ป

dirk

๐Ÿค”

Addie Morrison

๐Ÿ›

Alec Brunelle

๐Ÿ›

Gleb Bahmutov

๐Ÿค”

Jesse de Bruijne

๐Ÿ“–

Justin Littman

๐Ÿ’ฌ

harrison9149

๐Ÿ›

jdcl32

๐Ÿ’ฌ ๐Ÿ’ป

David Sheldrick

๐Ÿ’ป

Tom MacWright

๐Ÿ’ป

Andrew Hoddinott

๐Ÿ’ป

Eneko Rodrรญguez

๐Ÿ’ป

Dmitry Nikulin

๐Ÿ’ป

Thiago Brezinski

๐Ÿ›

Jack

๐Ÿ’ฌ

Yoni Gibbs

๐Ÿ›

benowenssonos

๐Ÿ›

Aymeric

๐Ÿ’ฌ

Alfredo Sumaran

๐Ÿ›

x-yuri

๐Ÿค”

Tri Q. Tran

๐Ÿ’ป

Francis Chartrand

๐Ÿ“–

Emil Ong

๐Ÿ’ป

Evgenii

๐Ÿ“–

Joseph Zidell

๐Ÿšง

Daniel Caballero

๐Ÿ’ป

Adrien Joly

๐Ÿ“–

Jayson Harshbarger

๐Ÿ’ป

Andrico

๐Ÿ“–

Paul Blyth

๐Ÿ’ป

Justin Bennett

๐Ÿ“–

Shafiq Jetha

๐Ÿ“–

tt rt

๐Ÿ’ป

Ed Hollinghurst

๐Ÿ“–

anark

โš ๏ธ ๐Ÿ’ป

Michael Altamirano

๐Ÿ’ป ๐Ÿ’ฌ

This project follows the all-contributors specification. Contributions of any kind welcome!

License

MIT