import { expect, test } from '@playwright/test'; test.describe('Taller de letras', () => { test('el índice separa los principios de las fichas', async ({ page }) => { await page.goto('/taller'); await expect(page.getByRole('heading', { level: 1 })).toHaveText('Taller de letras'); await expect(page.getByRole('heading', { level: 2, name: 'Los principios' })).toBeVisible(); await expect(page.getByRole('heading', { level: 2, name: 'Fichas de creación' })).toBeVisible(); // El índice lateral rotula sus bloques, pero no como encabezados: si no, // el esquema de la página diría dos veces lo mismo. await expect(page.getByRole('heading', { level: 2 })).toHaveCount(2); }); test('se llega a un principio y se encadena con el siguiente', async ({ page }) => { await page.goto('/taller'); // El listado de la página, no el índice lateral, que repite los enlaces. await page .locator('section.bloque') .getByRole('link', { name: /Lo que hace que una letra funcione/ }) .click(); await expect(page).toHaveURL('/taller/lo-que-hace-que-una-letra-funcione'); await expect(page.getByRole('heading', { level: 1 })).toHaveText( 'Lo que hace que una letra funcione' ); // Los capítulos están pensados para seguirse en orden. await page.getByRole('link', { name: /Siguiente/ }).click(); await expect(page.getByRole('heading', { level: 1 })).toHaveText('La imagen concreta'); }); test('una ficha de género trae sus datos de creación', async ({ page }) => { await page.goto('/taller/bolero'); await expect(page.getByRole('heading', { level: 1 })).toHaveText('Bolero'); await expect(page.getByText('Santiago de Cuba, 1883')).toBeVisible(); /* * Los datos comparables van en una franja a lo ancho, no en una barra * lateral: el compás y la métrica de un género se leen juntos o no se leen. * Antes era una lista de definiciones en una columna estrecha a la derecha, * con los datos en letra pequeña y el texto escorado. */ const franja = page.locator('.datos .dato'); await expect(franja).toHaveCount(4); await expect(franja.filter({ hasText: 'Compás' })).toContainText('2/4'); await expect(franja.filter({ hasText: 'Metro' })).toContainText('Octosílabo'); await expect(page.locator('aside.lateral')).toHaveCount(0); await expect(page.getByRole('heading', { level: 2, name: 'De qué se habla' })).toBeVisible(); await expect( page.getByRole('heading', { level: 2, name: 'Rasgos de escritura' }) ).toBeVisible(); await expect( page.getByRole('heading', { level: 2, name: 'Estructura de las canciones' }) ).toBeVisible(); }); test('la franja de datos ocupa el ancho, y el texto su medida de lectura', async ({ page }) => { // Es la diferencia con la barra lateral de antes, y lo que hace que los // datos se puedan comparar de un vistazo. await page.setViewportSize({ width: 1280, height: 900 }); await page.goto('/taller/bolero'); const datos = (await page.locator('.datos').boundingBox())!; const texto = (await page.locator('.texto').boundingBox())!; expect(datos.width).toBeGreaterThan(texto.width * 1.4); expect(Math.abs(datos.x - texto.x)).toBeLessThan(2); }); test('la portada del taller también lleva el índice al lado', async ({ page }) => { // El índice está en el layout de la sección, así que acompaña a todas sus // páginas, no solo a los capítulos. await page.goto('/taller'); const indice = page.getByRole('navigation', { name: 'Contenido del taller' }); await expect(indice).toBeVisible(); await expect(indice.getByRole('link', { name: 'Bolero' })).toBeVisible(); // En la portada no hay capítulo abierto que marcar. await expect(indice.locator('a[aria-current="page"]')).toHaveCount(0); }); test('cada capítulo lleva el índice del taller al lado', async ({ page }) => { await page.goto('/taller/bolero'); const indice = page.getByRole('navigation', { name: 'Contenido del taller' }); await expect(indice).toBeVisible(); // Marca dónde estás dentro de la estructura. await expect(indice.getByRole('link', { name: 'Bolero' })).toHaveAttribute( 'aria-current', 'page' ); // Y enseña los dos bloques enteros, no solo el actual. await expect(indice.getByRole('link', { name: 'El estribillo' })).toBeVisible(); }); test('un principio no enseña ficha de creación', async ({ page }) => { // La ficha es de los estilos; en un principio sería un panel vacío. await page.goto('/taller/el-estribillo'); await expect(page.getByRole('heading', { level: 2, name: 'De un vistazo' })).toHaveCount(0); }); test('el taller está en la navegación principal', async ({ page }) => { await page.goto('/'); await page .getByRole('navigation', { name: 'Principal' }) .getByRole('link', { name: 'Taller' }) .click(); await expect(page).toHaveURL('/taller'); }); }); test.describe('Tema claro y oscuro', () => { test('sin elegir nada no hay atributo, y manda el marino', async ({ browser }) => { // Aunque el sistema pida el claro. El color es de la marca y no lo decide // una preferencia que se configuró pensando en otra cosa. const claro = await browser.newContext({ colorScheme: 'light' }); const pagina = await claro.newPage(); await pagina.goto('/taller'); await expect(pagina.locator('html')).not.toHaveAttribute('data-tema', /.+/); await expect(pagina.getByRole('button', { name: 'Cambiar al tema claro' })).toBeVisible(); await claro.close(); }); test('la elección manual manda y se recuerda', async ({ browser }) => { const claro = await browser.newContext({ colorScheme: 'light' }); const pagina = await claro.newPage(); await pagina.goto('/taller'); await pagina.getByRole('button', { name: 'Cambiar al tema claro' }).click(); await expect(pagina.locator('html')).toHaveAttribute('data-tema', 'claro'); // Sobrevive a la recarga y, sobre todo, se aplica antes de pintar: lo hace // un script en línea, no la hidratación. await pagina.reload(); await expect(pagina.locator('html')).toHaveAttribute('data-tema', 'claro'); // Y se puede volver. await pagina.getByRole('button', { name: 'Cambiar al tema oscuro' }).click(); await expect(pagina.locator('html')).toHaveAttribute('data-tema', 'oscuro'); await claro.close(); }); });