Adloun

Vue : construire une application web (école d'ingénieur) · Chapitre 12 · Application directe du cours

Revue d'accessibilité

Projet : la Librairie en application Vue · niveau 2

Énoncé

Écrire avec Playwright une revue d'accessibilité automatique de la Librairie :

  • relever l'ordre de tabulation de la page de commande, panier rempli ;
  • vérifier sur chaque page la langue du document, la présence des repères (en-tête, navigation, contenu principal, pied de page), un seul titre principal, et un attribut alt sur chaque image.

Commenter l'ordre de tabulation obtenu.

Corrigé

La revue est dans code/ch12/librairie/e2e/accessibilite.spec.js :


import { test, expect } from "@playwright/test";

/** Nom court de l'élément qui a le focus : balise et texte ou étiquette. */
const focus = (page) => page.evaluate(() => {
  const e = document.activeElement;
  if (e === document.body) return "(hors de la page : barre d'adresse du navigateur)";
  const nom = e.getAttribute("aria-label") || e.labels?.[0]?.textContent || e.textContent;
  return `${e.tagName.toLowerCase()} « ${nom.replace(/\s+/g, " ").trim()} »`;
});

test("ordre de tabulation de la page de commande", async ({ page }) => {
  await page.goto("/");
  await page.getByRole("button", { name: /Ajouter au panier.*Le Petit Prince/ }).click();
  await page.goto("/commande");
  const ordre = [];
  for (let i = 0; i < 11; i++) {
    await page.keyboard.press("Tab");
    ordre.push(`${String(i + 1).padStart(2)}. ${await focus(page)}`);
  }
  console.log(ordre.join("\n"));
  expect(ordre[0]).toContain("Aller au contenu");
});

test("repères, titres et images de chaque page", async ({ page }) => {
  for (const adresse of ["/", "/livres/9782956300052", "/panier", "/nulle-part"]) {
    await page.goto(adresse);
    await page.waitForLoadState("networkidle");
    const bilan = await page.evaluate(() => ({
      lang: document.documentElement.lang,
      reperes: ["header", "nav", "main", "footer"].map((t) => document.querySelectorAll(t).length).join("/"),
      h1: [...document.querySelectorAll("h1")].map((h) => h.textContent.trim()),
      imagesSansAlt: [...document.images].filter((i) => !i.hasAttribute("alt")).length,
    }));
    console.log(`${adresse.padEnd(22)} lang=${bilan.lang} header/nav/main/footer=${bilan.reperes} h1=${JSON.stringify(bilan.h1)} images sans alt=${bilan.imagesSansAlt}`);
    expect(bilan.h1).toHaveLength(1);
    expect(bilan.imagesSansAlt).toBe(0);
  }
});

Running 2 tests using 1 worker

 1. a « Aller au contenu »
 2. a « La Librairie du Campus »
 3. a « Catalogue »
 4. a « Panier (1) »
 5. a « Contact »
 6. input « En boutique (gratuit) »
 7. input « Nom »
 8. input « Courriel »
 9. button « Valider la commande »
10. (hors de la page : barre d'adresse du navigateur)
11. a « Aller au contenu »
  ✓  1 e2e/accessibilite.spec.js:11:1 › ordre de tabulation de la page de commande (544ms)
/                      lang=fr header/nav/main/footer=1/1/1/1 h1=["Catalogue"] images sans alt=0
/livres/9782956300052  lang=fr header/nav/main/footer=1/1/1/1 h1=["Le Petit Prince"] images sans alt=0
/panier                lang=fr header/nav/main/footer=1/1/1/1 h1=["Votre panier"] images sans alt=0
/nulle-part            lang=fr header/nav/main/footer=1/1/1/1 h1=["Page introuvable"] images sans alt=0
  ✓  2 e2e/accessibilite.spec.js:24:1 › repères, titres et images de chaque page (2.2s)

  2 passed (4.2s)

L'ordre de tabulation suit l'ordre de lecture : le lien d'évitement d'abord (il permet de sauter l'en-tête), puis le menu, puis le formulaire. Les deux boutons radio du retrait ne forment qu'un seul arrêt : on passe de l'un à l'autre avec les flèches, comme le veut le comportement standard d'un groupe radio. Le bouton « Menu » n'apparaît pas : sur grand écran il est masqué, et un élément masqué ne reçoit pas le focus. Après le bouton d'envoi, le focus quitte la page pour la barre d'adresse, puis revient au début. Sur les quatre pages, les repères sont présents une fois chacun, chaque page a un seul titre principal et aucune image n'est privée de texte de remplacement.

100 % gratuit

La suite du chapitre est sur Adloun

Exercice suivant et tuteur

Sans compte : le cours et le premier QCM. Exercices et tuteur : inscription gratuite en 30 secondes, sans carte bancaire.

Le cours du chapitre · Tous les exercices du chapitre

Exercice suivant et tuteur →