Upload Your Images To Cypress Cloud

How to send your own images to be stored on Cypress Dashboard.

If a Cypress test fails and you are recording the test run on Cypress Dashboard Cloud, the screenshot image is uploaded and stored with the test run. The screenshot and the video are stored with the test and allow debugging the failure. You can also send screenshots of the entire test runner, the application, or individual elements to the cloud using the built-in cy.screenshot command. In this blog post, I will show how to upload your own images instead of the page screenshots.

🎁 You can find my example project in the repo bahmutov/cypress-image-to-cloud and its recorded test runs at Cypress Cloud project.

Solution 1: temporary image element

Imagine we take an image "hello.png" below and want to upload it to the Cloud.

Our test image to be uploaded

Our first solution will read the image contents and then attaches it to the document using a temporary <img src="data..." element. We can use the bundled jQuery under Cypress.$ to write the test.

cypress/e2e/spec.cy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
it('stores an image in Cypress cloud', () => {
cy.visit('index.html')
cy.get('h1').should('be.visible')
// let's take an existing image "hello.png"
// and store it with the recording of this test
// on Cypress cloud
cy.readFile('hello.png', 'base64').then((base64) => {
cy.document().then((doc) => {
Cypress.$(
`<img id="temp-image" src="data:image/png;base64,${base64}" />`,
).appendTo(doc.body)
})
cy.get('#temp-image')
.should('be.visible')
.screenshot('hello', { overwrite: true })
.invoke('remove')
})
})

The <img> appears and disappears, but its element screenshot is uploaded by the cy..screenshot('hello', { overwrite: true }) command

Image hello is uploaded as a screenshot of the temporary HTML IMG element

Solution 2: afterScreenshot Node API

A better solution in my opinion can avoid creating an image element just to upload it. We can use Cypress after screenshot API to "replace" the screenshot taken with a path to our image. It does not matter what screenshot we take. I like using the entire runner because it is faster since it avoids taking multiple screenshots to be stitched together.

cypress.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const { defineConfig } = require('cypress')

module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// https://on.cypress.io/after-screenshot-api
on('after:screenshot', (details) => {
console.log('details')
console.log(details)
if (details.name === 'spec2-hello') {
console.log('uploading hello.png image instead')
return { path: 'hello.png' }
}
})
},
},
})

The corresponding spec needs to take a screenshot named "spec2-hello" to trigger our Hello image upload

cypress/e2e/spec2.cy.js
1
2
3
4
5
6
7
8
it('stores an image in Cypress cloud by returning path', () => {
cy.visit('index.html')
cy.get('h1').should('be.visible')
cy.screenshot('spec2-hello', {
capture: 'runner',
overwrite: true,
})
})

The details of the image are shown in the terminal output

Uploading our image by returning its path from the after screenshot callback

Note: each cy.screenshot command provides onAfterScreenshot callback, but you cannot replace the image path in that callback, unfortunately.

The Cloud screenshots

You can find the results of recording these tests on the project's Cloud page. Go to the "Specs" view and click on the screenshots thumbnail buttons.

Recorded test run screenshots

Click on the screenshot button to see your image.

Our Hello image stored on the Cypress Cloud as a screenshot

Hope you find some use for this trick.