Search code examples
javascripttypescriptcypresscypress-task

Cypress modify screenshot path = Error: EPERM: operation not permitted, rename


I am trying the code found in the cypress document which allows cypress user to modify screenshot path.

I tried the examples they have given but i am having an issue with the permission.

Error: EPERM: operation not permitted, rename 'c:mydocuments/cypress/example/dist.cypress/app/screemshots/login.cy.ts -- my first test (failed).png' -> 'c:/myscreenshots/screenshot.png

https://docs.cypress.io/api/plugins/after-screenshot-api#Usage

system: windows 10 and 11
ide: visual studio code
using NX workspace

// cypress.config.js

{ defineConfig } from 'cypress'
import fs from 'fs'

export default defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {`your text`
      on('after:screenshot', (details) => {
        console.log(details) // print all details to terminal

        const newPath = '/new/path/to/screenshot.png'

        return new Promise((resolve, reject) => {
          // fs.rename moves the file to the existing directory 'new/path/to'
          // and renames the image to 'screenshot.png'
          fs.rename(details.path, newPath, (err) => {
            if (err) return reject(err)

            // because we renamed and moved the image, resolve with the new path
            // so it is accurate in the test results
            resolve({ path: newPath })
          })
        })
      })
    },
  },
})`

My code

const { defineConfig } = require('cypress')
const fs = require('fs')
const { Runnable } = require('mocha')

module.exports = defineConfig({
e2e: {
    setupNodeEvents(on, config) {
        on('after:screenshot', (details) => {
            console.log(details) // print all details to terminal
       const testNmae=Cypress.spec.name.replace(/\.|\/|\s/g,'_')
       const timeStamp = newDate().toISOString().replace(/:/g,'_')
       const filename = '${testname}--${timeStamp} (failed).png'
       const newPath = 'cypress/myscreenshots/${filename}'
    if (!fs.existsSync(newPath)) {fs.mkdirSync(newPath, {recursive: true });}
    return new Promise((resolve, reject) => {
    fs.rename(details.path, newPath, (err) => {if (err) return reject(err)
resolve({ path: newPath })
          })
        })
      })
    },
  },
})

result:

  1. template spec passes: Error: EPERM: operation not permitted, rename 'D:\Cypress\projects\example\cypress\screenshots\spec.cy.js\template spec -- passes (failed).png' -> 'D:\Cypress\projects\example\cypress\myscreenshots'

Solution

  • The error message is due to your call to fs.mkdirSync which is not necessary.

    But with that code, you would not even get that far - the Cypress.spec.name call will give you a different error - so I'm a little confused about inconsistencies in the question.

    In any case

    • Cypress.spec.name can only be used in tests, not in plugin events.
    • the details parameter has a specName property, but it's not passing anything. I assume that's a bug in the Cypress code, since the property exists, it should have the spec name in it.
    • to get around that, add a task to send the spec name into the plugin from the test. After Cypress remedies the issue, you can revert.

    Here is the code entire:

    const { defineConfig } = require("cypress");
    const fs = require('fs')
    
    let specName;   // temporary until bug fix
    
    module.exports = defineConfig({
      e2e: {
        setupNodeEvents(on, config) {
          on('task', {
            sendSpecNameToPlugin: (name) => specName = name 
          })
          on('after:screenshot', (details) => {
            const testname = (details.specName || specName)  // temporary until bug fix
              .replace(/\.|\/|\s/g,'_')
            const timeStamp = new Date().toISOString().replace(/:/g,'_')
            const filename = `${testname}--${timeStamp} (failed).png`
            const newPath = `cypress/myscreenshots/${filename}`
    
            return new Promise((resolve, reject) => {
              fs.rename(details.path, newPath, (err) => {
                if (err) return reject(err)
                resolve({ path: newPath })
              })
            })
          })
        },
      },
    })
    

    The test:

    cy.task('sendSpecNameToPlugin', Cypress.spec.name)
    cy.screenshot()