import { expect, test, type Page } from '@playwright/test'; /** * Abre el formulario y espera a que la página sea interactiva. * * La espera no es adorno. `page.goto` vuelve cuando la página ha cargado, no * cuando Svelte ha tomado el control, y al tomarlo reescribe el valor de cada * `input` con el del componente, que está vacío: lo que se teclee antes de ese * instante desaparece. Fallaba solo con la máquina cargada, así que se caía en * la tanda completa y pasaba al repetir la prueba a solas. * * Un reintento no lo arregla —comprobar que el texto está puesto no impide que * lo borren un instante después—, así que el layout marca el momento con * `data-hidratado` y aquí se espera a él. */ async function abrirFormulario(page: Page) { await page.goto('/contacto'); await page.locator('html[data-hidratado]').waitFor(); } /** Rellena el formulario con datos válidos, salvo lo que se sobrescriba. */ async function rellenar( page: Page, campos: Partial<{ nombre: string; email: string; motivo: string; mensaje: string }> = {} ) { await page.getByLabel('Nombre').fill(campos.nombre ?? 'Ana Ruiz'); await page.getByLabel('Correo electrónico').fill(campos.email ?? 'ana@example.com'); await page.getByLabel('Motivo').selectOption(campos.motivo ?? 'colaboracion'); await page .getByLabel('Mensaje') .fill(campos.mensaje ?? 'Me gustaría hablar contigo sobre una colaboración para un disco.'); } test.describe('Formulario de contacto', () => { test('el pie lo abre como diálogo sin abandonar la página', async ({ page }) => { await page.goto('/interpretes/pilar-na-rua'); await page.locator('html[data-hidratado]').waitFor(); const enlace = page .getByRole('contentinfo') .getByRole('link', { name: 'Formulario de contacto' }); await enlace.click(); const dialogo = page.getByRole('dialog', { name: 'Contacto' }); await expect(dialogo).toBeVisible(); await expect(dialogo.getByLabel('Nombre')).toBeFocused(); await expect(page).toHaveURL('/interpretes/pilar-na-rua'); await dialogo.getByRole('button', { name: 'Cerrar contacto' }).click(); await expect(dialogo).toBeHidden(); await expect(enlace).toBeFocused(); }); test('el diálogo muestra los errores del servidor y conserva lo escrito', async ({ page }) => { await page.goto('/'); await page.locator('html[data-hidratado]').waitFor(); await page .getByRole('contentinfo') .getByRole('link', { name: 'Formulario de contacto' }) .click(); const dialogo = page.getByRole('dialog', { name: 'Contacto' }); await dialogo.getByLabel('Nombre').fill('A'); await dialogo.getByLabel('Correo electrónico').fill('ana@example.com'); await dialogo.getByLabel('Motivo').selectOption('colaboracion'); await dialogo.getByLabel('Mensaje').fill('Hola'); await dialogo.getByRole('button', { name: 'Enviar mensaje' }).click(); await expect(dialogo.getByText('Indica tu nombre.')).toBeVisible(); await expect(dialogo.getByText(/Cuenta un poco más/)).toBeVisible(); await expect(dialogo.getByLabel('Correo electrónico')).toHaveValue('ana@example.com'); }); test('la validación del servidor devuelve los errores por campo', async ({ page }) => { await abrirFormulario(page); // "A" y "Hola" pasan la validación del navegador y fallan en el servidor, // que es justo lo que interesa comprobar aquí. await rellenar(page, { nombre: 'A', mensaje: 'Hola' }); await page.getByRole('button', { name: 'Enviar mensaje' }).click(); await expect(page.getByText('Indica tu nombre.')).toBeVisible(); await expect(page.getByText(/Cuenta un poco más/)).toBeVisible(); }); test('lo escrito no se pierde cuando la validación falla', async ({ page }) => { await abrirFormulario(page); await rellenar(page, { nombre: 'A' }); await page.getByRole('button', { name: 'Enviar mensaje' }).click(); await expect(page.getByText('Indica tu nombre.')).toBeVisible(); await expect(page.getByLabel('Correo electrónico')).toHaveValue('ana@example.com'); await expect(page.getByLabel('Motivo')).toHaveValue('colaboracion'); await expect(page.getByLabel('Mensaje')).not.toBeEmpty(); }); test('Atrás recupera un borrador sin enviar', async ({ page }) => { await abrirFormulario(page); await rellenar(page); await page.locator('header a.marca').click(); await expect(page).toHaveURL('/'); await page.goBack(); await expect(page).toHaveURL('/contacto'); await expect(page.getByLabel('Nombre')).toHaveValue('Ana Ruiz'); await expect(page.getByLabel('Correo electrónico')).toHaveValue('ana@example.com'); await expect(page.getByLabel('Motivo')).toHaveValue('colaboracion'); await expect(page.getByLabel('Mensaje')).toHaveValue( 'Me gustaría hablar contigo sobre una colaboración para un disco.' ); }); test('el campo trampa descarta los envíos automáticos', async ({ page }) => { await abrirFormulario(page); await rellenar(page); // Solo un cliente automático rellena este campo: está fuera de la vista // y fuera del recorrido de tabulación. await page.evaluate(() => { const trampa = document.querySelector('#companiaWeb'); if (trampa instanceof HTMLInputElement) trampa.value = 'https://spam.example'; }); await page.getByRole('button', { name: 'Enviar mensaje' }).click(); await expect(page.getByText('No se ha podido enviar el mensaje.')).toBeVisible(); }); test('un envío válido no muestra errores de campo', async ({ page }) => { await abrirFormulario(page); await rellenar(page); await page.getByRole('button', { name: 'Enviar mensaje' }).click(); // El resultado final depende de si hay proveedor de correo configurado // (ver src/lib/server/correo.ts), así que solo se comprueba que la // validación ha quedado atrás. await expect(page.getByText('Indica tu nombre.')).toHaveCount(0); await expect(page.getByText(/Cuenta un poco más/)).toHaveCount(0); await expect(page.getByText('Ese correo no parece válido.')).toHaveCount(0); }); });