skip to Main Content

How can add wait in following cy.task() functions like validateZipFile and getZipFileSize in the in the cypress.config.js file ?

test.spec.js

cy.get('button[type="submit"]').contains("Download").click({force:true});
helperFunctions.validateZip("Booking Results Resource Pack - Task");

// helperFunctions.js

validateZip (text) {
    const dfilename = text.replace(/-|_|s/g,"");
    const downloadedFilename = dfilename+"ZipFile.zip";
   
    cy.task('getZipFileSize', downloadedFilename)
    .should('eq', 6149237)

    cy.task('validateZipFile', downloadedFilename)
    .should('deep.eq', [
      '__Booking-Resource__Question-Cards__Booking_BW.pdf', 
      '__Booking-Resource__Question-Cards__Booking-Colour.pdf'
    ]);
    
  }

  

cypress.config.js

    const AdmZip = require("adm-zip");
    
    const { defineConfig } = require('cypress')
    
    module.exports = defineConfig({
       e2e: {
        setupNodeEvents(on, config) {
          .....
    
    
            on('task', {
            validateZipFile: filename => {
              const downloadsFolder = config.downloadsFolder
              return  validateZipFile(path.join(downloadsFolder, filename))
            },
            getZipFileSize: filename => {
              const downloadsFolder = config.downloadsFolder
              const stats = fs.statSync(path.join(downloadsFolder, filename))
              return stats.size
            }
          });
    
          return config;
        },
        baseUrl: "https://staging-qa.someurl.com/",
        specPattern: "cypress/e2e/**/*.spec.{js,jsx,ts,tsx}",
      },
    })

 function validateZipFile(filename) {
  const zip = new AdmZip(filename)
  const zipEntries = zip.getEntries()
  const names = zipEntries.map(entry => entry.entryName).sort()
  return names
}

2

Answers


  1. Assuming there is nothing on the web page to indicate the download has finished:

    There is this article by Yevhen Laichenkov from Kiev, Cypress: How to verify that file is downloaded with cy-verify-downloads.

    He mentions the memory issues with large files and cy.readFile(), has built a library cy-verify-downloads.

    cypress.config.js

    const { verifyDownloadTasks } = require('cy-verify-downloads');
    
    module.exports = defineConfig({
      e2e: {
        setupNodeEvents(on, config) {
          on('task', verifyDownloadTasks);
        },
      },
    })
    

    helper

    require('cy-verify-downloads').addCustomCommand()
    
    validateZip (text) {
        const dfilename = text.replace(/-|_|s/g,"");
        const downloadedFilename = dfilename+"ZipFile.zip";
       
        cy.verifyDownload(downloadedFilename );
    
        cy.task('getZipFileSize', downloadedFilename)
          .should('eq', 6149237)
    
        cy.task('validateZipFile', downloadedFilename)
          .should('deep.eq', [
            '__Booking-Resource__Question-Cards__Booking_BW.pdf', 
            '__Booking-Resource__Question-Cards__Booking-Colour.pdf'
          ]);
      }
    

    Test (no change)

    cy.get('button[type="submit"]').contains("Download").click({force:true});
    helperFunctions.validateZip("Booking Results Resource Pack - Task");
    

    Polling for the download

    You can build a task based on poll nodejs library.

    This method allows you to

    • configure timeout from the test
    • return true/false from the task and use the result in a should
    const fs = require('fs')
    const poll = require('poll')
    
    module.exports = defineConfig({
      e2e: {
        setupNodeEvents(on, config) {
          on('task', {
    
            poll: async ({filename, timeout = 30_000}) => {
              const {poll} = await import('poll')
    
              const filepath = path.resolve(config.downloadsFolder, filename)
              let fileExists = false;
              let stopPolling = false
              setTimeout(() => stopPolling = true, timeout)
    
              return new Promise(resolve => {
                const shouldStopPolling = () => {
                  if (stopPolling) resolve(false)
                  return stopPolling || fileExists
                }
                poll(
                  () => { 
                    fileExists = fs.existsSync(filepath) 
                    if (fileExists) resolve(true)
                  }, 
                  100,                                   // check every 100ms
                  shouldStopPolling
                )
              })
            },
          });
        },
      },
    })
    

    Test

    cy.get('button[type="submit"]').contains("Download").click({force:true});
    
    cy.task('poll', {filename, timeout: 30_000})
      .should('eq' true)
    
    helperFunctions.validateZip("Booking Results Resource Pack - Task");
    

    Testing the poll task

    To check the above task I added a dummy file test1.pdf to downloads, then ran a test that checked for that file and also for a non-existent file test2.pdf

    it('tests the poll task', () => {
    
      // The file test1.pdf exists in the downloads folder
      // but file test2.pdf does not
      
      cy.task('poll', {filename: 'test1.pdf'})            // returns immediately
        .should('eq', true)
      
      cy.task('poll', {filename: 'test2.pdf', timeout: 5000})   // after 5s
        .should('eq', false)
    })
    

    Equivalent plain-javascript task

    This is the same task but using plain javascript

    const fs = require('fs')
    
    module.exports = defineConfig({
      e2e: {
        setupNodeEvents(on, config) {
          on('task', {
    
            waitForDownload: ({filename, timeout= 30_000}) => {
              const filepath = path.resolve(config.downloadsFolder, filename)
              return new Promise(resolve => {
                let stopPolling = false
                setTimeout(() => stopPolling = true, timeout)
    
                function check() {
                  if (fs.existsSync(filepath)) {
                    resolve(true)
                  } else {
                    if (stopPolling) {
                      resolve(false)
                    } else {
                      setTimeout(() => check(), 100)  // check again after 100ms
                    }
                  }
                }
                check()
              })
            }
          });
        },
      },
    })
    
    Login or Signup to reply.
  2. You can avoid using 3-rd party libs by creating a function like this and invoking it at the start of your tasks:

    const fs = require('fs');
    
    const sleep = time => new Promise(res => {
        setTimeout(res, time);
    });
    
    const verifyFileDownloaded = async filePath => {
        const MAX_POLLS = 10;
        const POLL_INTERVAL = 1000;
        
        for (let i = 0; i < MAX_POLLS; i++) {
            if (fs.existsSync(filePath)) {
                return;
            }
    
            await sleep(POLL_INTERVAL);
        }
    
        throw new Error(`Failed to detect the file at the path "${filePath}" within the timeout of ${POLL_INTERVAL / 1000 * MAX_POLLS} seconds`);
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search