DIAGNOSTICS AND DELIVERY · LESSON 23 OF 24
Continuous integration and reports
Run the same tests in a clean CI environment.
- GitHub Actions
- npm ci
- browser dependencies
- artifacts
Your runnable checkpoint
Extract into a new practice folder. Each download contains tests, configuration, the complete local practice app and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.
Install dependencies and browser binaries using the commands below. The test runner starts the practice server on port 4174. Tests target localhost, never the production EduPoly site.
1. Understand the concepts
CI should reproduce a known local workflow: install locked dependencies, install matching browser binaries and OS dependencies, type-check, then run tests. The included workflow targets a dedicated repository containing this extracted checkpoint at its root.
Run npm install once and commit package-lock.json before using npm ci or setup-node’s npm cache. The ZIP intentionally contains no node_modules or browser downloads. A CI machine starts without your local browser cache.
The --with-deps option installs Linux browser system dependencies as well as the browser. This workflow runs Chromium; expand the project configuration and installation command together if you want a multi-browser matrix.
Upload the HTML report even when tests fail, but limit retention and avoid secrets in test artifacts. Store real credentials in CI secrets, use dedicated test accounts and do not expose production reset endpoints.
For larger suites, sharding divides tests across separate jobs. Each shard needs independent data and an artifact-merging strategy. Begin with a reliable single job before optimizing execution time.
2. Build and run
These are complete test files and configuration. The download also includes app/ and server.mjs; preserve their paths. Stop any manually started practice server before running the suite.
npm install
npx playwright install chromium
npm run check
npm testtests/lesson.spec.ts
import { test, expect } from '@playwright/test';
test('CI smoke check', async ({ page }) => {
await page.goto('/');
await expect(page.getByRole('heading', { name: 'Playwright Lab', exact: true })).toBeVisible();
await expect(page.getByRole('button', { name: 'Add task', exact: true })).toBeEnabled();
});
.github/workflows/playwright.yml
name: Playwright checks
on: [push, pull_request]
permissions:
contents: read
jobs:
browser-tests:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npm run check
- run: npm test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 7
playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
workers: process.env.CI ? 2 : undefined,
timeout: 30000,
expect: { timeout: 5000 },
reporter: [['list'], ['html', { open: 'never' }]],
use: { baseURL: 'http://127.0.0.1:4174', trace: 'retain-on-failure', screenshot: 'only-on-failure' },
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
webServer: { command: 'node server.mjs', url: 'http://127.0.0.1:4174', reuseExistingServer: false, timeout: 15000 }
});
package.json
{
"name": "edupoly-playwright-continuous-integration",
"version": "1.0.0",
"private": true,
"scripts": {
"test": "playwright test",
"test:headed": "playwright test --headed",
"test:ui": "playwright test --ui",
"check": "tsc --noEmit",
"report": "playwright show-report",
"serve": "node server.mjs"
},
"devDependencies": {
"@playwright/test": "1.56.1",
"typescript": "5.9.3",
"@types/node": "22.20.5"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "Node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": [
"node"
],
"noEmit": true
},
"include": [
"tests/**/*.ts",
"pages/**/*.ts",
"playwright.config.ts"
]
}
.gitignore
node_modules/
playwright-report/
test-results/
playwright/.auth/
.env
3. Trace the code and check the result
- The workflow uses npm ci rather than silently changing dependency resolution.
- Browser OS dependencies are installed explicitly on the Linux runner.
- The report artifact is retained for seven days.
Verify your result
- The local smoke test passes.
- After committing a lockfile in a dedicated repository, the workflow can install and run it.
4. Practice independently
Make CI reject a committed test.only.
Show the explanation
The supplied config sets forbidOnly when CI is present. Temporarily add test.only, observe the CI failure, then remove it before merging.
When something goes wrong
If a browser executable is missing, run npx playwright install chromium in the project folder. If port 4174 is occupied, stop your manual practice server. Read assertion call logs for mismatched locators or expectations. Use npm run report to inspect failures; do not add fixed sleeps or broad resets to hide them.
