import { expect, test } from '@playwright/test'; test.describe('Fichas de contenido', () => { test('la ficha de un tema publica la letra y sus créditos', async ({ page }) => { await page.goto('/canciones/como-si-nada'); await expect(page.getByRole('heading', { level: 1 })).toHaveText('Como si nada'); await expect(page.getByRole('tab', { name: 'Letra' })).toHaveAttribute('aria-selected', 'true'); await expect(page.getByText('Entras en la casa y no preguntas nada,')).toBeVisible(); await expect(page.getByRole('heading', { level: 2, name: 'Créditos' })).toBeVisible(); await expect(page.getByText('Marta Cifuentes')).toBeVisible(); }); test('los versos conservan sus saltos de línea', async ({ page }) => { // Sin remark-breaks, Markdown juntaría la estrofa en un único párrafo. await page.goto('/canciones/como-si-nada'); const saltos = await page.locator('.texto br').count(); expect(saltos).toBeGreaterThan(5); }); test('una pieza instrumental muestra notas en lugar de letra', async ({ page }) => { await page.goto('/canciones/preludio-i'); await expect(page.getByRole('tab', { name: 'Sobre la pieza' })).toBeVisible(); await expect(page.getByRole('tab', { name: 'Letra' })).toHaveCount(0); }); test('la bio se publica desde el Markdown de contenido', async ({ page }) => { await page.goto('/bio'); await expect(page.getByRole('heading', { level: 1 })).toHaveText('Biografía'); await expect(page.getByRole('heading', { level: 2, name: 'Las baladas' })).toBeVisible(); }); }); test.describe('Tarjetas de canción', () => { test('la tarjeta sitúa el tema: disco, año y estilo', async ({ page }) => { await page.goto('/musica/baladas-romanticas'); // Por el enlace, no por el nombre: el disco se llama igual que el tema. const tarjeta = page .locator('article') .filter({ has: page.locator('a[href="/canciones/como-si-nada"]') }); await expect(tarjeta.getByRole('heading', { name: 'Como si nada' })).toBeVisible(); await expect(tarjeta.getByText(/2024/)).toBeVisible(); await expect(tarjeta.getByText(/Baladas románticas/)).toBeVisible(); }); test('la tarjeta no enseña la letra', async ({ page }) => { // Identifica el tema; leer se lee en su ficha. await page.goto('/musica/baladas-romanticas'); await expect(page.getByText('Entras en la casa y no preguntas nada,')).toHaveCount(0); }); test('desde la tarjeta se llega a la letra completa', async ({ page }) => { await page.goto('/musica/baladas-romanticas'); // Por el enlace, no por el nombre: el disco se llama igual que el tema. const tarjeta = page .locator('article') .filter({ has: page.locator('a[href="/canciones/como-si-nada"]') }); await tarjeta.getByRole('link').first().click(); await expect(page).toHaveURL(new RegExp('/canciones/')); // La letra es la pestaña que se abre: es el contenido principal. await expect(page.getByRole('tab', { name: 'Letra' })).toHaveAttribute('aria-selected', 'true'); }); }); test.describe('Pestañas de la ficha', () => { test('la letra se abre por defecto', async ({ page }) => { await page.goto('/canciones/como-si-nada'); await expect(page.getByRole('tab', { name: 'Letra' })).toHaveAttribute('aria-selected', 'true'); await expect(page.getByRole('tabpanel', { name: 'Letra' })).toBeVisible(); await expect(page.getByRole('tabpanel', { name: 'De dónde sale' })).toBeHidden(); }); test('al pulsar una pestaña se cambia de panel', async ({ page }) => { await page.goto('/canciones/como-si-nada'); await page.getByRole('tab', { name: 'De dónde sale' }).click(); await expect(page.getByRole('tabpanel', { name: 'De dónde sale' })).toBeVisible(); await expect(page.getByRole('tabpanel', { name: 'Letra' })).toBeHidden(); await expect(page.getByText(/mi madre poniendo dos tazas/)).toBeVisible(); }); test('las flechas mueven entre pestañas', async ({ page }) => { // Es el patrón de pestañas de WAI-ARIA: activación automática al moverse. await page.goto('/canciones/como-si-nada'); await page.getByRole('tab', { name: 'Letra' }).focus(); await page.keyboard.press('ArrowRight'); await expect(page.getByRole('tab', { name: 'De dónde sale' })).toBeFocused(); await expect(page.getByRole('tabpanel', { name: 'De dónde sale' })).toBeVisible(); // Y da la vuelta al llegar al final. Se comprueba por posición y no por // nombre: cuántas pestañas hay depende de lo que tenga el tema. const pestanas = page.getByRole('tab'); await page.keyboard.press('End'); await expect(pestanas.last()).toBeFocused(); await page.keyboard.press('ArrowRight'); await expect(pestanas.first()).toBeFocused(); }); test('solo hay pestañas para lo que el tema tiene', async ({ page }) => { // "Media verdad" no tiene versiones alternativas. await page.goto('/canciones/media-verdad'); await expect(page.getByRole('tab', { name: 'Alternativas musicales' })).toHaveCount(0); await expect(page.getByRole('tab', { name: 'Letra' })).toBeVisible(); }); test('se puede enlazar una pestaña concreta', async ({ page }) => { // Un panel cerrado está oculto, así que sin esto el navegador no tendría // adónde ir: hay que abrir la pestaña antes de bajar hasta ella. await page.goto('/canciones/como-si-nada#panel-videos'); await expect(page.getByRole('tab', { name: 'Vídeos' })).toHaveAttribute( 'aria-selected', 'true' ); await expect(page.getByRole('tabpanel', { name: 'Vídeos' })).toBeVisible(); }); test('un ancla que no existe deja la pestaña de siempre', async ({ page }) => { await page.goto('/canciones/como-si-nada#panel-inventado'); await expect(page.getByRole('tab', { name: 'Letra' })).toHaveAttribute('aria-selected', 'true'); }); test('el contenido de las pestañas cerradas está en el HTML', async ({ page }) => { // Aunque no se vea, tiene que estar publicado: es lo que indexan los // buscadores y lo que se lee sin JavaScript. await page.goto('/canciones/como-si-nada'); await expect(page.getByRole('tabpanel', { name: 'Alternativas musicales' })).toBeHidden(); await expect(page.getByText('Versión orquestal')).toBeAttached(); }); }); test.describe('Letra en otro idioma', () => { test('se enfrenta con su traducción en dos columnas', async ({ page }) => { await page.goto('/canciones/the-long-way-home'); await expect(page.getByText('Letra original · inglés')).toBeVisible(); await expect(page.getByText('Traducción al español')).toBeVisible(); // Cada columna declara su idioma: es lo que hace que un lector de pantalla // no lea el inglés con voz española. const columnas = page.locator('.letra.pareada > .texto'); await expect(columnas).toHaveCount(2); await expect(columnas.first()).toHaveAttribute('lang', 'en'); await expect(columnas.last()).toHaveAttribute('lang', 'es'); }); test('la traducción conserva el corte de los versos', async ({ page }) => { await page.goto('/canciones/the-long-way-home'); // Verso a verso: si se perdieran los saltos, no se podría seguir la // original en paralelo, que es para lo que está al lado. const traduccion = await page.locator('.verso-a-verso').innerText(); expect(traduccion.split('\n').filter((l) => l.trim()).length).toBeGreaterThan(4); }); test('un tema en español se queda en una sola columna', async ({ page }) => { await page.goto('/canciones/como-si-nada'); await expect(page.locator('.letra.pareada')).toHaveCount(0); await expect(page.getByText('Traducción al español')).toHaveCount(0); }); });