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.
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’usage | Ce qu’il protège | Preuve Playwright |
|---|---|---|
| Article vers produits | Le lecteur atteint /products/ | CTA, URL, interaction mobile |
| Dashboard connecté | Un utilisateur authentifié atteint les pages privées | storageState, redirection, droits |
| Layout d’article technique | Code et tableaux ne cassent pas le mobile | Screenshot 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électeur | Exemple | Raison |
|---|---|---|---|
| Haute | Role et nom | page.getByRole('button', { name: /save/i }) | Proche de l’expérience accessible |
| Haute | Label | page.getByLabel(/email/i) | Vérifie la sémantique du formulaire |
| Moyenne | Texte | page.getByText(/Start trial/) | Lisible mais dépend du copy |
| Moyenne | Test id | page.getByTestId('checkout-submit') | Stable pour les actions métier |
| Basse | Structure 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.
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.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Permission receipt Claude Code : portée, preuves et rollback
Modèle de permission receipt pour Claude Code : actions autorisées, limites d'approbation, commandes de preuve, rollback et CTAs revenus.
Agent Harness securise pour Claude Code et Codex : permissions, verification et rollback
Construisez un Agent Harness pratique pour Claude Code et Codex avec politiques, plan, verification et recuperation.
Sous-agents Claude Code : guide pratique pour déléguer sans perdre le contrôle
Guide pratique des sous-agents Claude Code pour répartir articles et code : règles, prompts, pièges et checklist.