Playwright expose les événements WebSocket via page.on('websocket'), ce qui permet de vérifier l'établissement de la connexion et d'inspecter les messages entrants et sortants. Il permet aussi de simuler des réponses serveur pour tester des fonctionnalités temps réel sans serveur actif.
Ce que ressemble le test WebSocket en pratique
Contrairement aux requêtes HTTP, les WebSockets sont des connexions bidirectionnelles persistantes. Une seule connexion peut transporter des milliers de messages pendant sa durée de vie. Tester des WebSockets signifie :
1. Vérifier que la connexion est établie
2. Vérifier que l'application envoie les bons messages
3. Vérifier que l'application gère correctement les messages reçus
4. Tester ce qui se passe quand la connexion tombe
Écouter les événements WebSocket
Playwright déclenche des événements pour les connexions WebSocket :
import { test, expect } from '@playwright/test';
test('l\'application de chat établit une connexion WebSocket', async ({ page }) => {
// Écouter les connexions WebSocket
const wsConnected = page.waitForEvent('websocket');
await page.goto('/chat');
const ws = await wsConnected;
expect(ws.url()).toContain('/ws/chat');
console.log('Connecté à :', ws.url());
});Capturer les messages WebSocket
test('le chat envoie un message via WebSocket', async ({ page }) => {
const messages: string[] = [];
page.on('websocket', ws => {
ws.on('framesent', frame => {
// Messages envoyés du navigateur vers le serveur
if (frame.text) messages.push(frame.text);
});
});
await page.goto('/chat');
await page.getByPlaceholder('Écris un message').fill('Bonjour !');
await page.keyboard.press('Enter');
// Vérifier que le message WebSocket a été envoyé
await expect.poll(() => messages).toContain(
JSON.stringify({ type: 'message', content: 'Bonjour !' })
);
});
test('le chat reçoit un message et l\'affiche', async ({ page }) => {
const receivedFrames: string[] = [];
page.on('websocket', ws => {
ws.on('framereceived', frame => {
// Messages reçus du serveur par le navigateur
if (frame.text) receivedFrames.push(frame.text);
});
});
await page.goto('/chat');
// Attendre qu'au moins un message arrive (confirmation de connexion, par exemple)
await expect.poll(() => receivedFrames.length).toBeGreaterThan(0);
// Vérifier que le message est affiché dans l'interface
const firstMessage = JSON.parse(receivedFrames[0]);
await expect(page.getByText(firstMessage.content)).toBeVisible();
});framesent : messages que le navigateur a envoyés au serveur.
framereceived : messages que le serveur a envoyés au navigateur.
Simuler des réponses WebSocket
Le pattern le plus puissant : intercepter la connexion et simuler des messages serveur sans faire tourner un vrai serveur.
Playwright n'a pas d'API native de mock WebSocket, mais tu peux injecter un mock dans le contexte du navigateur :
test('l\'application affiche une notification quand le serveur envoie une alerte', async ({ page }) => {
await page.addInitScript(() => {
const OriginalWebSocket = window.WebSocket;
window.WebSocket = class MockWebSocket extends EventTarget {
url: string;
readyState = 1; // OPEN
constructor(url: string) {
super();
this.url = url;
// Simuler l'ouverture de la connexion
setTimeout(() => {
this.dispatchEvent(new Event('open'));
// Simuler un message serveur après 100ms
setTimeout(() => {
const messageEvent = new MessageEvent('message', {
data: JSON.stringify({ type: 'notification', text: 'Nouvelle commande reçue !' }),
});
this.dispatchEvent(messageEvent);
}, 100);
}, 0);
}
send(data: string) {
console.log('WebSocket send:', data);
}
close() {
this.readyState = 3;
this.dispatchEvent(new CloseEvent('close'));
}
} as any;
});
await page.goto('/dashboard');
// Vérifier que la notification apparaît quand le message WebSocket arrive
await expect(page.getByRole('alert')).toBeVisible({ timeout: 2000 });
await expect(page.getByRole('alert')).toHaveText('Nouvelle commande reçue !');
});Tester la déconnexion et la reconnexion
Les applications temps réel doivent gérer la perte de connexion sans casser l'expérience utilisateur :
test('l\'application affiche un état "reconnexion" quand le WebSocket se coupe', async ({ page }) => {
await page.addInitScript(() => {
const OriginalWebSocket = window.WebSocket;
window.WebSocket = class extends OriginalWebSocket {
constructor(url: string) {
super(url);
// Exposer l'instance pour le contrôle du test
(window as any).__wsInstance = this;
}
} as any;
});
await page.goto('/chat');
// Attendre l'établissement de la connexion
await page.waitForEvent('websocket');
// Forcer la fermeture de la connexion WebSocket
await page.evaluate(() => {
(window as any).__wsInstance?.close();
});
// Vérifier que l'interface affiche l'état de reconnexion
await expect(page.getByText('Reconnexion...')).toBeVisible();
// Vérifier que la reconnexion aboutit
await expect(page.getByText('Connecté')).toBeVisible({ timeout: 10000 });
});Utiliser routeWebSocket dans les versions récentes de Playwright
Playwright 1.48+ a introduit page.routeWebSocket() pour un mock WebSocket plus ergonomique :
// Playwright 1.48+
test('le statut d\'une commande se met à jour en temps réel', async ({ page }) => {
await page.routeWebSocket('/ws/orders', ws => {
ws.onopen = () => {
// Envoyer l'état initial
ws.send(JSON.stringify({ orderId: '123', status: 'processing' }));
};
ws.onmessage = (message) => {
const data = JSON.parse(message.data);
if (data.type === 'subscribe' && data.orderId === '123') {
// Simuler la progression du statut
setTimeout(() => ws.send(JSON.stringify({ orderId: '123', status: 'shipped' })), 500);
setTimeout(() => ws.send(JSON.stringify({ orderId: '123', status: 'delivered' })), 1000);
}
};
});
await page.goto('/orders/123');
await expect(page.getByTestId('status')).toHaveText('Processing');
await expect(page.getByTestId('status')).toHaveText('Shipped', { timeout: 2000 });
await expect(page.getByTestId('status')).toHaveText('Delivered', { timeout: 3000 });
});Vérifie ta version de Playwright avant d'utiliser routeWebSocket : elle n'est pas disponible dans les versions anciennes.
Quand tester les WebSockets
Toutes les fonctionnalités temps réel ne nécessitent pas des tests WebSocket dédiés. Priorités :
- Établissement de la connexion pour les fonctionnalités critiques (chat, tableau de bord live)
- Comportement lors d'une perte de connexion (affiche une erreur ? reconnecte ? perd des données ?)
- Ordre des messages pour les opérations séquentielles
- Authentification via WebSocket (la connexion est-elle refusée sans auth valide ?)
Passe les tests au niveau WebSocket quand la fonctionnalité est fiable et que le test E2E UI la couvre déjà. C'est aussi le bon choix si le WebSocket est tiers ou si le format des messages est mieux couvert par un test unitaire backend.
→ See also: Interception Réseau, Mocking et Stubbing dans Playwright | Tests d'API avec l'APIRequestContext de Playwright (Sans Postman) | Tests d'API GraphQL avec Playwright: Requêtes, Mutations et Gestion des Erreurs