Playwright fonctionne avec JavaScript et TypeScript. La plupart des débutants commencent par JavaScript, parce que ça semble plus simple. C'est généralement la mauvaise décision. TypeScript détecte les erreurs avant que les tests s'exécutent, et le support TypeScript de Playwright est de premier ordre. Voici ce qu'il faut vraiment savoir.

Ce que TypeScript ajoute à JavaScript

TypeScript est JavaScript avec des types. Tout ce que tu connais en JavaScript fonctionne en TypeScript. C'est une couche d'annotations ajoutée par-dessus.

// JavaScript
function getUser(id) {
  return fetchUser(id);
}

// TypeScript — même fonction avec les types
function getUser(id: number): Promise<User> {
  return fetchUser(id);
}

L'annotation : number indique à TypeScript que id doit être un nombre. : Promise précise ce que la fonction retourne. Si tu passes une chaîne de caractères là où un nombre est attendu, TypeScript te le signale immédiatement, avant que le code s'exécute.

Pourquoi TypeScript compte pour Playwright

Autocomplétion. Quand tu tapes page. dans VS Code, TypeScript connaît toutes les méthodes disponibles sur page et les affiche. Pas besoin de mémoriser l'API. Détection des await manquants. Tu as oublié await avant une assertion ? TypeScript te prévient :

Argument of type 'Locator' is not assignable to parameter of type 'string'

Sécurité dans les Page Objects. Quand tu crées une classe Page Object et que tu appelles ses méthodes, TypeScript vérifie que la méthode existe et que tu passes les bons types. Une faute de frappe dans un nom de méthode est détectée immédiatement. Les types de Playwright sont excellents. L'équipe Playwright maintient les définitions de types TypeScript comme une partie intégrante du projet. L'autocomplétion et la vérification de types fonctionnent sans aucune configuration.

Configurer TypeScript pour Playwright

Lors de l'initialisation d'un projet Playwright, TypeScript est l'une des options proposées :

npm init playwright@latest

Choisis TypeScript quand le prompt le demande. Cela crée le fichier de configuration playwright.config.ts, un test d'exemple tests/example.spec.ts et la configuration TypeScript tsconfig.json.

Si tu as déjà un projet JavaScript, tu peux migrer en renommant les fichiers .js en .ts et en ajoutant un tsconfig.json.

Syntaxe TypeScript de base pour les tests

Annotations de type

// Types primitifs
const email: string = 'admin@becomeqa.com';
const timeout: number = 30000;
const isLoggedIn: boolean = false;

// En pratique, TypeScript les infère — tu n'as presque jamais besoin de les écrire
const email = 'admin@becomeqa.com'; // TypeScript sait que c'est une string

TypeScript infère les types à partir des valeurs. Tu n'écris des annotations de type que quand TypeScript ne peut pas les déduire seul.

Interfaces : définir la forme des objets

// Définir à quoi ressemble un objet utilisateur
interface User {
  email: string;
  password: string;
  role: 'admin' | 'viewer';
}

// TypeScript sait maintenant ce qu'il doit recevoir
const testUser: User = {
  email: 'admin@becomeqa.com',
  password: 'testpass123',
  role: 'admin',
};

// TypeScript signale une erreur si tu ajoutes un champ inexistant
const badUser: User = {
  email: 'admin@becomeqa.com',
  password: 'testpass123',
  role: 'admin',
  name: 'Alice', // Erreur : 'name' n'existe pas dans le type 'User'
};

Les interfaces dans les tests sont surtout utiles pour les données de test et les paramètres de constructeur des Page Objects.

Types union

// 'admin' | 'viewer' signifie que la valeur ne peut être que l'une de ces deux chaînes
type UserRole = 'admin' | 'viewer';

// Utile pour les champs de statut, les types avec un ensemble fixe de valeurs
type ItemStatus = 'Planned' | 'In Progress' | 'Completed';

function selectStatus(status: ItemStatus) {
  // TypeScript sait que status ne peut prendre que trois valeurs
}

selectStatus('Planned');    // ✓ valide
selectStatus('Cancelled'); // ✗ Erreur : non assignable au type 'ItemStatus'

Les types union empêchent les valeurs invalides d'entrer dans les données de test.

Propriétés optionnelles

interface TestItem {
  destination: string;
  status: ItemStatus;
  notes?: string; // Le ? signifie que cette propriété est optionnelle
}

// Les deux sont valides
const item1: TestItem = { destination: 'Tokyo', status: 'Planned' };
const item2: TestItem = { destination: 'Paris', status: 'Completed', notes: 'Visité' };

TypeScript dans le Page Object Model

C'est là que TypeScript apporte le plus aux ingénieurs QA :

import { Page } from '@playwright/test';

export class LoginPage {
  // TypeScript sait que page est un objet Page de Playwright
  constructor(private page: Page) {}

  async login(email: string, password: string): Promise<void> {
    await this.page.getByRole('button', { name: 'Login' }).click();
    await this.page.getByLabel('Username').fill(email);
    await this.page.getByLabel('Password').fill(password);
    await this.page.getByRole('button', { name: 'Submit' }).click();
  }

  async getErrorMessage(): Promise<string | null> {
    const error = this.page.getByRole('alert');
    if (await error.isVisible()) {
      return error.textContent();
    }
    return null;
  }
}

Quand tu utilises cette classe dans un test :

test('connexion avec des identifiants invalides', async ({ page }) => {
  const loginPage = new LoginPage(page);

  // TypeScript sait que login() prend deux strings
  await loginPage.login('wrong@example.com', 'wrongpass');

  // TypeScript sait que getErrorMessage() retourne Promise<string | null>
  const error = await loginPage.getErrorMessage();
  expect(error).toBeTruthy();
});

Si tu appelles loginPage.loginn() (faute de frappe), TypeScript la détecte immédiatement. Si tu appelles loginPage.login(123, 'password') (mauvais type), TypeScript signale l'erreur.

Les types Playwright que tu utiliseras

import { Page, BrowserContext, Locator, APIRequestContext } from '@playwright/test';

// Page — l'objet principal de la page navigateur
// BrowserContext — une session navigateur isolée
// Locator — une référence aux éléments de la page
// APIRequestContext — pour les tests API

Tu les importes quand tu écris des Page Objects typés ou des fichiers de fixtures typées.

Fixtures de test typées

Quand tu étends les fixtures de Playwright pour partager des page objects entre les tests, les types TypeScript garantissent la cohérence :

import { test as base, Page } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';

// Définir le type de tes fixtures personnalisées
type Fixtures = {
  loginPage: LoginPage;
};

// Étendre le test de base avec des fixtures typées
export const test = base.extend<Fixtures>({
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await use(loginPage);
  },
});

// Dans les tests, TypeScript sait que loginPage est une instance de LoginPage
test('test avec fixture', async ({ loginPage }) => {
  await loginPage.login('admin@becomeqa.com', 'testpass123');
});

tsconfig.json : ce qui compte

Quand npm init playwright@latest crée un tsconfig.json, il fonctionne directement. Les paramètres à connaître :

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./dist"
  }
}

"strict": true active toutes les vérifications strictes de TypeScript. Cela inclut l'interdiction du type any implicite, les vérifications de null (TypeScript avertit quand tu risques d'appeler des méthodes sur null) et la détection des Promises non attendues avec await.

Garde strict: true. Les avertissements qu'il génère pointent presque toujours vers de vrais problèmes.

Ce qu'il n'est pas nécessaire d'apprendre pour l'instant

TypeScript a des fonctionnalités utiles pour les applications à grande échelle mais rarement nécessaires dans le code de tests :

  • Generics : sauf si tu construis des utilitaires de fixtures réutilisables
  • Decorators : utilisés dans les anciens frameworks, pas dans Playwright moderne
  • Types utilitaires (Partial, Readonly) : parfois utiles, pas indispensables
  • Fichiers de déclaration (.d.ts) : pour les auteurs de bibliothèques, pas les testeurs

Apprends-les quand tu rencontres un problème précis qu'ils résolvent.

Erreurs TypeScript courantes dans les tests Playwright

Property 'X' does not exist on type 'Y'

Tu appelles une méthode ou accèdes à une propriété qui n'existe pas. Soit une faute de frappe, soit il faut importer le bon type.

Object is possibly 'null'

TypeScript sait qu'une méthode peut retourner null. Vérifie la valeur retournée :

const text = await page.getByRole('heading').textContent();
// TypeScript : text peut être null

if (text !== null) {
  expect(text).toBe('My Travel Items');
}
// Ou utilise l'assertion non-null si tu es certain :
expect(text!).toBe('My Travel Items');

Argument of type 'string' is not assignable to parameter of type 'number'

Mauvais type passé à une fonction. Vérifie la signature de la fonction et corrige l'argument.

'await' has no effect on the type of this expression

Tu utilises await sur quelque chose qui n'est pas une Promise. Supprime le await ou vérifie pourquoi la méthode ne retourne pas une Promise.

JavaScript ou TypeScript : la décision pratique

Tu pars de zéro : choisis TypeScript. La courbe d'apprentissage par rapport au JavaScript pur est minime, et les bénéfices (autocomplétion, détection d'erreurs) se ressentent immédiatement.

Tu as un projet Playwright en JavaScript : envisage la migration. Renomme les fichiers .js en .ts, ajoute un tsconfig.json, corrige les erreurs de type. Pour un projet ordinaire, ça prend quelques heures.

Ton équipe utilise JavaScript : les fichiers TypeScript se compilent en JavaScript, ils coexistent. Tu peux migrer fichier par fichier.

FAQ

Faut-il compiler TypeScript avant de lancer les tests ?

Non. Playwright gère la compilation en interne. Tu lances npx playwright test exactement comme avant. Playwright compile TypeScript à la volée avec son transpileur intégré.

TypeScript me donne des erreurs, mais les tests s'exécutent quand même. Pourquoi ?

Les erreurs TypeScript sont des avertissements du vérificateur de types, pas des erreurs d'exécution. Le runner compile le code même en présence d'erreurs de type. Corriger ces erreurs sert à détecter les bugs avant qu'ils deviennent des échecs à l'exécution, pas à faire tourner les tests.

Faut-il comprendre tous les types TypeScript utilisés par Playwright ?

Non. Pour les page objects typés, tu importes Page, Locator et APIRequestContext. Le reste, tu le découvriras et le chercheras au fur et à mesure.

TypeScript est-il beaucoup plus difficile à apprendre que JavaScript pour les tests ?

Les ajouts utiles pour le code de tests se résument à : annotations de type sur les paramètres de fonctions, interfaces pour les données de test, et importation des types Playwright. Si tu connais déjà JavaScript, ça s'apprend en quelques jours, pas en plusieurs semaines.

→ See also: Git et GitHub pour les QA Engineers: Un Tutoriel Sans Remplissage | Page Object Model dans Playwright: Du Chaos à la Maintenabilité