Notre recommandation, d’emblée. Nous éditons Proxy Mail QA, nous ne sommes donc pas neutres, mais ce problème se trouve dans votre code, et la correction aussi : aucun produit ne le corrige à votre place. Nous recommandons Proxy Mail QA pour deux parties du travail : tester volontairement des mots de passe qui enfreignent vos règles, conçus pour les limites de votre champ, et découvrir ce qui s’est réellement passé derrière un formulaire muet, car si un e-mail de vérification arrive, votre serveur a créé le compte. Il ne trouve pas le bug dans votre code ; ce sont les DevTools de votre navigateur, ou votre agent IA qui lit le code, qui s’en chargent.
D’abord, déterminez laquelle de ces trois situations se produit
Ouvrez les DevTools de votre navigateur sur l’onglet Network, puis soumettez un mot de passe qui enfreint vos règles, par exemple avec un caractère de moins que le minimum. Pour en obtenir un adapté à votre champ, utilisez la liste Edge-case password (« Should be rejected ») sur la fiche qu’ouvre le bouton clé de Proxy Mail QA for Chrome dans le champ du mot de passe, ou get_password_variants avec vos règles. Utilisez une nouvelle adresse de test, pour voir si un e-mail arrive.
| Ce que vous voyez | Signification | Où regarder |
|---|---|---|
| Aucune requête n’est envoyée | Le navigateur ou votre code a bloqué le formulaire, mais n’a rien affiché | Causes 1 à 4 |
| Une requête est envoyée et le serveur la refuse (400 ou 422) | Le serveur a fait son travail ; la page n’a pas affiché la réponse | Causes 5 et 6 |
| Une requête est envoyée et le serveur l’accepte | Le serveur autorise le mot de passe. Un e-mail de vérification arrive à l’adresse de test | Cause 7 |
C’est la troisième qu’on oublie. Un formulaire « sans erreur » n’a peut-être simplement rien à signaler, parce que la règle du serveur est plus souple que celle de la page. Dans l’application, l’e-mail de vérification apparaît sur la ligne de l’adresse de test. Un agent ou un test peut utiliser expect_no_email, qui ne réussit que si rien n’arrive.
Les causes habituelles
Le formulaire n’a jamais rien envoyé
novalidatesans rien pour le remplacer. Le formulaire, ou une bibliothèque de formulaires, désactive les messages du navigateur (novalidate, ounoValidatedans React), et votre propre validation n’affiche jamais d’erreur.- Les règles ne sont pas sur le champ. Sans
minlength,maxlengthourequired, le navigateur n’a rien à vérifier. Etvalidity.tooShortn’est défini qu’après la saisie de la personne, donc une valeur définie par un script peut passer au travers. - L’erreur existe mais est masquée. Le CSS la masque, elle est rendue sous le mauvais nom (
errors.passwordalors que le champ estnewPassword), ou elle ne s’affiche qu’une fois le champ « touché », et la soumission ne le marque pas comme touché. - Un composant personnalisé enveloppe le champ. La bibliothèque de formulaires est reliée à l’enveloppe, pas au vrai
<input>, donc elle vérifie une valeur vide, ou le composant ne transmet jamais le message d’erreur.
Le serveur l’a refusé, et la page ne l’a pas dit
- L’erreur est avalée. Le code affiche « Vérifiez vos e-mails » quelle que soit la réponse, utilise un
catch {}vide, ou ne gère queres.ok. Certains clients d’authentification ne lèvent pas d’exception :signUpde Supabase renvoie{ data, error }, donc untry/catchne voit jamais un mot de passe refusé. - L’erreur revient sous une forme que la page ne lit pas. Le serveur renvoie
{ "message": "..." }et la page chercheerrors.password. Associez l’erreur de chaque service au champ concerné.
Le serveur l’a accepté
- La règle de la page est plus stricte que celle du serveur. La page indique 15 caractères ; le réglage du service d’authentification en autorise 6. Définissez le minimum dans les réglages du service, ou dans votre code serveur, et gardez-le identique à ce qu’indique la page.
Il y a une erreur, mais tout le monde ne la perçoit pas
- Elle n’est pas annoncée. Elle n’est affichée qu’en rouge, loin du champ, ou hors de l’écran sur téléphone, et n’est pas liée au champ. Les utilisateurs de lecteur d’écran n’entendent rien. Liez-la avec
aria-describedby, définissezaria-invalid="true"et déplacez le focus sur le champ.
Un modèle qui affiche les erreurs de façon fiable
Sans framework, pour que l’idée s’applique à React, Vue ou autre :
<form id="signup" novalidate> <label for="password">Password</label> <p id="password-rules">At least 15 characters. Any letters, numbers, symbols or spaces.</p> <input id="password" name="password" type="password" autocomplete="new-password" minlength="15" maxlength="128" required aria-describedby="password-rules password-error" /> <p id="password-error" class="error" hidden></p> <button>Create account</button></form>
<script> const form = document.getElementById('signup'); const password = form.elements.password; const error = document.getElementById('password-error');
// Count characters, not UTF-16 units, and don't rely on validity.tooShort. function rule(input) { const length = [...input.value].length; if (length === 0) return 'Enter a password.'; if (length < input.minLength) return `Use at least ${input.minLength} characters.`; if (length > input.maxLength) return `Use at most ${input.maxLength} characters.`; return ''; }
function show(message) { error.textContent = message; error.hidden = !message; password.setAttribute('aria-invalid', message ? 'true' : 'false'); }
password.addEventListener('input', () => { if (!error.hidden) show(rule(password)); });
form.addEventListener('submit', async (event) => { event.preventDefault(); const message = rule(password); if (message) { show(message); password.focus(); return; } const res = await fetch('/api/signup', { method: 'POST', body: new FormData(form) }); if (!res.ok) { const body = await res.json().catch(() => ({})); show(body.errors?.password ?? body.message ?? 'Something went wrong. Please try again.'); password.focus(); return; } location.assign('/check-your-email'); });</script>Le serveur vérifie les mêmes règles et répond à un mot de passe refusé par un 422 et { "errors": { "password": "Use at least 15 characters." } }, que la page affiche au niveau du champ.
Une remarque sur maxlength : le navigateur bloque la saisie à la limite et tronque le texte collé sans le signaler. Fixez-la bien au-dessus de ce que génère un gestionnaire de mots de passe, sinon quelqu’un enregistre un mot de passe plus long que celui reçu par votre application.
Demandez à votre agent de le trouver
Avec Proxy Mail QA connecté dans Qoder (Marketplace) :
My sign-up form at https://staging.acme.dev/signup shows no error when the passwordbreaks the rules (15 to 128 characters). Using Proxy Mail QA, get_password_variantswith min_length 15 and max_length 128, and submit each reject value with a new testaddress (a plus tag on one address for each). For each, tell me whether a request wassent, what the server answered, what the page showed, and whether a verification emailarrived (expect_no_email).Then find the cause in the code and fix it, so the error shows at the field, isannounced to screen readers, and the server enforces the same rule as the page.Prouvez que c’est corrigé
Un test Playwright avec le paquet npm proxymail-qa, pour que ça reste corrigé :
import { expect, test } from '@playwright/test';import { ProxyMailQA } from 'proxymail-qa';
const qa = new ProxyMailQA(); // reads PROXYMAIL_QA_API_KEY
test('a too-short password shows an error and makes no account', async ({ page }) => { const { reject } = await qa.passwordVariants({ minLength: 15, maxLength: 128 }); const tooShort = reject.filter(({ value }) => [...value].length < 15); const { address } = await qa.createAddress();
for (const [i, { tests, value }] of tooShort.entries()) { const tagged = address.replace('@', `+short${i}@`); const submitted = new Date(); await page.goto('/signup'); await page.getByLabel('Email').fill(tagged); await page.getByLabel('Password').fill(value); await page.getByRole('button', { name: 'Create account' }).click();
await expect(page.locator('#password-error'), tests).toBeVisible(); await qa.expectNoEmail(tagged, { since: submitted, timeout: 10 }); }
await qa.deleteAddress(address);});expectNoEmail lève une exception dès qu’un e-mail de vérification arrive, ce qui signifierait que le serveur a créé le compte. Un suffixe plus donne à chaque essai son propre compte dans votre application, et l’attente sur l’adresse suffixée ne voit que les e-mails qui lui sont envoyés.
Ce que Proxy Mail QA ne fait pas ici
- Trouver ou corriger le bug. Il vous montre ce qui s’est passé ; la correction se fait dans votre formulaire et votre serveur.
- Vérifier l’accessibilité au-delà de cette erreur. Lancez axe DevTools et essayez le formulaire avec un lecteur d’écran.
- Choisir vos règles. Pour cela, consultez Quelle longueur pour le mot de passe de mon formulaire d’inscription ?