Traducido automáticamente del inglés.

Leer en inglés
Proxy Mail QA

¿Por qué mi formulario de registro no muestra un error para contraseñas que no cumplen los criterios?

Alguien escribe una contraseña demasiado corta, presiona **Crear cuenta** y no pasa nada. O la página avanza como si todo estuviera bien. La causa casi siempre es una de ocho, y una prueba te dice en qué grupo buscar.

Actualizado el

Nuestra recomendación, desde el principio. Hacemos Proxy Mail QA, así que no somos neutrales, pero este problema está en tu código y la solución también: ningún producto lo arregla por ti. Recomendamos Proxy Mail QA para dos partes del trabajo: probar contraseñas que incumplen tus reglas a propósito, hechas para los límites de tu campo, y averiguar qué pasó de verdad detrás de un formulario silencioso, porque si llega un correo de verificación, tu servidor creó la cuenta. No encuentra el error en tu código; eso lo hacen las DevTools de tu navegador o tu agente de IA leyendo el código.

Primero, averigua cuál de tres cosas está pasando

Abre las DevTools de tu navegador en la pestaña Network y envía una contraseña que incumpla tus reglas, como una con un carácter menos del mínimo. Para obtener una hecha para tu campo, usa la lista Edge-case password (“Should be rejected”) en la tarjeta que abre el botón de llave de Proxy Mail QA for Chrome en el campo de contraseña, o get_password_variants con tus reglas. Usa una dirección de prueba nueva, para ver si llega un correo.

Lo que ves Qué significa Revisa
No se envía ninguna solicitud El navegador o tu código detuvo el formulario, pero no mostró nada Causas 1 a 4
Se envía una solicitud y el servidor la rechaza (un 400 o 422) El servidor hizo su trabajo; la página no mostró la respuesta Causas 5 y 6
Se envía una solicitud y el servidor la acepta El servidor permite la contraseña. Llega un correo de verificación a la dirección de prueba Causa 7

La tercera es la que se pasa por alto. Un formulario “sin error” puede simplemente no tener nada que informar, porque la regla del servidor es más laxa que la de la página. En la app, el correo de verificación aparece en la fila de la dirección de prueba. Un agente o una prueba pueden usar expect_no_email, que pasa solo si no llega nada.

Las causas habituales

El formulario nunca envió nada

  1. novalidate sin nada en su lugar. El formulario, o una librería de formularios, desactiva los mensajes propios del navegador (novalidate, o noValidate en React), y tu propia validación nunca muestra un error.
  2. Las reglas no están en el input. Sin minlength, maxlength o required, el navegador no tiene nada que revisar. Y validity.tooShort solo se activa después de que la persona escribe, así que un valor puesto por un script puede pasar sin control.
  3. El error existe pero está oculto. El CSS lo oculta, se muestra con el nombre equivocado (errors.password cuando el campo es newPassword), o solo aparece cuando el campo está “tocado” y enviar no lo marca como tocado.
  4. Un componente propio envuelve el input. La librería de formularios está conectada al contenedor, no al <input> real, así que revisa un valor vacío, o el componente nunca pasa el mensaje de error.

El servidor lo rechazó y la página no lo dijo

  1. El error se pierde. El código muestra “Revisa tu correo” sea cual sea la respuesta, usa catch {} vacío, o solo maneja res.ok. Algunos clientes de autenticación no lanzan excepciones: signUp de Supabase devuelve { data, error }, así que un try/catch nunca ve una contraseña rechazada.
  2. El error vuelve con una forma que la página no lee. El servidor dice { "message": "..." } y la página busca errors.password. Asigna el error de cada servicio al campo al que se refiere.

El servidor lo aceptó

  1. La regla de la página es más estricta que la del servidor. La página dice 15 caracteres; la configuración del servicio de autenticación permite 6. Define el mínimo en la configuración del servicio, o en tu propio código de servidor, y mantenlo igual a lo que dice la página.

Hay un error, pero no todos pueden notarlo

  1. No se anuncia. Solo se muestra en rojo, lejos del campo, o fuera de la pantalla en un teléfono, y no está vinculado al campo. Quienes usan lectores de pantalla no oyen nada. Vincúlalo con aria-describedby, define aria-invalid="true" y mueve el foco al campo.

Un patrón que muestra los errores de forma fiable

Sin framework, para que la idea sirva en React, Vue o cualquier otro:

<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>

El servidor revisa las mismas reglas y responde a una contraseña rechazada con un 422 y { "errors": { "password": "Use at least 15 characters." } }, que la página muestra en el campo.

Una nota sobre maxlength: el navegador deja de aceptar texto en el límite y recorta el texto pegado sin avisar. Ponlo muy por encima de lo que genera un gestor de contraseñas, o alguien guardará una contraseña más larga que la que recibió tu app.

Pídele a tu agente que lo encuentre

Con Proxy Mail QA conectado en Qoder (Marketplace):

My sign-up form at https://staging.acme.dev/signup shows no error when the password
breaks the rules (15 to 128 characters). Using Proxy Mail QA, get_password_variants
with min_length 15 and max_length 128, and submit each reject value with a new test
address (a plus tag on one address for each). For each, tell me whether a request was
sent, what the server answered, what the page showed, and whether a verification email
arrived (expect_no_email).
Then find the cause in the code and fix it, so the error shows at the field, is
announced to screen readers, and the server enforces the same rule as the page.

Comprueba que está arreglado

Una prueba de Playwright con el paquete npm de proxymail-qa, para que siga arreglado:

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 lanza un error en cuanto llega un correo de verificación, lo que significaría que el servidor creó la cuenta. Una etiqueta con + da a cada intento su propia cuenta en tu app, y esperar en la dirección etiquetada solo ve el correo enviado a ella.

Lo que Proxy Mail QA no hace aquí

Úsalo en tu próxima prueba de registro

Plan gratuito, sin tarjeta de crédito. Crea una dirección y mira cómo llega el código o el enlace.

Crear una cuenta gratuitaAbrir la aplicación

¿Creaste tu app con una herramienta de IA? Elige la tuya

¿Tienes una pregunta sobre esta página?

Pregunta en cualquier idioma. La documentación puede responderte de inmediato, o puedes enviar tu pregunta al equipo de Proxy Mail QA, que responde en inglés.