La mise en place prend environ 30 minutes et produit un pipeline prêt pour la production qu'on peut intégrer dans n'importe quel projet.

Pourquoi GitHub Actions

GitHub Actions est la plateforme CI dominante pour les nouveaux projets. L'enquête JetBrains State of CI/CD 2025 indique que 62 % des développeurs l'utilisent pour des projets personnels et 41 % dans leurs organisations, deux chiffres supérieurs à n'importe quel autre outil.

Les raisons pratiques sont simples : gratuit pour les dépôts publics, gratuit pour 2 000 minutes par mois sur les privés, et intégré à GitHub. Pas de serveur séparé à provisionner, pas de compte tiers à connecter, pas de configuration de webhook. Vous poussez un fichier YAML et les tests se lancent.

Pour Playwright spécifiquement, GitHub Actions fonctionne bien parce que les runners Ubuntu fournis ont tout ce dont Playwright a besoin. Les dépendances de navigateur, les polices et les bibliothèques système sont disponibles via --with-deps. L'équipe officielle Playwright teste sur ces runners. Vous êtes dans le chemin supporté.

Le workflow minimal fonctionnel

Créez le fichier .github/workflows/playwright.yml à la racine de votre projet. C'est là que GitHub cherche les définitions de workflow.

mkdir -p .github/workflows

Voici le workflow minimal complet :

# .github/workflows/playwright.yml
name: Playwright Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Run Playwright tests
        run: npx playwright test

      - name: Upload test report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 14

Committez ce fichier, poussez, et ouvrez l'onglet Actions dans votre dépôt GitHub. Vous verrez le workflow apparaître et se lancer en quelques secondes.

Quelques détails à comprendre. npm ci au lieu de npm install installe exactement ce qui est dans package-lock.json sans le modifier. C'est la bonne commande pour les environnements CI car elle donne des builds reproductibles. npx playwright install --with-deps installe les navigateurs avec leurs dépendances système (libgtk, libnss, etc.) qui ne sont pas présentes sur un runner Ubuntu vierge. Omettre --with-deps est la cause la plus fréquente d'échec de Playwright en CI quand ça fonctionne en local.

Le if: always() sur l'étape d'upload est important. Sans lui, l'artifact n'est uploadé qu'en cas de succès du workflow, ce qui signifie qu'on perd le rapport exactement quand les tests échouent et qu'on en a le plus besoin.

Le timeout-minutes: 60 sur le job est un garde-fou. Un test bloqué peut autrement consommer tout votre quota mensuel de minutes avant que vous ne vous en rendiez compte.

Stocker et télécharger les rapports de test

L'étape upload-artifact dans le workflow ci-dessus sauvegarde tout le répertoire playwright-report/ sous forme de zip téléchargeable. Après la fin d'un run, allez sur la page du run du workflow, puis Summary, puis Artifacts, puis playwright-report. Téléchargez et décompressez, puis ouvrez index.html dans un navigateur.

Le rapport HTML montre quels tests ont passé, lequel ont échoué, les captures d'échec, les traces, et les vidéos si l'enregistrement est activé. C'est comme ça qu'on investigue les échecs sans accéder directement au runner CI.

Si vous générez plusieurs rapports (un par navigateur dans un run matrix), donnez à chaque artifact un nom unique :

      - name: Upload test report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report-${{ matrix.browser }}
          path: playwright-report/
          retention-days: 14

retention-days: 14 conserve les rapports deux semaines, ce qui est suffisant pour la plupart des workflows de débogage sans accumuler des coûts de stockage inutiles. GitHub permet jusqu'à 90 jours.

Variables d'environnement et secrets

Ne mettez jamais des identifiants ou des URLs spécifiques à l'environnement dans le fichier YAML du workflow. Les GitHub Secrets sont le bon endroit pour tout ce qui est sensible.

Allez dans votre dépôt, puis Settings, puis Secrets and variables, puis Actions, puis New repository secret. Ajoutez les secrets dont vos tests ont besoin : TEST_USER, TEST_PASSWORD, et toute clé d'API.

Référencez-les dans le workflow sous env: sur l'étape de test :

      - name: Run Playwright tests
        run: npx playwright test
        env:
          BASE_URL: https://staging.example.com
          TEST_USER: ${{ secrets.TEST_USER }}
          TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

Dans vos tests, lisez-les avec process.env :

const baseURL = process.env.BASE_URL ?? 'http://localhost:3000';
const user = process.env.TEST_USER ?? '';
const password = process.env.TEST_PASSWORD ?? '';

GitHub masque les valeurs secrètes dans les logs. Si un secret apparaît dans la sortie, il est remplacé par *. Les valeurs non sensibles comme BASE_URL peuvent aussi être stockées comme variables de dépôt (pas des secrets), visibles dans l'interface. Utilisez les variables pour les URLs et les feature flags, les secrets pour les identifiants.

Les secrets ne sont pas disponibles pour les workflows déclenchés par des pull requests de forks. C'est une mesure de sécurité : une PR de fork pourrait autrement lire vos secrets. Si vous avez besoin de tester des PRs de forks avec des secrets, renseignez-vous sur l'événement pull_request_target, mais comprenez les implications de sécurité avant de l'utiliser.

Cache pour des runs plus rapides

Un run de workflow frais installe les modules Node et les navigateurs Playwright depuis zéro à chaque fois. Sur un runner froid, c'est 2 à 3 minutes avant qu'un seul test ne se lance. Le cache réduit ça significativement.

L'action setup-node avec cache: 'npm' gère déjà automatiquement le cache de node_modules. Pour les navigateurs Playwright, ajoutez une étape de cache dédiée :

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Cache Playwright browsers
        uses: actions/cache@v4
        id: playwright-cache
        with:
          path: ~/.cache/ms-playwright
          key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

      - name: Install Playwright browsers
        run: npx playwright install --with-deps
        if: steps.playwright-cache.outputs.cache-hit != 'true'

La clé de cache inclut un hash de package-lock.json. Quand vous mettez à niveau Playwright, le lock file change, le hash change, le cache rate, et les navigateurs se réinstallent. Quand rien ne change, le cache est valide et l'installation des navigateurs est sautée.

Cette optimisation compte surtout sur les grandes suites. Une suite de 200 tests avec le cache activé peut passer de 8 minutes à 4 minutes simplement en évitant des téléchargements répétés.

Lancer uniquement sur des branches spécifiques

La section on: contrôle quand votre workflow se lance. L'exemple minimal filtre déjà sur main et develop. Vous pouvez être plus précis :

on:
  push:
    branches:
      - main
      - develop
      - 'release/**'
  pull_request:
    branches:
      - main
      - develop
  workflow_dispatch:

workflow_dispatch: ajoute un bouton "Run workflow" dans l'onglet Actions, vous permettant de le déclencher manuellement. Utile pour lancer les tests sur une branche spécifique à la demande sans pousser un commit.

Vous pouvez aussi lancer les tests selon un planning. Des runs de smoke tests nocturnes détectent les problèmes qui n'apparaissent que dans certaines fenêtres horaires ou après accumulation de données :

on:
  schedule:
    - cron: '0 3 * * *'   # 3h UTC chaque nuit
  push:
    branches: [main]
  pull_request:
    branches: [main]

Dans un job, vous pouvez ajouter des conditions de branche sur des étapes spécifiques avec if: :

      - name: Run full suite
        run: npx playwright test
        if: github.ref == 'refs/heads/main'

      - name: Run smoke tests only
        run: npx playwright test --grep @smoke
        if: github.event_name == 'pull_request'

Ce pattern donne aux pull requests un retour rapide avec une suite smoke, tandis que les runs sur la branche main reçoivent la suite complète.

Stratégie matrix pour les tests cross-browser

Playwright supporte Chromium, Firefox et WebKit. Lancer les trois sur chaque PR coûte cher en temps et en minutes, mais vous voulez quand même une couverture cross-browser. La stratégie matrix lance des jobs parallèles avec des configurations différentes :

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 60

    strategy:
      fail-fast: false
      matrix:
        browser: [chromium, firefox, webkit]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Cache Playwright browsers
        uses: actions/cache@v4
        id: playwright-cache
        with:
          path: ~/.cache/ms-playwright
          key: playwright-${{ runner.os }}-${{ matrix.browser }}-${{ hashFiles('package-lock.json') }}

      - name: Install Playwright browsers
        run: npx playwright install ${{ matrix.browser }} --with-deps
        if: steps.playwright-cache.outputs.cache-hit != 'true'

      - name: Run tests
        run: npx playwright test --project=${{ matrix.browser }}
        env:
          BASE_URL: ${{ vars.BASE_URL }}
          TEST_USER: ${{ secrets.TEST_USER }}
          TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

      - name: Upload report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report-${{ matrix.browser }}
          path: playwright-report/
          retention-days: 14

fail-fast: false signifie que si Firefox échoue, les jobs Chromium et WebKit continuent. Avec fail-fast: true (la valeur par défaut), tout échec de job matrix annule le reste, généralement pas ce qu'on veut quand on débogue des problèmes cross-browser. npx playwright install ${{ matrix.browser }} --with-deps installe uniquement le navigateur pour ce job, pas les trois. C'est plus rapide que tout installer dans chaque job.
Lancez la matrix uniquement sur les pushs vers main, pas sur chaque PR. Ajoutez if: github.ref == 'refs/heads/main' sur le job ou utilisez le filtre de branche sur le déclencheur on.push. Vos PRs restent rapides ; votre branche main reçoit la couverture complète.

Sharding : diviser les tests sur plusieurs jobs

Le sharding divise votre suite de tests sur plusieurs jobs parallèles. Là où la stratégie matrix lance des configurations différentes, le sharding lance les mêmes tests plus vite en les divisant :

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 30

    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3, 4]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install chromium --with-deps

      - name: Run shard
        run: npx playwright test --shard=${{ matrix.shard }}/4
        env:
          BASE_URL: ${{ vars.BASE_URL }}
          TEST_USER: ${{ secrets.TEST_USER }}
          TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

      - name: Upload shard report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report-shard-${{ matrix.shard }}
          path: playwright-report/
          retention-days: 14

Playwright distribue les tests équitablement entre les shards selon les données de timing des runs précédents si vous fournissez un rapport blob, ou par ordre de fichier sinon. Avec quatre shards, une suite de 20 minutes tourne en environ 5 minutes.

Vous pouvez combiner sharding et stratégie matrix. Un pattern courant : quatre shards par navigateur pour les grandes suites :

    strategy:
      matrix:
        browser: [chromium, firefox]
        shard: [1, 2, 3, 4]

Ça crée 8 jobs parallèles. Attention : chaque job consomme des minutes de runner. 8 jobs tournant 5 minutes chacun coûtent 40 minutes de quota, pareil qu'en série. L'avantage est le temps d'exécution réel, pas la consommation de quota.

Fusionner les rapports de shards en un seul rapport HTML nécessite le reporter blob. Définissez reporter: 'blob' dans votre config Playwright pour les runs CI, puis ajoutez un job de fusion :

  merge-reports:
    if: always()
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - name: Download blob reports
        uses: actions/download-artifact@v4
        with:
          path: all-blob-reports
          pattern: blob-report-*
          merge-multiple: true
      - name: Merge into HTML report
        run: npx playwright merge-reports --reporter html ./all-blob-reports
      - name: Upload merged report
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 14

Publier les résultats en commentaires de PR

Avoir les résultats de tests directement dans la pull request rend les échecs bien plus visibles. Ça nécessite une action séparée après la fin du run de tests.

L'approche la plus simple utilise l'action dawidd6/action-junit-report, qui lit la sortie JUnit XML et poste un commentaire. Ajoutez d'abord le reporter JUnit à votre config Playwright :

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  reporter: [
    ['html'],
    ['junit', { outputFile: 'results.xml' }],
  ],
});

Puis ajoutez une étape de reporting à votre workflow :

      - name: Run Playwright tests
        run: npx playwright test
        env:
          BASE_URL: ${{ vars.BASE_URL }}
          TEST_USER: ${{ secrets.TEST_USER }}
          TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

      - name: Post test results to PR
        uses: dawidd6/action-junit-report@v4
        if: always()
        with:
          report_paths: results.xml
          github_token: ${{ secrets.GITHUB_TOKEN }}
          check_name: Playwright Test Results

GITHUB_TOKEN est automatiquement disponible dans chaque workflow. Pas de configuration de secret manuelle nécessaire.

Le commentaire montre les compteurs pass/fail, les noms des tests, et les messages d'erreur directement dans la conversation PR. Les reviewers n'ont pas besoin de naviguer jusqu'à l'onglet Actions pour savoir si les tests ont passé.

FAQ

Les tests passent en local mais échouent dans GitHub Actions. Par où commencer ?

Vérifiez dans cet ordre : les variables d'environnement, le BASE_URL, et le timing. Un secret non défini dans Actions vaudra undefined sans erreur. Si BASE_URL pointe vers localhost, ça ne fonctionnera pas en CI. Les runners CI sont plus lents que les machines de développement. Augmentez les timeouts dans votre playwright.config.ts ou ajoutez retries: 1 spécifiquement pour CI avec process.env.CI ? 1 : 0.

Comment voir le rapport HTML d'un run en échec ?

Allez sur le run du workflow en échec, puis Summary, puis faites défiler jusqu'à Artifacts, puis téléchargez playwright-report. Extrayez le zip et ouvrez index.html dans un navigateur. Le rapport inclut captures d'écran et traces pour les tests en échec.

Dois-je commiter les navigateurs Playwright dans le dépôt ?

Non. Le workflow les installe à l'exécution via npx playwright install. Avec le cache configuré, les runs suivants sautent le téléchargement. Commiter les binaires de navigateurs gonflerait votre dépôt de plusieurs centaines de mégaoctets.

Mon workflow est lent. Qu'est-ce qui a le plus d'impact ?

Le cache est la première optimisation : sauvegarder les navigateurs Playwright réduit de 1 à 2 minutes par run. Le sharding est la deuxième, en divisant une suite de 15 minutes sur 3 shards pour la ramener à 5 minutes. La troisième est de lancer uniquement Chromium sur les PRs et la matrix complète sur main, ce qui triple la vitesse de chaque run de PR.

Puis-je utiliser l'image Docker Playwright au lieu d'installer les navigateurs ?

Oui. Définissez container: mcr.microsoft.com/playwright:v1.52.0-jammy sur le job et sautez l'étape d'installation. Le compromis : les conteneurs Docker démarrent plus lentement que les runners Ubuntu nus, et vous êtes bloqué sur une version spécifique de Playwright dans le tag de l'image. C'est un choix raisonnable pour les équipes qui veulent éviter l'étape d'installation.

Comment lancer uniquement les tests liés aux fichiers modifiés ?

Playwright ne supporte pas ça nativement. Certaines équipes utilisent des filtres basés sur les chemins dans le workflow, ne déclenchant le job de test que quand des fichiers dans certains répertoires changent. Ajoutez paths: sous on.pull_request pour limiter quand le workflow se lance. La sélection de tests complète basée sur les changements de code nécessite des outils externes et est rarement rentable avant que votre suite ne dépasse 10 minutes.

→ See also: CI/CD pour QA: GitHub Actions, Jenkins et GitLab Comparés | Déboguer les Tests Instables: Un Guide Pratique | Exécution Parallèle dans Playwright: Workers, Fragments et Fragmentation pour la Vitesse | Rapports de Tests Playwright: HTML Intégré vs Allure | GitLab CI + Playwright: Guide de Configuration Complet