2

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'

2 Answers2

3

You can set the screenshots folder in configuration and save yourself a lot of hassle.

In cypress.config.js

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: 'http://localhost:1234',
  },
  screenshotsFolder: 'cypress\myscreenshots',
})

That way you keep the spec/test sub-path and don't get any nasty surprises when one screenshot over-rides the other.

Hogan
  • 35
  • 6
  • hello Hogan, I also tried this but it is not working on my end. thank you for your help – Christian Rudolf Tan May 16 '23 at 00:56
  • You're welcome. I misread the question - the way Tonya does it makes much more sense now. The EPERM error is indeed caused because you tried to make the folder first, but it's unnecessary since Cypress does that for you. – Hogan May 16 '23 at 11:02
  • Hello Hogan, actually I need to create the folder first. because cypress will return an error path not found for the folder path given. – Christian Rudolf Tan May 18 '23 at 04:36
3

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()
  • Hello thank you for answering my question. actually. the goal is to have a custom filename save in a different directory. i came across in the net while looking for the solution. on how to format the filename of the screenshot. and I found this. 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}' – Christian Rudolf Tan May 16 '23 at 00:53
  • i added this line of code: if (!fs.existsSync(newPath)) {fs.mkdirSync(newPath, {recursive: true });} since it will return an error about path not found. and it is working now. thank you very much! – Christian Rudolf Tan May 17 '23 at 01:04