Attach Cypress test run information to the API calls using `cy.intercept` middleware.
Imagine the application under test is making web calls to the backend. Sometimes things go wrong, and you have set up server-side logging to debug the failures. It would be nice having the test run information attached to each API call the application makes while Cypress is running its tests. In this blog post I will show how to set such information via X-... request headers. We can easily send the following information with each app call:
We can spy and stub application network calls using the cy.request command. I even have an entire course of network testing exercises, but how do you spy on every network call? By using the "middleware" option. For example, let's add the current test title to the outgoing request:
The server should see the two lowercase X-... headers on every Ajax call sent by the application. The Command Log shows the modified outgoing request:
TestRail case ID
If you are using TestRail to manage your test cases, you might be using my plugin cypress-testrail-simple. I like putting the test case ID into the test title:
We can parse the test case and send it in a separate request header:
cypress/support/e2e.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
beforeEach(() => { const currentTestTitle = Cypress.currentTest.title // there might be TestRail case id in the title // try extracting it separately const testRailId = currentTestTitle.match(/(?<caseId>C\d+)/)?.groups?.caseId
If you are using my plugin @bahmutov/cy-grep to filter the tests to run the How To Tag And Run End-to-End Tests, you can send the effective test tags in the request headers. Here is an example spec showing several levels of test tags:
The test "Visible button" has 2 effective test tags: @sanity comes from the test itself. The test tag @static comes from its parent suite "API". The support E2E file can grab the effective test tags
beforeEach(() => { const currentTestTitle = Cypress.currentTest.title // there might be TestRail case id in the title // try extracting it separately const testRailId = currentTestTitle.match(/(?<caseId>C\d+)/)?.groups?.caseId
// the effective test tags are set by the plugin // @bahmutov/cy-grep const testTags = Cypress.env('testTags')
I like running my E2E tests using GitHub Actions. Let's pass CI runtime information too. For simplicity, I will pass the CI runner numbers via CYPRESS_ environment variables.
name:ci on:push jobs: tests: runs-on:ubuntu-22.04 steps: -name:PrintGitHubCIvariables run:npx@bahmutov/print-envGITHUB -name:Checkout uses:actions/checkout@v4 -name:Cypressrun uses:cypress-io/github-action@v6 with: start:npmstart # pass CI run information via Cypress_ variables # to make it readily available in Cypress specs and support files env: CYPRESS_runId:${{github.run_id}} CYPRESS_runNumber:${{github.run_number}} CYPRESS_runAttempt:${{github.run_attempt}} CYPRESS_jobName:${{github.job}}
The support file can grab each CI variable using the Cypress.env command
beforeEach(() => { const currentTestTitle = Cypress.currentTest.title // there might be TestRail case id in the title // try extracting it separately const testRailId = currentTestTitle.match(/(?<caseId>C\d+)/)?.groups?.caseId
// the effective test tags are set by the plugin // @bahmutov/cy-grep const testTags = Cypress.env('testTags')
cy.intercept({ resourceType: /fetch|xhr/ }, (req) => { req.headers['X-Test-Source'] = 'Cypress' req.headers['X-Test-Title'] = currentTestTitle if (testRailId) { req.headers['X-Test-Rail-Id'] = testRailId } if (Cypress.env('runId')) { req.headers['X-Run-Id'] = Cypress.env('runId') } if (Cypress.env('runNumber')) { req.headers['X-Run-Number'] = Cypress.env('runNumber') } if (Cypress.env('runAttempt')) { req.headers['X-Run-Attempt'] = Cypress.env('runAttempt') } if (Cypress.env('jobName')) { req.headers['X-Job-Name'] = Cypress.env('jobName') }