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ée100 % gratuit
La suite du chapitre est sur Adloun
- 9 autres exercices corrigés dans ce chapitre
- Les 2 QCM du chapitre, le premier sans compte
- Le tuteur, qui guide pas à pas sans donner la réponse
Sans compte : le cours et le premier QCM. Exercices et tuteur : inscription gratuite en 30 secondes, sans carte bancaire.