Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/scripts/checkCoverage.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Fails CI when the latest supercov run in the current directory did not pass
// or its coverage is below the given floors.
// Usage: node checkCoverage.mjs --lines 100 --branches 100 --mcdc 100
import { execFileSync } from 'node:child_process'
import { parseArgs } from 'node:util'

const { values } = parseArgs({
options: {
lines: { type: 'string', default: '100' },
branches: { type: 'string', default: '100' },
mcdc: { type: 'string', default: '100' },
},
})

const output = execFileSync(
'npx',
[
'-y',
`supercov@${process.env.SUPERCOV_VERSION}`,
'runs',
'latest',
'--json',
],
{ encoding: 'utf8' },
)
const { data } = JSON.parse(output)

const failures = []

if (data.testExitCode !== 0) {
failures.push(`the test command exited with ${data.testExitCode}`)
}
if (!data.valid || data.stale) {
failures.push('the coverage run is not valid for the current source')
}

const metrics = {
lines: data.coverage.lines.percentage,
branches: data.coverage.branches.percentage,
mcdc: data.coverage.conditionCoveragePct,
}

for (const [name, actual] of Object.entries(metrics)) {
const floor = Number(values[name])
const status = actual >= floor ? 'ok' : 'below the floor'
console.log(`${name}: ${actual}% (floor ${floor}%) ${status}`)
if (actual < floor) failures.push(`${name} ${actual}% < ${floor}%`)
}

if (failures.length) {
console.error(`Coverage check failed: ${failures.join('; ')}`)
process.exit(1)
}
131 changes: 131 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: Test

on:
push:
branches: [main]
pull_request:

concurrency:
group: test-${{ github.ref }}
cancel-in-progress: true

env:
SUPERCOV_VERSION: 4.15.0
# Real-provider integration tests skip themselves when these are empty or
# rejected, so the suite passes without them.
TEST_OPENAI_API_KEY: ${{ secrets.TEST_OPENAI_API_KEY }}
TEST_ANTHROPIC_API_KEY: ${{ secrets.TEST_ANTHROPIC_API_KEY }}
TEST_GOOGLE_API_KEY: ${{ secrets.TEST_GOOGLE_API_KEY }}
TEST_OPENROUTER_API_KEY: ${{ secrets.TEST_OPENROUTER_API_KEY }}
TEST_AZURE_OPENAI_API_KEY: ${{ secrets.TEST_AZURE_OPENAI_API_KEY }}
TEST_AZURE_OPENAI_ENDPOINT: ${{ secrets.TEST_AZURE_OPENAI_ENDPOINT }}
TEST_AZURE_AI_PROJECT_ENDPOINT: ${{ secrets.TEST_AZURE_AI_PROJECT_ENDPOINT }}
TEST_AZURE_TENANT_ID: ${{ secrets.TEST_AZURE_TENANT_ID }}
TEST_AZURE_CLIENT_ID: ${{ secrets.TEST_AZURE_CLIENT_ID }}
TEST_AZURE_CLIENT_SECRET: ${{ secrets.TEST_AZURE_CLIENT_SECRET }}

jobs:
server:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
defaults:
run:
working-directory: packages/server
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
working-directory: .
- run: npx prisma generate
# The server imports @superinterface/react/server from its build.
- run: npm run build
working-directory: packages/react
- run: npx -y supercov@$SUPERCOV_VERSION -- npm run test
- run: node ../../.github/scripts/checkCoverage.mjs --lines 100 --branches 100 --mcdc 100

react:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
defaults:
run:
working-directory: packages/react
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
working-directory: .
# The e2e tests start the real server (next dev) against Postgres.
- run: npx prisma generate
working-directory: packages/server
# The server it starts imports @superinterface/react/server from the build.
- run: npm run build
- run: npx -y supercov@$SUPERCOV_VERSION -- npx vitest run
# Two MC/DC conditions are known to be unreachable for now (React 19.2
# <Activity>, threadIdStorageOptions={null}).
- run: node ../../.github/scripts/checkCoverage.mjs --lines 100 --branches 99.3 --mcdc 99.4

embeds:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: npx prisma generate
working-directory: packages/server
- run: npx playwright install --with-deps chromium
# The script-tag bundles include the built react and root-element packages.
- run: npm run build
working-directory: packages/root-element
- run: npm run build
working-directory: packages/react
- run: npx -y supercov@$SUPERCOV_VERSION -- sh -c "npm run build && npx playwright test"
working-directory: packages/root-element
- run: node ../../.github/scripts/checkCoverage.mjs --lines 100 --branches 100 --mcdc 100
working-directory: packages/root-element
- run: npx -y supercov@$SUPERCOV_VERSION -- sh -c "npm run build && npx playwright test"
working-directory: packages/javascript
- run: node ../../.github/scripts/checkCoverage.mjs --lines 100 --branches 100 --mcdc 100
working-directory: packages/javascript
Loading