Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Playwright testing guide covering E2E, component, API, visual, accessibility, and security tests.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
infrastructure-ci-cd/gitlab.md
1# GitLab CI/CD Configuration23## Table of Contents451. [Key Commands](#key-commands)62. [Patterns](#patterns)73. [Decision Guide](#decision-guide)84. [Anti-Patterns](#anti-patterns)95. [Troubleshooting](#troubleshooting)1011> **When to use**: Running Playwright tests in GitLab pipelines on merge requests, merges to main, or scheduled pipelines.1213## Key Commands1415```bash16npx playwright install --with-deps # install browsers + OS deps17npx playwright test --shard=1/4 # run 1 of 4 parallel shards18npx playwright merge-reports ./blob-report # merge shard results19npx playwright test --reporter=dot # minimal output for CI logs20```2122## Patterns2324### Basic Pipeline Configuration2526**Use when**: Any GitLab project with Playwright tests.2728```yaml29# .gitlab-ci.yml30image: mcr.microsoft.com/playwright:v1.48.0-noble3132stages:33- install34- test35- report3637variables:38CI: "true"39npm_config_cache: "$CI_PROJECT_DIR/.npm"4041cache:42key:43files:44- package-lock.json45paths:46- .npm/47- node_modules/4849setup:50stage: install51script:52- npm ci53artifacts:54paths:55- node_modules/56expire_in: 1 hour5758e2e:59stage: test60needs: [setup]61script:62- npx playwright test63artifacts:64when: always65paths:66- playwright-report/67- test-results/68expire_in: 14 days69rules:70- if: $CI_PIPELINE_SOURCE == "merge_request_event"71- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH72```7374### Sharded Parallel Execution7576**Use when**: Test suite exceeds 10 minutes. GitLab's `parallel` keyword splits across jobs automatically.77**Avoid when**: Suite runs under 5 minutes.7879```yaml80image: mcr.microsoft.com/playwright:v1.48.0-noble8182stages:83- install84- test85- report8687variables:88CI: "true"89npm_config_cache: "$CI_PROJECT_DIR/.npm"9091cache:92key:93files:94- package-lock.json95paths:96- .npm/97- node_modules/9899setup:100stage: install101script:102- npm ci103artifacts:104paths:105- node_modules/106expire_in: 1 hour107108e2e:109stage: test110needs: [setup]111parallel: 4112script:113- npx playwright test --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL114artifacts:115when: always116paths:117- blob-report/118expire_in: 1 hour119rules:120- if: $CI_PIPELINE_SOURCE == "merge_request_event"121- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH122123combine-reports:124stage: report125needs: [e2e]126when: always127script:128- npx playwright merge-reports --reporter=html ./blob-report129artifacts:130when: always131paths:132- playwright-report/133expire_in: 14 days134```135136**Config for sharded pipelines:**137138```typescript139// playwright.config.ts140export default defineConfig({141reporter: process.env.CI142? [["blob"], ["dot"]]143: [["html", { open: "on-failure" }]],144});145```146147### Environment Variables and Secrets148149**Use when**: Tests need secrets (API keys, passwords) and should only run on merge requests or the default branch.150151```yaml152image: mcr.microsoft.com/playwright:v1.48.0-noble153154stages:155- test156157variables:158CI: "true"159160e2e:staging:161stage: test162variables:163BASE_URL: $STAGING_URL164TEST_PASSWORD: $TEST_PASSWORD165API_KEY: $API_KEY166before_script:167- npm ci168script:169- npx playwright test170artifacts:171when: always172paths:173- playwright-report/174- test-results/175expire_in: 14 days176rules:177- if: $CI_PIPELINE_SOURCE == "merge_request_event"178- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH179- when: manual180allow_failure: true181```182183**Setting variables in GitLab:**184Navigate to **Settings > CI/CD > Variables** and add:185186- `STAGING_URL` -- not masked, not protected187- `TEST_PASSWORD` -- masked, protected188- `API_KEY` -- masked, protected189190### Multi-Browser Matrix191192**Use when**: Running Chromium on MRs and all browsers on the default branch.193194```yaml195image: mcr.microsoft.com/playwright:v1.48.0-noble196197stages:198- install199- test200201variables:202CI: "true"203204setup:205stage: install206script:207- npm ci208artifacts:209paths:210- node_modules/211expire_in: 1 hour212213e2e:chromium:214stage: test215needs: [setup]216script:217- npx playwright test --project=chromium218artifacts:219when: always220paths:221- playwright-report/222- test-results/223expire_in: 14 days224rules:225- if: $CI_PIPELINE_SOURCE == "merge_request_event"226227e2e:all-browsers:228stage: test229needs: [setup]230parallel:231matrix:232- PROJECT: [chromium, firefox, webkit]233script:234- npx playwright test --project=$PROJECT235artifacts:236when: always237paths:238- playwright-report/239- test-results/240expire_in: 14 days241rules:242- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH243```244245### Services Integration (Database, Cache)246247**Use when**: Tests need the application running alongside Playwright, or you need external services.248249```yaml250stages:251- test252253e2e:integration:254stage: test255image: mcr.microsoft.com/playwright:v1.48.0-noble256services:257- name: postgres:latest258alias: db259- name: redis:latest260alias: cache261variables:262CI: "true"263DATABASE_URL: "postgresql://postgres:postgres@db:5432/testdb"264REDIS_URL: "redis://cache:6379"265POSTGRES_PASSWORD: "postgres"266POSTGRES_DB: "testdb"267before_script:268- npm ci269- npx prisma db push270- npx prisma db seed271script:272- npx playwright test273artifacts:274when: always275paths:276- playwright-report/277- test-results/278expire_in: 14 days279rules:280- if: $CI_PIPELINE_SOURCE == "merge_request_event"281- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH282```283284### Scheduled Nightly Regression285286**Use when**: Full regression is too slow for every MR.287288```yaml289e2e:nightly:290stage: test291image: mcr.microsoft.com/playwright:v1.48.0-noble292before_script:293- npm ci294script:295- npx playwright test --grep @regression296artifacts:297when: always298paths:299- playwright-report/300expire_in: 30 days301rules:302- if: $CI_PIPELINE_SOURCE == "schedule"303```304305Set up the schedule in **CI/CD > Schedules**: `0 3 * * 1-5` (3 AM UTC, weekdays).306307## Decision Guide308309| Scenario | Approach | Why |310| ------------------------------------ | ------------------------------------------------------ | --------------------------------------------------- |311| Simple project, < 5 min suite | Single `test` job using Playwright Docker image | No sharding overhead; artifacts capture report |312| Suite > 10 min | `parallel: N` with `--shard` | GitLab auto-assigns `CI_NODE_INDEX`/`CI_NODE_TOTAL` |313| Merge request fast feedback | Chromium only on MRs; all browsers on main | 3x fewer pipeline minutes on MRs |314| External services needed (DB, Redis) | `services:` keyword with Postgres/Redis images | GitLab manages service lifecycle |315| Secrets for staging environment | GitLab CI/CD Variables (masked + protected) | Never hardcode secrets in `.gitlab-ci.yml` |316| Full nightly regression | Pipeline schedule (`CI_PIPELINE_SOURCE == "schedule"`) | Avoids blocking MR pipelines |317| Report browsing | `artifacts:` with `paths: [playwright-report/]` | Browse directly in GitLab job artifacts UI |318319## Anti-Patterns320321| Anti-Pattern | Problem | Do This Instead |322| ---------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------- |323| Not using the Playwright Docker image | Installing browsers every run adds 1-2 minutes | Use `mcr.microsoft.com/playwright:v1.48.0-noble` as base image |324| `artifacts: when: on_failure` only | No report when tests pass; can't verify results | Use `when: always` to capture reports regardless |325| No `expire_in` on artifacts | Artifacts accumulate and consume storage | Set `expire_in: 14 days` for reports, `1 hour` for intermediate artifacts |326| Hardcoding `CI_NODE_TOTAL` in shard flag | Breaks when you change `parallel:` value | Use `--shard=$CI_NODE_INDEX/$CI_NODE_TOTAL` |327| Skipping `needs:` between stages | Jobs wait for all previous stage jobs, not just their dependencies | Use `needs:` for precise dependency graphs |328| Large `cache:` including `node_modules/` without key | Stale cache causes version conflicts | Key cache on `package-lock.json` hash |329330## Troubleshooting331332### Browser launch fails: "Failed to launch browser"333334**Cause**: Not using the Playwright Docker image, or using a version that doesn't match your `@playwright/test` version.335336**Fix**: Match the Docker image tag to your Playwright version:337338```yaml339# Check your version: npm ls @playwright/test340image: mcr.microsoft.com/playwright:v1.48.0-noble341```342343### Tests hang in GitLab runner: "Navigation timeout exceeded"344345**Cause**: GitLab shared runners may have limited resources.346347**Fix**: Reduce workers and increase timeouts:348349```typescript350export default defineConfig({351workers: process.env.CI ? 2 : undefined,352use: {353navigationTimeout: process.env.CI ? 30_000 : 15_000,354},355});356```357358### Pipeline runs on every push, not just merge requests359360**Cause**: Missing `rules:` configuration.361362**Fix**: Add explicit rules:363364```yaml365rules:366- if: $CI_PIPELINE_SOURCE == "merge_request_event"367- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH368```369370### Services (Postgres/Redis) not reachable from tests371372**Cause**: Using `localhost` instead of the service alias.373374**Fix**: Use the service alias as hostname:375376```yaml377services:378- name: postgres:latest379alias: db380381variables:382DATABASE_URL: "postgresql://postgres:postgres@db:5432/testdb"383```384385### Merged report is empty after sharded run386387**Cause**: Each shard job needs the `blob` reporter, not `html`.388389**Fix**: Configure blob reporter for CI:390391```typescript392export default defineConfig({393reporter: process.env.CI394? [["blob"], ["dot"]]395: [["html", { open: "on-failure" }]],396});397```398