You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
senzapaura_es/e2e/revision-publica.e2e.ts

251 lines
11 KiB

import { expect, test } from '@playwright/test';
import { stringify } from 'devalue';
test('la música y los letristas no enlazan fichas de géneros sin publicar', async ({ page }) => {
await page.goto('/musica');
await page.locator('html[data-hidratado]').waitFor();
await page.getByRole('button', { name: 'Tabla', exact: true }).click();
await expect(page.getByRole('table')).toBeVisible();
await expect(page.locator('a[href="/generos/bolero-son"]')).toHaveCount(0);
await page.goto('/generos/letristas');
await expect(page.getByText('Bolero-son', { exact: true }).first()).toBeVisible();
await expect(page.locator('a[href="/generos/bolero-son"]')).toHaveCount(0);
});
test('el directorio de intérpretes usa una galería contenida con retratos dominantes', async ({
page
}) => {
await page.setViewportSize({ width: 1440, height: 1000 });
await page.goto('/interpretes');
const tarjeta = page.locator('.artistas .tarjeta').first();
const retrato = tarjeta.locator('.marco-retrato');
await expect(tarjeta).toBeVisible();
const medidas = await page.evaluate(() => {
const contenedor = document.querySelector<HTMLElement>('.pagina')!;
const ficha = document.querySelector<HTMLElement>('.artistas .tarjeta')!;
const imagen = ficha.querySelector<HTMLElement>('.marco-retrato')!;
return {
izquierda: Math.round(contenedor.getBoundingClientRect().left),
anchoTarjeta: Math.round(ficha.getBoundingClientRect().width),
anchoRetrato: Math.round(imagen.getBoundingClientRect().width),
altoRetrato: Math.round(imagen.getBoundingClientRect().height)
};
});
expect(medidas.izquierda).toBeGreaterThan(100);
expect(medidas.anchoTarjeta).toBeLessThan(450);
expect(medidas.anchoRetrato).toBe(medidas.anchoTarjeta - 2);
expect(medidas.altoRetrato).toBeGreaterThan(medidas.anchoRetrato);
await expect(retrato).toContainText('SP');
});
test('contacto conserva el mensaje ante un fallo de red y permite reintentar', async ({ page }) => {
await page.goto('/contacto');
await page.locator('html[data-hidratado]').waitFor();
await page.route('**/contacto', (ruta) =>
ruta.request().method() === 'POST' ? ruta.abort() : ruta.continue()
);
await page.getByLabel('Nombre', { exact: true }).fill('Prueba local');
await page.getByLabel('Correo electrónico').fill('prueba@example.test');
await page.getByLabel('Motivo').selectOption('colaboracion');
await page
.getByLabel('Mensaje', { exact: true })
.fill('Este mensaje se conserva y no se envía fuera de la prueba.');
await page.getByRole('button', { name: 'Enviar mensaje' }).click();
await expect(page.getByRole('alert')).toContainText('Conservamos tu mensaje');
await expect(page.getByLabel('Mensaje', { exact: true })).toHaveValue(
'Este mensaje se conserva y no se envía fuera de la prueba.'
);
await expect(page.getByLabel('Correo electrónico')).toHaveValue('prueba@example.test');
await expect(page.getByRole('button', { name: 'Enviar mensaje' })).toBeEnabled();
await expect(page).toHaveURL('/contacto');
});
test('el acceso conserva el correo si falla la conexión', async ({ page }) => {
await page.goto('/entrar');
await page.locator('html[data-hidratado]').waitFor();
await page.route('**/entrar?*/solicitar', (ruta) => ruta.abort());
await page.getByLabel('Correo electrónico').fill('prueba@example.test');
await page.getByRole('button', { name: 'Enviarme el código' }).click();
await expect(page.getByRole('alert')).toContainText('Conservamos los datos');
await expect(page.getByLabel('Correo electrónico')).toHaveValue('prueba@example.test');
await expect(page.getByRole('button', { name: 'Enviarme el código' })).toBeEnabled();
});
test('el código pegado con espacios se acepta y bloquea envíos simultáneos', async ({ page }) => {
await page.goto('/entrar?paso=codigo&email=prueba%40example.test');
await page.locator('html[data-hidratado]').waitFor();
const campo = page.getByLabel('Código', { exact: true });
await campo.evaluate((nodo) => {
const datos = new DataTransfer();
datos.setData('text/plain', '123 456');
nodo.dispatchEvent(
new ClipboardEvent('paste', { clipboardData: datos, bubbles: true, cancelable: true })
);
});
await expect(campo).toHaveValue('123456');
let liberar!: () => void;
const espera = new Promise<void>((resolver) => {
liberar = resolver;
});
await page.route('**/entrar?*/verificar', async (ruta) => {
await espera;
await ruta.fulfill({
contentType: 'application/json',
body: JSON.stringify({
type: 'failure',
status: 400,
data: stringify({
paso: 'codigo',
email: 'prueba@example.test',
error: 'Código de prueba rechazado.'
})
})
});
});
await page.getByRole('button', { name: 'Entrar', exact: true }).click();
await expect(page.getByRole('button', { name: 'Enviar otro código' })).toBeDisabled();
await expect(page.getByRole('button', { name: 'Usar otro correo' })).toBeDisabled();
liberar();
await expect(page.getByRole('alert')).toContainText('Código de prueba rechazado');
await expect(campo).toHaveValue('123456');
});
test('el modal y el formulario de contacto tienen etiquetas y focos independientes', async ({
page
}) => {
await page.goto('/contacto');
await page.locator('html[data-hidratado]').waitFor();
await page.getByRole('banner').getByRole('link', { name: 'Entrar', exact: true }).click();
const dialogo = page.getByRole('dialog');
await expect(dialogo.getByLabel('Correo electrónico')).toBeFocused();
await dialogo.locator('label').filter({ hasText: 'Correo electrónico' }).click();
await expect(dialogo.getByLabel('Correo electrónico')).toBeFocused();
const duplicados = await page.locator('[id]').evaluateAll((nodos) => {
const ids = nodos.map((n) => n.id);
return ids.filter((id, i) => ids.indexOf(id) !== i);
});
expect(duplicados).toEqual([]);
});
test('la barra espaciadora acciona el botón enfocado y no cambia la reproducción', async ({
page
}) => {
await page.goto('/albumes/cupido-sin-flechas');
await page.locator('html[data-hidratado]').waitFor();
await page.getByRole('button', { name: 'Reproducir', exact: true }).first().click();
const barra = page.getByRole('region', { name: 'Reproductor' });
await expect(barra.getByRole('button', { name: 'Pausar', exact: true })).toBeVisible();
const tema = page.getByRole('button', { name: /Cambiar al tema/ }).first();
const nombre = await tema.getAttribute('aria-label');
await tema.focus();
await page.keyboard.press('Space');
await expect(tema).not.toHaveAttribute('aria-label', nombre!);
await expect(barra.getByRole('button', { name: 'Pausar', exact: true })).toBeVisible();
await barra.getByRole('button', { name: 'Pausar', exact: true }).focus();
await page.keyboard.press('Space');
await expect(barra.getByRole('button', { name: 'Reproducir', exact: true })).toBeVisible();
});
test('cada canción muestra sus versiones y recomendaciones en una fila horizontal', async ({
page
}) => {
await page.goto('/canciones/cuando-tus-manos-me-tocan');
await page.locator('html[data-hidratado]').waitFor();
await page.getByRole('tab', { name: 'Versiones', exact: true }).click();
const panel = page.getByRole('tabpanel', { name: 'Versiones', exact: true });
await expect(panel).toBeVisible();
await expect(panel.getByText('Versión principal', { exact: true })).toBeVisible();
const relacionadas = page.getByRole('region', { name: 'Canciones relacionadas' });
await expect(relacionadas).toBeVisible();
expect(await relacionadas.locator('.carril > li').count()).toBeGreaterThan(1);
const medidas = await relacionadas
.locator('.carril')
.evaluate((carril) => ({ ancho: carril.clientWidth, contenido: carril.scrollWidth }));
expect(medidas.contenido).toBeGreaterThan(medidas.ancho);
await expect(relacionadas.getByText(/^Por |Mismo género|Con |Del mismo/).first()).toBeVisible();
const motivos = relacionadas.locator('.motivo .etiqueta');
await expect(motivos.first()).toBeVisible();
const estilos = await motivos.evaluateAll((elementos) =>
elementos.map((elemento) => {
const estilo = getComputedStyle(elemento);
return {
color: estilo.color,
fondo: estilo.backgroundColor,
tamano: estilo.fontSize
};
})
);
expect(estilos.every((estilo) => estilo.tamano === '11px')).toBe(true);
expect(estilos.every((estilo) => estilo.fondo === 'rgba(0, 0, 0, 0)')).toBe(true);
expect(new Set(estilos.map((estilo) => estilo.color)).size).toBeGreaterThan(1);
const tarjetas = relacionadas.locator('.carril > li article');
const alineacion = await tarjetas.evaluateAll((elementos) =>
elementos.slice(0, 4).map((tarjeta) => {
const nota = tarjeta.querySelector('.nota');
const pie = tarjeta.querySelector('.pie');
const titulo = tarjeta.querySelector('.titulo');
const personales = pie?.querySelector('.acciones-personales');
const iconos = personales?.querySelectorAll('svg');
const compra = pie?.querySelector('.compra');
if (!nota || !titulo || !iconos || iconos.length !== 2 || !compra)
throw new Error('Tarjeta incompleta');
return {
nota: Math.round(nota.getBoundingClientRect().top),
titulo: Math.round(titulo.getBoundingClientRect().left),
favorito: Math.round(iconos[0].getBoundingClientRect().left),
lista: Math.round(iconos[1].getBoundingClientRect().left),
compra: Math.round(compra.getBoundingClientRect().left)
};
})
);
const posicionesDeNota = alineacion.map((tarjeta) => tarjeta.nota);
expect(Math.max(...posicionesDeNota) - Math.min(...posicionesDeNota)).toBeLessThanOrEqual(1);
expect(alineacion.every((tarjeta) => Math.abs(tarjeta.favorito - tarjeta.titulo) <= 1)).toBe(
true
);
expect(
alineacion.every(
(tarjeta) => tarjeta.favorito < tarjeta.lista && tarjeta.lista < tarjeta.compra
)
).toBe(true);
});
test('favoritos, listas y valoración abren el acceso como popup sin abandonar la canción', async ({
page
}) => {
const ruta = '/canciones/cuando-tus-manos-me-tocan';
await page.goto(ruta);
await page.locator('html[data-hidratado]').waitFor();
const ficha = page.locator('header.ficha');
for (const { nombre, motivo } of [
{
nombre: /añadir a favoritos/i,
motivo:
'Necesitas entrar para guardar esta canción en Favoritos y recuperarla desde cualquier dispositivo.'
},
{
nombre: /añadir a una lista de reproducción/i,
motivo:
'Necesitas entrar para añadir esta canción a una lista de reproducción o crear una nueva.'
},
{
nombre: /inicia sesión para valorar/i,
motivo:
'Necesitas entrar para valorar esta canción. Así cada persona puede votar una sola vez.'
}
]) {
await ficha.getByRole('link', { name: nombre }).click();
const dialogo = page.getByRole('dialog');
await expect(dialogo).toBeVisible();
await expect(dialogo.getByText(motivo, { exact: true })).toBeVisible();
await expect(page).toHaveURL(ruta);
await page.keyboard.press('Escape');
await expect(dialogo).toBeHidden();
}
});

Powered by TurnKey Linux.