Search code examples
javascriptnode.jscypressadm-zipcypress-task

Validating zip file using adm-zip and cypress throws out of memory error


I was trying to validate zip file contents using adm-zip and cypress, but cypress throws out of memory error. The Zip file could contain .txt or .pdf or .ppt or .docx files. I would like to validate below in the zip file:

   a) no of files
   b) type of files 

"cypress": "v12.8.1",

"adm-zip": "^0.5.10",

"path": "^0.12.7"

Note: Using Adm-zip from this https://www.npmjs.com/package/adm-zip

test.spec.js

  import HelperFunctions from "../../../support/helperFunctions";
  const helperFunctions = new HelperFunctions();
 
 
  it('Download test', function () {
    cy.get('button[type="submit"]').contains("Download").click({force:true});
    helperFunctions.validateZip();
   })

helperFunctions.js

validateZip () {
    const downloadsFolder = Cypress.config('downloadsFolder')
    const downloadedFilename = path.join(downloadsFolder, 'ComprehensionStrategyTeachingResourcePackSummarisingZipFile.zip')
  
    // wait for the file to be fully downloaded by reading it (as binary)
    // and checking its length
    cy.readFile(downloadedFilename, 'binary', { timeout: 35000 }).should('have.length.gt', 300)
  
    // unzipping and validating a zip file requires the direct access to the file system
    // thus it is easier to perform the checks from the `setupNodeEvents` function in the Cypress configuration that runs in Node
    // see the "on('task')" code in the `setupNodeEvents` function to see how we can read and validate a Zip file
    cy.task('validateZipFile', downloadedFilename)
  }

cypress.config.js

const AdmZip = require("adm-zip");

const { defineConfig } = require('cypress')

module.exports = defineConfig({
   e2e: {
    setupNodeEvents(on, config) {
      .....


      on("task", {
        validateZipFile: (filename) => {
          return validateZipFile(filename);
        }, 
      });

      return config;
    },
    baseUrl: "https://staging-qa.someurl.com/",
    specPattern: "cypress/e2e/**/*.spec.{js,jsx,ts,tsx}",
  },
})
    
    function validateZipFile (filename) {
      // now let's validate the downloaded ZIP file
      // Tip: use https://github.com/cthackers/adm-zip to load and unzip the Zip contents
      console.log('loading zip', filename)
    
      const zip = new AdmZip(filename)
      const zipEntries = zip.getEntries()
      const names = zipEntries.map((entry) => entry.entryName).sort()
    
      console.log('zip file %s has entries %o', filename, names)
      // any other validations?
      return null
    }

Solution

  • If the main problem is memory, I would change the cy.readFile() into a task, that way you don't read the whole file into Cypress memory.

    Changes:

    • add a task that uses fs.statSync() to get the file size in nodejs, which should be more memory-friendly than reading the file into Cypress browser memory

    • move path.join() to the task as it's a native nodejs function

    • return a summary of whatever validations are performed (e.g the list of names in the zip) and assert validations in the test

    Other than that, it all looks good.

    cypress.config.js

    const { defineConfig } = require('cypress')
    const AdmZip = require("adm-zip");
    const path = require('path')
    const fs = require('fs')
    
    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
        },
      },
    })
    
    function validateZipFile(filename) {
      const zip = new AdmZip(filename)
      const zipEntries = zip.getEntries()
      const names = zipEntries.map(entry => entry.entryName).sort()
      return names
    }
    

    Test

    function validateZip () {
      const downloadedFilename = 'sample-large-zip-file.zip' 
    
      cy.task('getZipFileSize', downloadedFilename)
        .should('eq', 5216156)
    
      cy.task('validateZipFile', downloadedFilename)
        .should('deep.eq', ['sample-mpg-file.mpg'])
    }
    
    it('Download test', function () {
      cy.get('button[type="submit"]')
        .contains('Download')
        .click({ force: true })
    
      validateZip()
    })