cypress: Add support to get downloaded file when its name is not known.

It would be useful to have an option to parse downloaded files when their name is not known at the moment of download. I tried to create a helper function (task) for that which returns the latest file, however, it’s not reliable as it’s executed when file is not there yet:

// plugins/index.js
const getLastDownloadFilePath = () => {
  const dirPath = 'cypress/downloads';
  const filesOrdered = readdirSync(dirPath)
    .map(entry => path.join(dirPath, entry))
    .filter(entryWithPath => lstatSync(entryWithPath).isFile())
    .map(fileName => ({ fileName, mtime: lstatSync(fileName).mtime }))
    .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());

  return filesOrdered.length ? filesOrdered[0].fileName : false;
};

I believe it may be useful to have the ability to:

  1. wait for file download,
  2. get the name of the downloaded file.

These two should allow handling such cases.

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 2
  • Comments: 21 (5 by maintainers)

Most upvoted comments

There’s an example of reading a file directly after download by looking for extension. Is this helpful? https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/testing-dom__download/cypress/integration/spec.js#L263

@viktorgogulenko thanks for that, that should work when the file is generated in the backend and later sent in the response. Unfortunately, in my case, the file is generated by frontend code with file-saver library. Therefore there’s no additional request with a filename in it.

I managed to create a workaround with:

// plugins/index.js
const path = require('path');
const { existsSync, readdirSync, lstatSync } = require('fs');

const downloadsDirPath = 'cypress/downloads';

const getLastDownloadFilePath = () => {
  if (!existsSync(downloadsDirPath)) {
    return null;
  }

  const filesOrdered = readdirSync(downloadsDirPath)
    .map(entry => path.join(downloadsDirPath, entry))
    .filter(entryWithPath => lstatSync(entryWithPath).isFile())
    .map(fileName => ({ fileName, mtime: lstatSync(fileName).mtime }))
    .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());

  if (!filesOrdered.length) {
    return null;
  }

  // TODO: this works only for chrome family browsers
  if (filesOrdered[0].fileName.indexOf('crdownload') > -1) {
    return null;
  }

  return filesOrdered[0].fileName;
};

and in test, I use it with wait-until plugin:

// integration/test.spec.js
cy.fixture('expectedFile').then(expectedFile => cy
  .waitUntil(() => cy
    .task('getLastDownloadFilePath')
    .then(result => result),
  { timeout: 3000, interval: 100 })
  .then(filePath => {
    cy.readFile(filePath).should(actualFile => {
      // assertion goes here
    });
  })
);

I solved this issue with really great feature of Cypress - interceptor. Filename you can automatically grab from ‘content-disposition’ response header. Screenshot 2021-03-26 at 11 59 33

Here is a code snippet:

    // Prepare interceptor to get response after click on download button
    cy.intercept('file').as('incidentPdfReport');
    // Click on download button
    cy.get('[data-test=reportDownload]').click();
    // Interceptor waiting when response will come and file is completely downloaded
    cy.wait('@incidentPdfReport', { timeout: 20000 }).then((res) => {
      const downloadsFolder = Cypress.config('downloadsFolder');
      // grab filename from 'content-disposition' header
      const filename = res.response.headers['content-disposition'].split('filename=')[1];
      const downloadedFilename = path.join(downloadsFolder, filename);
      cy.readFile(downloadedFilename, 'binary', { timeout: 15000 })
        .should((buffer) => {
          expect(buffer.length).to.be.gt(100);
        });
      cy.log(`**File ${filename} exists in downloads folder**`);
      cy.readFile(downloadedFilename).should((text) => {
        const lines = text.split('\n');
        expect(lines).to.have.length.gt(2);
        expect(lines[0]).to.equal('%PDF-1.4');
      });
    });

In this way you don’t need any manipulations with downloads folder like finding the last downloaded file, delete all files before test etc. - you have exact filename and you can do with this file whatever you need. Hopefully it will help you.

There’s an example of reading a file directly after download by looking for extension. Is this helpful? https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/testing-dom__download/cypress/integration/spec.js#L263

Seems that the link is broken