Skip to main content
Cypress AccessibilityPremium Solution

Report Run Checks as GitHub pull request statuses

Run Checks let you encode accessibility thresholds in your App Quality configuration, such as a minimum accessibility score or a maximum number of failing elements, and have Cypress Cloud evaluate them on every recorded run. To surface those results on a pull request, fetch them in CI with getCheckResults() and post one commit status per applicable check.

This guide covers the GitHub workflow for posting Run Check statuses. Similar techniques apply to other version control contexts. For custom policy logic the built-in assertion types don't cover, such as allowlisting known failing rules or comparing against a baseline, use the Results API approach in Block pull requests and set policies.

Install @cypress/extract-cloud-results as described in the Results API guide. Run the status script in the same CI job that recorded the run, after cypress run --record. For the full getCheckResults() response shape and options, see Using check results in CI.

This GitHub Actions workflow records a run, then posts one commit status per applicable Run Check to the pull request. A failed check shows as a failing status on the pull request, and you can make the statuses required in your branch protection rules.

.github/workflows/cypress.yml
name: Cypress
on: pull_request

permissions:
contents: read
statuses: write

jobs:
cypress:
runs-on: ubuntu-latest
env:
CYPRESS_PROJECT_ID: abc123
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
steps:
- uses: actions/checkout@v4
- run: npm ci
# Failing tests fail this step, but the checks still need to be reported.
- run: npx cypress run --record
continue-on-error: true
- run: npm install --force https://cdn.cypress.io/extract-cloud-results/v1/extract-cloud-results.tgz
- run: node scripts/post-check-statuses.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
scripts/post-check-statuses.js
const fs = require('fs')
const { getCheckResults } = require('@cypress/extract-cloud-results')

const STATES = { passed: 'success', failed: 'failure' }

// On pull_request events, GITHUB_SHA is a merge commit that GitHub creates.
// Statuses only show on the pull request when they're posted to its head commit.
const event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8'))
const sha = event.pull_request?.head.sha ?? process.env.GITHUB_SHA

const main = async () => {
const results = await getCheckResults()
const checks = (results.checks ?? []).filter(
(check) => check.status !== 'not-applicable'
)

for (const check of checks) {
const missed = check.assertionResults
.filter((assertion) => assertion.status !== 'passed')
.map(({ type, value, status }) => `${type} ${value} (${status})`)

const response = await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/statuses/${sha}`,
{
method: 'POST',
headers: {
authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
accept: 'application/vnd.github+json',
},
body: JSON.stringify({
context: `Cypress check: ${check.name}`,
state: STATES[check.status] ?? 'error',
description: (missed.length
? `Missed: ${missed.join('; ')}`
: 'All assertions passed'
).slice(0, 140),
target_url: `${results.runUrl}/properties`,
}),
}
)

if (!response.ok) {
throw new Error(`GitHub responded ${response.status}`)
}
}
}

main().catch((error) => {
console.error(error.message)
process.exit(1)
})

The script skips not-applicable checks, so a check scoped to release/.* branches only appears on pull requests from those branches. GitHub limits a status description to 140 characters, so the script truncates it and links each status to the run for details.

A GitHub pull request with one Cypress check status per applicable check: two failing with the assertions they missed, and two passing with the assertions they met

See also​

Contents