Advanced (Mis à jour: 03/06/2026)

Tests E2E avec Claude Code et Playwright en production

Concevez des E2E Playwright avec Claude Code: mobile, auth state, Trace Viewer, sélecteurs et retries CI.

Tests E2E avec Claude Code et Playwright en production

Demander à Claude Code “ajoute des tests Playwright” ne suffit pas pour sécuriser une mise en production. Vous risquez d’obtenir des tests qui passent une fois, mais reposent sur des classes CSS fragiles, refont le login à chaque scénario, ignorent les captures mobiles et ne laissent aucune trace exploitable en CI.

La bonne approche consiste à utiliser Claude Code comme partenaire de conception de tests. Vous définissez les risques métier, les routes, les règles de sélecteurs et la commande de vérification; Claude Code lit le code et propose la configuration Playwright. Cet article couvre les parcours de monétisation, les screenshots mobiles, les blocs de code, l’état authentifié, Trace Viewer, les retries CI et les moyens d’éviter les tests flaky.

Gardez les sources officielles ouvertes: Claude Code overview, Claude Code common workflows, puis Playwright pour Locators, Authentication, Screenshots, Trace Viewer, Retries et CI. Sur ClaudeCodeLab, complétez avec stratégie de test, CI/CD et responsive design.

Choisir les flux à protéger

Un test E2E démarre un vrai navigateur. Il doit donc protéger ce qu’un test unitaire ne peut pas prouver.

Cas d’usageCe qu’il protègePreuve Playwright
Article vers produitsLe lecteur atteint /products/CTA, URL, interaction mobile
Dashboard connectéUn utilisateur authentifié atteint les pages privéesstorageState, redirection, droits
Layout d’article techniqueCode et tableaux ne cassent pas le mobileScreenshot mobile, pas d’overflow, trace
flowchart LR
  A["Parcours revenu ou inscription"] --> B["Playwright E2E"]
  C["Risque layout mobile"] --> B
  D["Validation pure"] --> E["Tests unitaires"]
  F["Frontière API ou composant"] --> G["Tests d'intégration"]

Pour un média monétisé, un CTA masqué sur mobile, une ligne de code qui élargit l’article ou une page payante absente de CI sont de petits défauts techniques avec un effet direct sur la confiance.

Encadrer la demande à Claude Code

Le prompt doit préciser les routes, les sélecteurs acceptés, les fichiers modifiables et la commande de preuve.

Lis le site Astro existant et ajoute des tests E2E Playwright.

Objectifs:
- Vérifier que `/fr/blog/claude-code-playwright-testing/` mène à `/products/` et `/training/`
- Contrôler en largeur mobile 390px que l'article, les tableaux et les blocs de code ne débordent pas
- Utiliser `storageState` pour les tests authentifiés au lieu de refaire le login UI
- En CI, utiliser 2 retries et `trace: "on-first-retry"`

Contraintes:
- Ne pas utiliser `page.waitForTimeout()`
- Préférer role, label, text ou test id aux chaînes de classes CSS
- Modifier seulement `playwright.config.ts` et `tests/e2e/**`
- Lancer `npx playwright test` et expliquer les échecs avec Trace Viewer

La revue doit vérifier si l’échec sera compréhensible, si les sélecteurs correspondent à l’intention utilisateur et si les données sont maîtrisées.

Configuration prête à copier

cd site
npm i -D @playwright/test
npx playwright install
mkdir tests/e2e
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

const baseURL = process.env.BASE_URL ?? 'http://127.0.0.1:4321';
const hasAuth = Boolean(process.env.TEST_EMAIL && process.env.TEST_PASSWORD);
const authFile = 'playwright/.auth/user.json';

export default defineConfig({
  testDir: './tests/e2e',
  timeout: 30_000,
  expect: { timeout: 5_000 },
  fullyParallel: true,
  forbidOnly: Boolean(process.env.CI),
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 2 : undefined,
  reporter: process.env.CI ? [['html'], ['github']] : 'html',
  use: {
    baseURL,
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
  ...(process.env.PLAYWRIGHT_WEB_SERVER === '1'
    ? {
        webServer: {
          command: 'npm run preview -- --host 127.0.0.1 --port 4321',
          url: baseURL,
          reuseExistingServer: !process.env.CI,
          timeout: 120_000,
        },
      }
    : {}),
  projects: [
    ...(hasAuth
      ? [
          {
            name: 'setup',
            testMatch: /.*\.setup\.ts/,
          },
        ]
      : []),
    {
      name: 'desktop-chrome',
      use: {
        ...devices['Desktop Chrome'],
        storageState: hasAuth ? authFile : undefined,
      },
      dependencies: hasAuth ? ['setup'] : [],
    },
    {
      name: 'mobile-safari',
      use: {
        ...devices['iPhone 13'],
        storageState: hasAuth ? authFile : undefined,
      },
      dependencies: hasAuth ? ['setup'] : [],
    },
  ],
});

En local, les retries sont désactivés pour garder le signal clair. En CI, ils sont utiles seulement si les traces et les rapports sont conservés.

Sauvegarder l’état authentifié

Refaire le login dans chaque test est lent et fragile. storageState enregistre l’état du navigateur après connexion, puis les projets Playwright le réutilisent. Le dossier playwright/.auth doit rester hors git.

// tests/e2e/auth.setup.ts
import { test as setup, expect } from '@playwright/test';
import fs from 'node:fs';
import path from 'node:path';

const authFile = path.resolve('playwright/.auth/user.json');
const email = process.env.TEST_EMAIL;
const password = process.env.TEST_PASSWORD;

setup('save signed-in browser state', async ({ page }) => {
  setup.skip(!email || !password, 'Set TEST_EMAIL and TEST_PASSWORD to record auth state.');

  await page.goto('/login');
  await page.getByLabel(/email|メール|e-mail/i).fill(email!);
  await page.getByLabel(/password|パスワード/i).fill(password!);
  await page.getByRole('button', { name: /log in|sign in|ログイン/i }).click();

  await expect(page).toHaveURL(/dashboard|account|admin/);
  await expect(page.locator('body')).toBeVisible();

  fs.mkdirSync(path.dirname(authFile), { recursive: true });
  await page.context().storageState({ path: authFile });
});

Demandez à Claude Code de séparer le test du login lui-même des tests de fonctionnalités déjà authentifiées. Le reste du suite ne dépendra pas d’un seul bouton de connexion.

Tester mobile, screenshots et blocs de code

Les articles techniques cassent souvent à cause de longues lignes de code ou de tableaux. Ce spec vérifie les CTA, capture la page mobile et échoue en cas de débordement horizontal.

// tests/e2e/article-quality.spec.ts
import { test, expect } from '@playwright/test';

const articlePath = process.env.ARTICLE_PATH ?? '/fr/blog/claude-code-playwright-testing/';

test.describe('article quality checks', () => {
  test('article has monetization CTAs', async ({ page }) => {
    await page.goto(articlePath);

    await expect(page.getByRole('heading', { level: 1 })).toContainText(/Playwright|E2E|Claude Code/i);
    await expect(page.locator('a[href="/products/"], a[href="/products"]').first()).toBeVisible();
    await expect(page.locator('a[href="/training/"], a[href="/training"]').first()).toBeVisible();
  });

  test('mobile layout has no horizontal overflow', async ({ page }, testInfo) => {
    await page.setViewportSize({ width: 390, height: 844 });
    await page.goto(articlePath);
    await expect(page.locator('main, article').first()).toBeVisible();

    const overflow = await page.evaluate(() => ({
      viewport: window.innerWidth,
      documentWidth: document.documentElement.scrollWidth,
      offenders: Array.from(document.querySelectorAll('pre, table, img, iframe, .prose'))
        .filter((node) => {
          const rect = node.getBoundingClientRect();
          return rect.left < -1 || rect.right > window.innerWidth + 1;
        })
        .map((node) => {
          const rect = node.getBoundingClientRect();
          return `${node.tagName.toLowerCase()} ${Math.round(rect.left)}-${Math.round(rect.right)}`;
        }),
    }));

    expect(overflow.documentWidth, JSON.stringify(overflow)).toBeLessThanOrEqual(overflow.viewport + 2);
    expect(overflow.offenders).toEqual([]);
    await page.screenshot({ path: testInfo.outputPath('article-mobile.png'), fullPage: true });
  });

  test('code examples are present and copyable', async ({ page }) => {
    await page.goto(articlePath);

    const blocks = page.locator('pre code');
    await expect(blocks.first()).toBeVisible();
    expect(await blocks.count()).toBeGreaterThanOrEqual(3);
    await expect(blocks.nth(0)).toContainText(/playwright|defineConfig|test/i);
  });
});

La capture sert à la revue humaine; l’assertion chiffrée sert au gate CI.

Protéger les CTA de monétisation

// tests/e2e/revenue-flows.spec.ts
import { test, expect } from '@playwright/test';

const articlePath = process.env.ARTICLE_PATH ?? '/fr/blog/claude-code-playwright-testing/';

test.describe('revenue and learning flows', () => {
  test('reader can move from article to products', async ({ page }) => {
    await page.goto(articlePath);

    await page.locator('a[href="/products/"], a[href="/products"]').first().click();
    await expect(page).toHaveURL(/\/products\/?$/);
    await expect(page.locator('main').first()).toBeVisible();
  });

  test('training CTA is reachable on mobile', async ({ page }) => {
    await page.setViewportSize({ width: 390, height: 844 });
    await page.goto(articlePath);

    await page.locator('a[href="/training/"], a[href="/training"]').first().click();
    await expect(page).toHaveURL(/\/training\/?$/);
    await expect(page.locator('main').first()).toBeVisible();
  });

  test('main navigation can open the blog index', async ({ page }) => {
    await page.goto('/');

    await expect(page.getByRole('navigation').first()).toBeVisible();
    await page.getByRole('link', { name: /blog|記事|articles/i }).first().click();
    await expect(page).toHaveURL(/blog/);
  });
});

Sur un site multilingue, le texte change. Pour les actions critiques, un href stable ou un data-testid métier évite des tests trop fragiles.

Sélecteurs moins flaky

PrioritéSélecteurExempleRaison
HauteRole et nompage.getByRole('button', { name: /save/i })Proche de l’expérience accessible
HauteLabelpage.getByLabel(/email/i)Vérifie la sémantique du formulaire
MoyenneTextepage.getByText(/Start trial/)Lisible mais dépend du copy
MoyenneTest idpage.getByTestId('checkout-submit')Stable pour les actions métier
BasseStructure CSS.card:nth-child(3)Fragile aux changements de layout

Évitez page.waitForTimeout(). Préférez toBeVisible(), toHaveURL() et toContainText(), qui attendent des conditions réelles.

Lire les échecs avec Trace Viewer

npx playwright test --trace on
npx playwright show-report
npx playwright show-trace test-results/path-to-trace/trace.zip

Quand vous redonnez un échec à Claude Code, indiquez le nom du test, l’état visible dans la trace et le comportement attendu. Cela conduit à une correction de sélecteur, de donnée ou d’UI, pas à un délai arbitraire.

CI avec retries et artefacts

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

on:
  pull_request:
  push:
    branches: [main]

jobs:
  e2e:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    defaults:
      run:
        working-directory: site
    env:
      BASE_URL: http://127.0.0.1:4321
      PLAYWRIGHT_WEB_SERVER: "1"
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
          cache-dependency-path: site/package-lock.json
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm run build
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: site/playwright-report
          retention-days: 7

Un retry ne guérit pas un test flaky. Il le classe et laisse des preuves à examiner.

Pièges fréquents

Premier piège: tout mettre en E2E. Les calculs, validations et permissions fines restent meilleurs en tests unitaires ou d’intégration.

Deuxième piège: commiter l’état authentifié. Utilisez des comptes de test limités et excluez playwright/.auth.

Troisième piège: cacher l’instabilité derrière les retries. Un test qui passe seulement au retry reste un risque.

Quatrième piège: demander une correction trop large à Claude Code. Ajoutez le test qui échoue, lisez la trace, puis faites la plus petite correction produit.

Pour démarrer seul, utilisez les modèles ClaudeCodeLab dans products. Pour une équipe, training permet d’aligner les règles de review, la politique CI et l’onboarding.

J’ai appliqué ce workflow à une page locale de type ClaudeCodeLab: screenshot 390px, overflow des blocs de code, navigation vers /products/ et /training/, et configuration des retries CI. Les premiers problèmes utiles n’étaient pas Playwright, mais des lignes de code trop longues et des noms de liens ambigus. Une fois corrigés, Trace Viewer est devenu une preuve claire pour le prompt Claude Code suivant.

#Claude Code #Playwright #tests E2E #automatisation des tests #qualité
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.