CI Workflows

Upload strategies

When you run tests and create recordings, they are stored locally. You can opt to upload them automatically or define your own uploading strategy. All uploaded recordings become accessible in the Replay App.


While uploading just failed test is good for saving resources, our recommendation is to upload both failed and passed tests so that you can compare them. This can be really useful for debugging purposes.

Upload failed tests only

By default, all test replays are uploaded no matter the result. If you want to upload only the failed recordings, you can use the filter property in the plugin configuration:

export default defineConfig({
e2e: {
setupNodeEvents(cyOn, config) {
const on = wrapOn(cyOn)
replayPlugin(on, config, {
upload: true,
apiKey: process.env.REPLAY_API_KEY,
filter: function (recording) {
// upload runtime crashes and any failed tests
return (
recording.status === 'crashed' ||
recording.metadata.test.result === 'failed'
)
},
})
return config
},
},
})

Upload failed and flaky Cypress tests

By default, all test replays are uploaded no matter the result. If you want to upload failed and flaky tests, you can use the filter property in the plugin configuration:

export default defineConfig({
e2e: {
setupNodeEvents(cyOn, config) {
const on = wrapOn(cyOn)
replayPlugin(on, config, {
upload: true,
apiKey: process.env.REPLAY_API_KEY,
filter: function (recording) {
// upload runtime crashes and recordings with any tests that failed
return (
recording.status === 'crashed' ||
recording.metadata.test.tests.some(
(test) => test.result === 'failed',
)
)
},
})
return config
},
},
})

Upload only for the primary branch

The recording metadata includes some details about the source control including the repository and branch name which can also be used to filter your uploads. The example below uploads all recordings from the main branch:

export default defineConfig({
e2e: {
setupNodeEvents(cyOn, config) {
const on = wrapOn(cyOn)
replayPlugin(on, config, {
upload: true,
apiKey: process.env.REPLAY_API_KEY,
filter: function (recording) {
return recording.metadata.source.branch === 'main'
},
})
return config
},
},
})

Upload some passing runs

If you've adopted one the configurations above but would also like to periodically upload all replays for a test run, you can add a condition to the filter that returns true for a given test run id. This is only one possible implementation of this approach and you're welcome to adopt others such as using external environment variables.

const convertStringToInt = (string) =>
string.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
export default defineConfig({
e2e: {
setupNodeEvents(cyOn, config) {
const on = wrapOn(cyOn)
replayPlugin(on, config, {
upload: true,
apiKey: process.env.REPLAY_API_KEY,
filter: function (recording) {
// randomly upload 10% of all test runs
if (convertStringToInt(r.metadata.test.run.id) % 10 === 1) {
return true
}
// upload runtime crashes and any failed tests
return (
recording.status === 'crashed' ||
recording.metadata.test.result === 'failed'
)
},
})
return config
},
},
})

Using GitHub Action

.github/workflows/e2e.yml
1name: Replay tests
2on:
3 pull_request:
4 push:
5 branches: [main]
6jobs:
7 cypress-run:
8 runs-on: ubuntu-22.04
9 steps:
10 - name: Checkout
11 uses: actions/checkout@v4
12 - name: Install dependencies
13 run: npm ci
14 - name: Install Replay Chromium
15 run: npx replayio install
16 - name: Run Playwright tests with Replay Browser
17 run: npx playwright test --project replay-chromium --reporter=@replayio/playwright/reporter,line
18 - name: Upload replays
19 if: ${{ always() }}
20 uses: replayio/action-upload@v0.5.1
21 with:
22 api-key: ${{ secrets.REPLAY_API_KEY }}
23 filter: ${{ 'function($v) { $v.metadata.test.result = "failed" }' }}