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.
564 lines
23 KiB
564 lines
23 KiB
import { expect, test } from '@playwright/test';
|
|
|
|
test.describe('Página de música', () => {
|
|
test('la lupa de la cabecera abre y enfoca la búsqueda del catálogo', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.locator('html[data-hidratado]').waitFor();
|
|
|
|
const buscar = page.getByRole('link', { name: 'Buscar en Música' });
|
|
await expect(buscar).toBeVisible();
|
|
await buscar.click();
|
|
|
|
await expect(page).toHaveURL('/musica#buscar-tema');
|
|
await expect(page.getByRole('searchbox', { name: 'Buscar en el catálogo' })).toBeFocused();
|
|
});
|
|
|
|
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('las etiquetas se despliegan bajo el buscador con su propio toggle', async ({ page }) => {
|
|
await page.setViewportSize({ width: 1000, height: 800 });
|
|
await page.goto('/musica?vista=canciones&idioma=es');
|
|
|
|
const lateral = page.locator('aside.lateral');
|
|
const nube = page.getByRole('navigation', { name: 'Filtrar por etiqueta' });
|
|
const boton = page.getByRole('button', { name: 'Etiquetas', exact: true });
|
|
await expect(lateral.getByRole('navigation', { name: 'Filtrar por etiqueta' })).toHaveCount(0);
|
|
await expect(nube).toBeHidden();
|
|
await expect(boton).toHaveAttribute('aria-pressed', 'false');
|
|
await expect(boton).toHaveAttribute('aria-expanded', 'false');
|
|
|
|
await boton.click();
|
|
await expect(nube).toBeVisible();
|
|
await expect(boton).toHaveAttribute('aria-pressed', 'true');
|
|
await expect(boton).toHaveAttribute('aria-expanded', 'true');
|
|
await expect(boton).toHaveClass(/oro/);
|
|
|
|
const medidas = await page.evaluate(() => {
|
|
const aside = document.querySelector<HTMLElement>('aside.lateral')!;
|
|
const barra = document.querySelector<HTMLElement>('.barra')!;
|
|
const etiquetas = document.querySelector<HTMLElement>('.filtros-etiquetas')!;
|
|
const resultado = document.querySelector<HTMLElement>('.resultado')!;
|
|
return {
|
|
desbordaAside: aside.scrollHeight > aside.clientHeight,
|
|
debajoDeBarra: Boolean(
|
|
barra.compareDocumentPosition(etiquetas) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
),
|
|
antesDelResultado: Boolean(
|
|
etiquetas.compareDocumentPosition(resultado) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
),
|
|
tieneRotuloVisible: etiquetas.querySelector('p') !== null
|
|
};
|
|
});
|
|
|
|
expect(medidas.desbordaAside).toBe(false);
|
|
expect(medidas.debajoDeBarra).toBe(true);
|
|
expect(medidas.antesDelResultado).toBe(true);
|
|
expect(medidas.tieneRotuloVisible).toBe(false);
|
|
|
|
await boton.click();
|
|
await expect(nube).toBeHidden();
|
|
await expect(boton).toHaveAttribute('aria-pressed', 'false');
|
|
});
|
|
|
|
test('ofrece vistas explícitas para todos los álbumes y todas las canciones', async ({
|
|
page
|
|
}) => {
|
|
await page.goto('/musica');
|
|
const catalogo = page.getByRole('navigation', { name: 'Ver catálogo' });
|
|
await expect(catalogo.getByRole('link', { name: 'Todo el catálogo' })).toBeVisible();
|
|
await expect(catalogo.getByRole('link', { name: 'Todos los álbumes' })).toBeVisible();
|
|
const todas = catalogo.getByRole('link', { name: 'Todas las canciones' });
|
|
await todas.click();
|
|
|
|
await expect(page).toHaveURL('/musica?vista=canciones');
|
|
await expect(todas).toHaveAttribute('aria-current', 'page');
|
|
await expect(page.getByRole('heading', { level: 2, name: 'Canciones' })).toBeVisible();
|
|
await expect(page.getByRole('heading', { name: 'Álbumes y publicaciones' })).toHaveCount(0);
|
|
await expect(page.getByRole('status')).toContainText('11 canciones');
|
|
|
|
await page
|
|
.getByRole('navigation', { name: 'Estilos' })
|
|
.getByRole('link', { name: /Baladas románticas/ })
|
|
.click();
|
|
await expect(page).toHaveURL('/musica?vista=canciones&estilo=baladas-romanticas');
|
|
await expect(page.getByRole('heading', { name: 'Álbumes y publicaciones' })).toHaveCount(0);
|
|
});
|
|
|
|
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 en el catálogo').fill('desamor');
|
|
|
|
// Ningún tema se llama «desamor»: si aparecen, es por sus etiquetas.
|
|
const estado = page.getByRole('status');
|
|
await expect(estado).toContainText('búsqueda «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 en el catálogo').fill('zzzzz');
|
|
|
|
await expect(page.getByRole('status')).toContainText('0 canciones');
|
|
await expect(page.getByText('No hay canciones que coincidan.')).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: 'Canciones', exact: true })).toBeVisible();
|
|
await expect(page.getByRole('heading', { name: 'Dorina' })).toBeVisible();
|
|
});
|
|
|
|
test('los álbumes tienen la misma anchura que las canciones y la portada ocupa la tarjeta', async ({
|
|
page
|
|
}) => {
|
|
await page.setViewportSize({ width: 1000, height: 1000 });
|
|
await page.emulateMedia({ reducedMotion: 'reduce' });
|
|
await page.goto('/musica');
|
|
|
|
const albumes = page.locator('section.albumes');
|
|
await expect(
|
|
albumes.getByRole('heading', { level: 2, name: 'Álbumes y publicaciones' })
|
|
).toBeVisible();
|
|
await expect(albumes.getByText('Álbum', { exact: true })).toBeVisible();
|
|
await expect(albumes.getByText('Colección', { exact: true })).toBeVisible();
|
|
|
|
const medidas = await page.evaluate(() => {
|
|
const rejilla = document.querySelector<HTMLElement>('section.albumes .rejilla-albumes')!;
|
|
const album = rejilla.querySelector<HTMLElement>('.tarjeta')!;
|
|
const portada = album.querySelector<HTMLElement>('.tarjeta__medio')!;
|
|
const titulo = album.querySelector<HTMLElement>('.titulo')!;
|
|
const meta = album.querySelector<HTMLElement>('.meta')!;
|
|
const cancion = document.querySelector<HTMLElement>('section.canciones .tarjeta')!;
|
|
const tipo = album.querySelector<HTMLElement>('.tipo')!;
|
|
const reproducirAlbum = album.querySelector<HTMLElement>('.tarjeta__reproduccion button')!;
|
|
return {
|
|
columnas: getComputedStyle(rejilla).gridTemplateColumns.split(' ').length,
|
|
album: album.getBoundingClientRect(),
|
|
portada: portada.getBoundingClientRect(),
|
|
cancion: cancion.getBoundingClientRect(),
|
|
titulo: Number.parseFloat(getComputedStyle(titulo).fontSize),
|
|
meta: Number.parseFloat(getComputedStyle(meta).fontSize),
|
|
playAlbumEnPortada: Boolean(portada.querySelector('button')),
|
|
playAlbumEnCuerpo: Boolean(
|
|
album.querySelector('.tarjeta__cuerpo .tarjeta__reproduccion button')
|
|
),
|
|
playCancionEnPortada: Boolean(cancion.querySelector('.tarjeta__medio button')),
|
|
playCancionEnCuerpo: Boolean(
|
|
cancion.querySelector('.tarjeta__cuerpo .tarjeta__reproduccion button')
|
|
),
|
|
alineacionPlay:
|
|
Math.abs(
|
|
tipo.getBoundingClientRect().top +
|
|
tipo.getBoundingClientRect().height / 2 -
|
|
(reproducirAlbum.getBoundingClientRect().top +
|
|
reproducirAlbum.getBoundingClientRect().height / 2)
|
|
) < 2
|
|
};
|
|
});
|
|
|
|
expect(medidas.columnas).toBe(2);
|
|
expect(Math.abs(medidas.album.width - medidas.cancion.width)).toBeLessThan(2);
|
|
expect(medidas.portada.width).toBeGreaterThan(medidas.album.width * 0.98);
|
|
expect(Math.abs(medidas.portada.width - medidas.portada.height)).toBeLessThan(2);
|
|
expect(medidas.titulo).toBeGreaterThanOrEqual(24);
|
|
expect(medidas.meta).toBeGreaterThanOrEqual(14);
|
|
expect(medidas.playAlbumEnPortada).toBe(false);
|
|
expect(medidas.playAlbumEnCuerpo).toBe(true);
|
|
expect(medidas.playCancionEnPortada).toBe(false);
|
|
expect(medidas.playCancionEnCuerpo).toBe(true);
|
|
expect(medidas.alineacionPlay).toBe(true);
|
|
});
|
|
|
|
test('la tarjeta responde al puntero y la portada permanece fija', async ({ page }) => {
|
|
await page.goto('/musica');
|
|
const tarjeta = page.locator('section.albumes .tarjeta').first();
|
|
await expect(tarjeta).toBeVisible();
|
|
await tarjeta.scrollIntoViewIfNeeded();
|
|
// Deja terminar la entrada escalonada antes de medir la posición real.
|
|
await page.waitForTimeout(1100);
|
|
|
|
const imagen = tarjeta.locator('.tarjeta__medio img');
|
|
const posicionInicial = await imagen.boundingBox();
|
|
expect(posicionInicial).not.toBeNull();
|
|
|
|
await tarjeta.hover({ position: { x: 24, y: 24 } });
|
|
await page.waitForTimeout(500);
|
|
const posicionHoverInicial = await imagen.boundingBox();
|
|
|
|
const cajaTarjeta = await tarjeta.boundingBox();
|
|
expect(cajaTarjeta).not.toBeNull();
|
|
await page.mouse.move(
|
|
cajaTarjeta!.x + cajaTarjeta!.width * 0.75,
|
|
cajaTarjeta!.y + cajaTarjeta!.height * 0.72
|
|
);
|
|
await page.waitForTimeout(100);
|
|
const posicionHoverFinal = await imagen.boundingBox();
|
|
|
|
const sombra = await tarjeta.evaluate((nodo) => getComputedStyle(nodo).boxShadow);
|
|
const destello = await tarjeta.evaluate(
|
|
(nodo) => getComputedStyle(nodo, '::after').animationName
|
|
);
|
|
const portada = await imagen.evaluate((nodo) => ({
|
|
transformacion: getComputedStyle(nodo).transform,
|
|
filtro: getComputedStyle(nodo).filter,
|
|
transicion: getComputedStyle(nodo).transitionDuration
|
|
}));
|
|
|
|
expect(sombra).not.toBe('none');
|
|
expect(destello).toBe('tarjeta-destello');
|
|
for (const posicion of [posicionHoverInicial, posicionHoverFinal]) {
|
|
expect(posicion).not.toBeNull();
|
|
expect(posicion!.x).toBeCloseTo(posicionInicial!.x, 1);
|
|
expect(posicion!.y).toBeCloseTo(posicionInicial!.y, 1);
|
|
expect(posicion!.width).toBeCloseTo(posicionInicial!.width, 1);
|
|
expect(posicion!.height).toBeCloseTo(posicionInicial!.height, 1);
|
|
}
|
|
expect(portada).toEqual({
|
|
transformacion: 'none',
|
|
filtro: 'none',
|
|
transicion: '0s'
|
|
});
|
|
});
|
|
|
|
test('los rótulos de sección respiran antes de sus títulos', async ({ page }) => {
|
|
await page.goto('/musica');
|
|
|
|
for (const seccion of ['.albumes', '.canciones']) {
|
|
const distancia = await page.locator(seccion).evaluate((elemento) => {
|
|
const rotulo = elemento.querySelector<HTMLElement>('.eyebrow')!.getBoundingClientRect();
|
|
const titulo = elemento.querySelector<HTMLElement>('h2')!.getBoundingClientRect();
|
|
return titulo.top - rotulo.bottom;
|
|
});
|
|
expect(distancia).toBeGreaterThanOrEqual(8);
|
|
}
|
|
});
|
|
|
|
test('el catálogo general muestra todos los álbumes publicados antes de filtrar', async ({
|
|
page,
|
|
request
|
|
}) => {
|
|
const sitemap = await (await request.get('/sitemap.xml')).text();
|
|
const esperados = [
|
|
...new Set([...sitemap.matchAll(/<loc>[^<]*\/albumes\/([^<]+)<\/loc>/g)].map((m) => m[1]))
|
|
].sort();
|
|
|
|
await page.goto('/musica');
|
|
const catalogo = page.locator('section.albumes');
|
|
await expect(
|
|
catalogo.getByRole('heading', { level: 2, name: 'Álbumes y publicaciones' })
|
|
).toBeVisible();
|
|
const visibles = (
|
|
await catalogo
|
|
.locator('a.tarjeta__enlace')
|
|
.evaluateAll((enlaces) =>
|
|
enlaces.map((enlace) =>
|
|
new URL((enlace as HTMLAnchorElement).href).pathname.split('/').pop()!
|
|
)
|
|
)
|
|
).sort();
|
|
|
|
expect(visibles).toEqual(esperados);
|
|
});
|
|
|
|
test('el aside filtra a la vez los álbumes y las canciones por estilo', async ({ page }) => {
|
|
await page.goto('/musica');
|
|
const estilos = page.getByRole('navigation', { name: 'Estilos' });
|
|
await estilos.getByRole('link', { name: /Baladas románticas/ }).click();
|
|
|
|
await expect(page).toHaveURL('/musica?estilo=baladas-romanticas');
|
|
await expect(estilos.getByRole('link', { name: /Baladas románticas/ })).toHaveAttribute(
|
|
'aria-current',
|
|
'page'
|
|
);
|
|
await expect(page.getByRole('status')).toContainText('estilo «Baladas románticas»');
|
|
await expect(page.locator('section.albumes').getByText('Cupido sin flechas')).toBeVisible();
|
|
await expect(page.locator('section.canciones').getByText('Dorina')).toBeVisible();
|
|
});
|
|
|
|
test('el aside ofrece los idiomas del catálogo y filtra canciones y lanzamientos', async ({
|
|
page
|
|
}) => {
|
|
await page.goto('/musica');
|
|
const idiomas = page.getByRole('navigation', { name: 'Idiomas' });
|
|
for (const idioma of [
|
|
'Español',
|
|
'Portugués',
|
|
'Inglés',
|
|
'Francés',
|
|
'Italiano',
|
|
'Armenio',
|
|
'Búlgaro',
|
|
'Latín',
|
|
'Irlandés'
|
|
])
|
|
await expect(idiomas.getByText(idioma, { exact: true })).toBeVisible();
|
|
|
|
const frances = idiomas.getByRole('link', { name: /Francés/ });
|
|
await frances.click();
|
|
await expect(page).toHaveURL('/musica?idioma=fr');
|
|
await expect(frances).toHaveAttribute('aria-current', 'page');
|
|
await page.evaluate(() => {
|
|
document.documentElement.dataset.tema = 'claro';
|
|
});
|
|
expect(
|
|
await frances.evaluate((enlace) => ({
|
|
bordeIzquierdo: getComputedStyle(enlace).borderLeftWidth,
|
|
peso: getComputedStyle(enlace).fontWeight
|
|
}))
|
|
).toEqual({ bordeIzquierdo: '0px', peso: '600' });
|
|
await expect(page.getByRole('status')).toContainText('idioma «Francés»');
|
|
await expect(page.getByRole('heading', { name: /Ton silence/ })).toBeVisible();
|
|
await expect(page.getByRole('heading', { name: 'Dorina' })).toHaveCount(0);
|
|
await expect(page.locator('section.albumes').getByText('Cupido sin flechas')).toBeVisible();
|
|
|
|
await page
|
|
.getByRole('navigation', { name: 'Estilos' })
|
|
.getByRole('link', { name: /Baladas románticas/ })
|
|
.click();
|
|
await expect(page).toHaveURL('/musica?estilo=baladas-romanticas&idioma=fr');
|
|
});
|
|
|
|
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 lleva su etiqueta al filtro de música', async ({ page }) => {
|
|
await page.goto('/canciones/ton-silence-me-libere');
|
|
|
|
await page.getByRole('link', { name: 'Rabia', exact: true }).click();
|
|
|
|
await expect(page).toHaveURL('/musica?etiqueta=rabia');
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Música');
|
|
});
|
|
|
|
test('las etiquetas del desplegable filtran temas de discos distintos', async ({ page }) => {
|
|
await page.goto('/musica');
|
|
const nube = page.getByRole('navigation', { name: 'Filtrar por etiqueta' });
|
|
await page.getByRole('button', { name: 'Etiquetas', exact: true }).click();
|
|
await expect(nube).toBeVisible();
|
|
await expect(page.locator('aside.lateral').getByRole('link', { name: 'Rabia' })).toHaveCount(0);
|
|
await nube.getByRole('link', { name: 'Rabia', exact: true }).click();
|
|
|
|
await expect(page).toHaveURL('/musica?etiqueta=rabia');
|
|
await expect(nube.getByRole('link', { name: 'Rabia', exact: true })).toHaveAttribute(
|
|
'aria-current',
|
|
'page'
|
|
);
|
|
await expect(page.getByRole('heading', { name: 'Ton silence me libère' })).toBeVisible();
|
|
await expect(page.getByRole('heading', { name: 'Tus malas maneras' })).toBeVisible();
|
|
|
|
await nube.getByRole('link', { name: 'Rabia', exact: true }).click();
|
|
await expect(page).toHaveURL('/musica');
|
|
await expect(nube).toBeVisible();
|
|
await expect(nube.getByRole('link', { name: 'Rabia', exact: true })).not.toHaveAttribute(
|
|
'aria-current',
|
|
'page'
|
|
);
|
|
});
|
|
|
|
test('las etiquetas del desplegable se ordenan de más usada a menos', async ({ page }) => {
|
|
await page.goto('/musica');
|
|
await page.getByRole('button', { name: 'Etiquetas', exact: true }).click();
|
|
|
|
const titulos = await page
|
|
.getByRole('navigation', { name: 'Filtrar por etiqueta' })
|
|
.locator('a:not(.todas)')
|
|
.evaluateAll((enlaces) => enlaces.map((enlace) => enlace.getAttribute('title') ?? ''));
|
|
const cuentas = titulos.map((titulo) => Number(titulo.split(' ')[0]));
|
|
expect(cuentas).toEqual([...cuentas].sort((a, b) => b - a));
|
|
});
|
|
|
|
test('ya no existe una sección pública independiente de etiquetas', async ({ page }) => {
|
|
const respuesta = await page.goto('/etiquetas');
|
|
expect(respuesta?.status()).toBe(404);
|
|
});
|
|
});
|