Adloun

Vue : construire une application web (école d'ingénieur) · Chapitre 12 · Exercice de TD

Deux clics, une commande

Projet : la Librairie en application Vue · niveau 2

Énoncé

Dans une première version de la page de commande, valider ne contenait pas la ligne if (envoi.value) return;. Le bouton d'envoi était pourtant désactivé pendant l'envoi. Écrire un test, avec un faux serveur qui ne répond que sur ordre, qui vérifie qu'une seule commande part quand l'utilisateur insiste. Que montre-t-il sur la première version ?

Corrigé


// Exercice 12.2 : deux clics rapides, une seule commande.
import { test, expect, vi, afterEach } from "vitest";
import { mount, flushPromises } from "@vue/test-utils";
import PageCommande from "@/pages/PageCommande.vue";
import { usePanierStore } from "@/stores/panier.js";
import { preparer, livre } from "./aide.js";

afterEach(() => vi.unstubAllGlobals());

test("le bouton est désactivé pendant l'envoi", async () => {
  let repondre;
  // Un serveur lent : la réponse n'arrive que quand le test appelle repondre().
  const fetch = vi.fn(() => new Promise((r) => { repondre = r; }));
  vi.stubGlobal("fetch", fetch);
  const { plugins } = await preparer();
  usePanierStore().ajouter(livre());
  const page = mount(PageCommande, { global: { plugins } });
  await page.find("#nom").setValue("Ada Lovelace");
  await page.find("#courriel").setValue("ada@example.org");

  await page.find("form").trigger("submit");
  const bouton = page.find("button[type='submit']");
  console.info("pendant l'envoi :", bouton.text(), "| désactivé :", bouton.element.disabled,
    "| aria-busy :", page.find("form").attributes("aria-busy"));
  await bouton.trigger("click"); // second clic : ignoré, le bouton est désactivé
  await page.find("form").trigger("submit"); // même un envoi forcé du formulaire…
  console.info("requêtes envoyées :", fetch.mock.calls.length);

  repondre({ ok: true, status: 201, json: async () => ({ numero: 1001, total: 8.5 }) });
  await flushPromises();
  console.info("après la réponse :", page.find(".confirmation h2").text());
  expect(fetch).toHaveBeenCalledTimes(1);
});

Sur la première version, le test échoue :


pendant l'envoi : Envoi en cours… | désactivé : true | aria-busy : true
requêtes envoyées : 2
après la réponse : Commande n° 1001 enregistrée
AssertionError: expected "vi.fn()" to be called 1 times, but got 2 times

Le bouton est bien désactivé, et le second clic n'a rien produit ; mais le formulaire, lui, peut toujours être envoyé : le test le fait directement (événement submit), comme le ferait un script (requestSubmit()) ou un second bouton d'envoi. Le navigateur, lui, ignore la touche Entrée tant que le bouton d'envoi par défaut est désactivé (vérifié dans Chromium) ; mais on ne bâtit pas une protection sur ce détail. Désactiver le bouton est une indication visuelle, pas une protection. La ligne if (envoi.value) return; au début de valider règle le problème :


pendant l'envoi : Envoi en cours… | désactivé : true | aria-busy : true
requêtes envoyées : 1
après la réponse : Commande n° 1001 enregistrée

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 →