Les messages d'erreur de Playwright ont une structure cohérente : un type d'erreur avec un message, un call log indiquant ce qui était attendu, et une stack trace pointant vers la ligne du test. Savoir lire chaque partie réduit le temps de débogage de 30 minutes à 5.

L'anatomie d'une erreur Playwright

Error: locator.click: Timeout 30000ms exceeded.
Call log:
  - waiting for getByRole('button', { name: 'Submit' })

    waiting for element to be visible, enabled and stable
    element is not visible - waiting...
    element is not visible - waiting...
    element is not visible - waiting...

  at Object.<anonymous> (tests/checkout.spec.ts:45:40)

Trois parties :

1. Le type d'erreur et le message : locator.click: Timeout 30000ms exceeded — l'action a échoué, ce qui a expiré

2. Le call log : ce que Playwright faisait et observait pendant l'attente

3. La stack trace : où dans le code du test c'est arrivé

Lisez-les dans cet ordre : le type d'erreur d'abord, le call log ensuite, la stack trace en dernier. Le call log est le plus informatif.

Erreurs courantes et leur signification

Timeout dépassé

Error: locator.click: Timeout 30000ms exceeded.

Playwright n'a pas pu terminer l'action dans le délai imparti. Le call log vous dit pourquoi. Causes fréquentes :

  • L'élément n'existe pas dans le DOM
  • L'élément existe mais est masqué (display: none, visibility: hidden, opacity: 0)
  • L'élément existe et est visible mais est désactivé
  • L'élément est recouvert par un autre élément (overlay, modale, notification toast)
  • La navigation de page n'a pas terminé avant l'action suivante
Correction : Lisez le call log. Il liste la condition non satisfaite (element is not visible, element is not stable, element is disabled). C'est votre vrai problème.

Violation du mode strict

Error: strict mode violation: getByRole('button', { name: 'OK' }) resolved to 2 elements

Votre locator a correspondant à plus d'un élément. Playwright exige que les locators soient uniques, sauf si vous l'indiquez explicitement.

Correction : Soyez plus précis en ajoutant un filtre contextuel.

// Trop large
page.getByRole('button', { name: 'OK' })

// Plus précis — limité à la modale
page.getByRole('dialog').getByRole('button', { name: 'OK' })

// Ou utilisez nth si l'ordre est stable
page.getByRole('button', { name: 'OK' }).first()

Locator non trouvé

Error: locator.fill: Error: strict mode violation: getByLabel('Email') resolved to 0 elements

Zéro élément correspond. Soit le locator est incorrect, soit l'élément n'est pas encore sur la page, soit vous êtes sur la mauvaise page.

Correction : Vérifiez le locator. Ouvrez le test avec le flag --debug pour inspecter l'état de la page.

npx playwright test --debug tests/login.spec.ts

Timeout de navigation

Error: page.goto: Timeout 30000ms exceeded.
call log:
  navigating to "http://localhost:3000/login", waiting until "load"

La page n'a pas fini de se charger. Causes possibles :

Le serveur est lent ou ne tourne pas, l'option waitUntil est trop stricte ('networkidle' sur une page avec du polling en arrière-plan), ou une ressource (image, script) bloque l'événement load.

Correction : Vérifiez que le serveur tourne. Essayez waitUntil: 'domcontentloaded' si vous n'avez pas besoin d'attendre toutes les ressources. Utilisez l'onglet Network du Trace Viewer pour voir ce qui bloque.

Timeout d'assertion

Error: expect(locator).toHaveText() with timeout 5000ms
Received string: ""
Expected string: "Order confirmed"

L'assertion n'a pas passé dans le délai imparti. L'élément existe mais a un contenu incorrect (ou pas encore de contenu).

Correction : Le contenu se charge-t-il réellement ? Augmentez temporairement le timeout, par exemple await expect(el).toHaveText('Order confirmed', { timeout: 15000 }). Si ça passe avec plus de temps, vous avez un problème de performance, pas de sélecteur.

Contexte d'exécution détruit

Error: Execution context was destroyed, most likely because of a navigation.

Playwright tenait une référence à un élément de page quand la page a navigué vers une autre URL. Cause fréquente : vous avez fait const text = page.getByText('foo') puis navigué avant d'évaluer la référence.

Correction : Évaluez les locators avant la navigation.

// Cassé — locator évalué après la navigation
const text = page.getByText('Confirmation');
await page.getByRole('button', { name: 'Submit' }).click(); // déclenche la navigation
await expect(text).toBeVisible(); // la page n'est plus là

// Corrigé — asserter avant que la navigation se déclenche, ou re-requêter après
await page.getByRole('button', { name: 'Submit' }).click();
await expect(page).toHaveURL('/confirmation');
await expect(page.getByText('Order confirmed')).toBeVisible();

Target closed

Error: Target page, context or browser has been closed

Le contexte de navigateur a été fermé avant que le test ne se termine. Ça arrive généralement quand :

Un test ferme le contexte manuellement au mauvais endroit, ou un bloc beforeAll a fermé un contexte dont afterAll ou d'autres tests ont encore besoin. Le cas le plus courant est un test qui a planté pendant que son code de nettoyage s'exécutait.

Erreurs réseau

Error: net::ERR_CONNECTION_REFUSED at http://localhost:3000

Le serveur ne tourne pas. Vérifiez :

Vérifiez que votre serveur de développement est démarré avant les tests, que le baseURL est correct, et que le port correspond.

En CI, ajoutez une étape pour démarrer votre serveur avant que Playwright ne s'exécute, ou utilisez webServer dans la config :

// playwright.config.ts
webServer: {
  command: 'npm run start',
  url: 'http://localhost:3000',
  reuseExistingServer: !process.env.CI,
},

Workflow de débogage

Étape 1 : Lisez le type d'erreur et le call log. Le call log vous dit ce que Playwright attendait et ce qu'il a observé. Étape 2 : Ouvrez le Trace Viewer. npx playwright show-trace test-results/.../trace.zip : le snapshot DOM vous montre exactement ce qui était sur la page quand l'échec s'est produit. Étape 3 : Lancez en mode debug (--debug) pour avancer pas à pas dans le test de façon interactive. Étape 4 : Utilisez page.pause() pour figer le test à un point précis et inspecter manuellement.

await page.goto('/checkout');
await page.pause(); // Le test se fige ici, l'Inspector s'ouvre
await page.getByRole('button', { name: 'Pay' }).click();

L'Inspector ouvre un navigateur avec la barre d'outils Playwright : vous pouvez interagir avec la page, lancer des requêtes de locator, et observer les actions en temps réel.

→ See also: Playwright Trace Viewer: Déboguez les Tests Échoués Comme un Pro | Déboguer les Tests Instables: Un Guide Pratique | Assertions dans Playwright: Le Guide Complet