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/musica.e2e.ts

251 lines
10 KiB

import { expect, test } from '@playwright/test';
test.describe('Página de música', () => {
test('los estilos están siempre a la vista, con su recuento', async ({ page }) => {
await page.goto('/musica');
const lateral = page.getByRole('navigation', { name: 'Estilos' });
await expect(lateral).toBeVisible();
await expect(lateral.getByRole('link', { name: /Baladas románticas/ })).toBeVisible();
await expect(lateral.getByRole('link', { name: /Clásica/ })).toBeVisible();
});
test('buscar y elegir vista son una sola pieza', async ({ page }) => {
/*
* Eran dos cajas con un hueco entre ellas y parecían dos controles sin
* relación. El borde es de la barra: dentro, el campo y los botones no
* tienen canto propio y llegan al mismo alto.
*/
await page.setViewportSize({ width: 1100, height: 800 });
await page.goto('/musica');
const medidas = await page.evaluate(() => {
const campo = document.querySelector('input[type="search"]')!;
const activo = document.querySelector('.vista.oro')!;
const barra = document.querySelector('.barra')!.getBoundingClientRect();
return {
bordeDelCampo: getComputedStyle(campo).borderTopWidth,
altoCampo: Math.round(campo.getBoundingClientRect().height),
altoBoton: Math.round(activo.getBoundingClientRect().height),
altoBarra: Math.round(barra.height),
dentro: activo.getBoundingClientRect().right <= barra.right + 1
};
});
expect(medidas.bordeDelCampo).toBe('0px');
expect(medidas.altoCampo).toBe(medidas.altoBoton);
// Sin hueco: la barra mide lo que miden sus piezas, más su propio canto.
expect(medidas.altoBarra).toBeLessThanOrEqual(medidas.altoCampo + 2);
expect(medidas.dentro).toBe(true);
});
test('la vista activa lleva el oro de los botones, no un color plano', async ({ page }) => {
await page.goto('/musica');
const oro = page.locator('.vista.oro');
await expect(oro).toHaveText('Tarjetas');
// El degradado es lo que se pedía: al lado hay un botón dorado de verdad
// y se notaba que el conmutador no era el mismo oro.
const pintura = await oro.evaluate((e) => {
const s = getComputedStyle(e);
return { imagen: s.backgroundImage, filo: s.boxShadow, color: s.color };
});
expect(pintura.imagen).toContain('linear-gradient');
expect(pintura.filo).toContain('inset');
// Y la letra es la del botón dorado, no el gris del inactivo: el `.vista`
// de la página le ganaba por especificidad al `.oro` general.
const inactivo = await page
.getByRole('button', { name: 'Tabla' })
.evaluate((e) => getComputedStyle(e).color);
expect(pintura.color).not.toBe(inactivo);
// Y cambia con la vista.
await page.getByRole('button', { name: 'Tabla' }).click();
await expect(page.locator('.vista.oro')).toHaveText('Tabla');
});
test('en un móvil la barra se apila sin estirar el campo', async ({ page }) => {
// En columna, `flex-basis` mide el alto: las doce rem que dan ancho al
// campo lo convertían en un cajón de doscientos píxeles.
await page.setViewportSize({ width: 360, height: 800 });
await page.goto('/musica');
const alto = await page
.locator('input[type="search"]')
.evaluate((e) => Math.round(e.getBoundingClientRect().height));
expect(alto).toBeLessThan(60);
});
test('la tabla ordena por la columna que se pulsa', async ({ page }) => {
await page.goto('/musica');
await page.getByRole('button', { name: 'Tabla' }).click();
const columna = page.getByRole('columnheader', { name: 'Duración' });
await expect(columna).toHaveAttribute('aria-sort', 'none');
// Las cifras empiezan de mayor a menor: lo más largo arriba.
await columna.getByRole('button').click();
await expect(columna).toHaveAttribute('aria-sort', 'descending');
// Por su clase y no por su posición: la tabla lleva portada, y el número
// de columna cambia con lo que se enseñe en cada página.
const duraciones = await page.locator('tbody td.duracion').allInnerTexts();
const enSegundos = duraciones.map((d) => {
const [m, s] = d.trim().split(':').map(Number);
return m * 60 + s;
});
expect(enSegundos).toEqual([...enSegundos].sort((a, b) => b - a));
// Y volver a pulsarla le da la vuelta.
await columna.getByRole('button').click();
await expect(columna).toHaveAttribute('aria-sort', 'ascending');
});
test('la búsqueda encuentra por etiqueta, no solo por título', async ({ page }) => {
await page.goto('/musica');
await page.getByLabel('Buscar un tema').fill('desamor');
// Ningún tema se llama «desamor»: si aparecen, es por sus etiquetas.
const estado = page.getByRole('status');
await expect(estado).toContainText('para «desamor»');
await expect(page.getByRole('heading', { name: 'Prometiste' })).toBeVisible();
});
test('una búsqueda sin resultados lo dice', async ({ page }) => {
await page.goto('/musica');
await page.getByLabel('Buscar un tema').fill('zzzzz');
await expect(page.getByRole('status')).toContainText('0 temas');
await expect(page.getByText('Ningún tema coincide.')).toBeVisible();
});
test('en la página de un estilo se ven todos sus temas', async ({ page }) => {
// No solo los destacados: si no, un tema recién añadido no se ve.
await page.goto('/musica/baladas-romanticas');
await expect(page.getByRole('heading', { name: 'Todos los temas' })).toBeVisible();
await expect(page.getByRole('heading', { name: 'Dorina' })).toBeVisible();
});
test('la ficha publica el idioma real de la letra', async ({ page }) => {
await page.goto('/canciones/ton-silence-me-libere');
const ficha = page.getByRole('heading', { name: 'Ficha' }).locator('..');
await expect(ficha.getByText('Idioma', { exact: true })).toBeVisible();
await expect(ficha.getByText(/francés/i)).toBeVisible();
await expect(ficha.getByText('Voces', { exact: true })).toBeVisible();
await expect(ficha.getByText('Dueto mixto', { exact: true })).toBeVisible();
await expect(ficha.getByText('Acceso de escucha', { exact: true })).toBeVisible();
await expect(ficha.getByText('Público', { exact: true })).toBeVisible();
await expect(page.locator('.texto[lang="fr"]')).toBeVisible();
});
test('la valoración y las acciones personales están junto al título', async ({ page }) => {
await page.goto('/canciones/ton-silence-me-libere');
const cabecera = page.locator('header.ficha');
const valoracion = cabecera.getByRole('region', { name: 'Valoración de la canción' });
await expect(valoracion).toBeVisible();
await expect(valoracion.getByRole('heading')).toHaveCount(0);
await expect(valoracion).not.toContainText(/valoración|de 5|votos?|quitar mi voto/i);
const numeroValoracion = valoracion.locator('.media');
const estrellas = await valoracion.locator('.estrellas').boundingBox();
const puntuacion = await numeroValoracion.boundingBox();
expect(estrellas).not.toBeNull();
expect(puntuacion).not.toBeNull();
const centroEstrellas = (estrellas?.y ?? 0) + (estrellas?.height ?? 0) / 2;
const centroPuntuacion = (puntuacion?.y ?? 0) + (puntuacion?.height ?? 0) / 2;
expect(Math.abs(centroEstrellas - centroPuntuacion)).toBeLessThan(2);
const familiaNumero = await numeroValoracion.evaluate(
(elemento) => getComputedStyle(elemento).fontFamily
);
expect(familiaNumero).toContain('Archivo');
expect(familiaNumero).not.toContain('Newsreader');
const favorito = cabecera.getByRole('link', { name: /añadir a favoritos/i });
const lista = cabecera.getByRole('link', { name: /añadir a una lista de reproducción/i });
await expect(favorito).toBeVisible();
await expect(lista).toBeVisible();
await expect(favorito.locator('svg')).toHaveAttribute('width', '28');
await expect(lista.locator('svg')).toHaveAttribute('width', '28');
});
test('la letra y las tarjetas de la ficha comparten retícula y tipografía de interfaz', async ({
page
}) => {
await page.goto('/canciones/ton-silence-me-libere');
const filaPestanas = page.getByRole('tablist', { name: 'Contenido del tema' });
const ficha = page.locator('aside.lateral');
const tituloFicha = ficha.getByRole('heading', { name: 'Ficha' });
const cajaPestanas = await filaPestanas.boundingBox();
const cajaFicha = await tituloFicha.boundingBox();
expect(cajaPestanas).not.toBeNull();
expect(cajaFicha).not.toBeNull();
expect(
Math.abs(
(cajaPestanas?.y ?? 0) +
(cajaPestanas?.height ?? 0) -
((cajaFicha?.y ?? 0) + (cajaFicha?.height ?? 0))
)
).toBeLessThan(2);
const familiaPestana = await filaPestanas
.getByRole('tab', { name: 'Letra' })
.evaluate((elemento) => getComputedStyle(elemento).fontFamily);
const familiaFicha = await tituloFicha.evaluate(
(elemento) => getComputedStyle(elemento).fontFamily
);
const familiaLetra = await page
.locator('.texto.versos')
.evaluate((elemento) => getComputedStyle(elemento).fontFamily);
expect(familiaFicha).toBe(familiaPestana);
expect(familiaLetra).not.toBe(familiaFicha);
await expect(filaPestanas.getByRole('tab', { name: 'Letra' })).toHaveCSS('font-size', '15px');
await expect(tituloFicha).toHaveCSS('font-size', '15px');
await expect(ficha.locator('dl').first()).toHaveCSS('font-size', '15px');
const tarjetas = ficha.locator('section.bloque');
expect(await tarjetas.count()).toBeGreaterThanOrEqual(2);
for (const tarjeta of await tarjetas.all()) {
await expect(tarjeta).toHaveCSS('border-top-style', 'solid');
await expect(tarjeta).toHaveCSS('overflow', 'hidden');
}
});
});
test.describe('Etiquetas', () => {
test('la ficha de un tema enlaza sus etiquetas', async ({ page }) => {
await page.goto('/canciones/ton-silence-me-libere');
await page.getByRole('link', { name: 'Rabia', exact: true }).click();
await expect(page).toHaveURL('/etiquetas/rabia');
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Rabia');
});
test('una etiqueta reúne temas de discos distintos', async ({ page }) => {
await page.goto('/etiquetas/rabia');
await expect(page.getByRole('heading', { name: 'Ton silence me libère' })).toBeVisible();
await expect(page.getByRole('heading', { name: 'Tus malas maneras' })).toBeVisible();
});
test('el índice ordena de más usada a menos', async ({ page }) => {
await page.goto('/etiquetas');
const nombres = await page.locator('.nube a').allInnerTexts();
const cuentas = nombres.map((t) => Number(t.trim().split(/\s+/).pop()));
expect(cuentas).toEqual([...cuentas].sort((a, b) => b - a));
});
test('una etiqueta que no existe da 404', async ({ page }) => {
const respuesta = await page.goto('/etiquetas/no-existe');
expect(respuesta?.status()).toBe(404);
});
});

Powered by TurnKey Linux.