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.
197 lines
7.4 KiB
197 lines
7.4 KiB
import { expect, test } from '@playwright/test';
|
|
|
|
test.describe('Navegación', () => {
|
|
test('la portada presenta al artista y lleva a la música', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Senza Paura');
|
|
await expect(page.getByText('Letrista y compositor').first()).toBeVisible();
|
|
|
|
// La cabecera destaca un tema y ofrece el catálogo entero.
|
|
await page.getByRole('link', { name: 'Toda la música' }).click();
|
|
await expect(page).toHaveURL('/musica');
|
|
});
|
|
|
|
test('el fondo de la cabecera es propio, no un incrustado', async ({ page }) => {
|
|
// Lo que se comprueba es la decisión, no la técnica con que esté hecha:
|
|
// un iframe de terceros en la primera pantalla pesaría más que el resto de
|
|
// la página y dejaría cookies antes de que nadie pida ver nada.
|
|
await page.goto('/');
|
|
|
|
await expect(page.locator('iframe')).toHaveCount(0);
|
|
// Y el fondo existe: hoy es un shader propio sobre una imagen propia.
|
|
await expect(page.locator('.fondo')).not.toHaveCount(0);
|
|
});
|
|
|
|
test('la cabecera acompaña al desplazamiento', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.evaluate(() => window.scrollBy(0, 1400));
|
|
|
|
const cabecera = page.getByRole('banner');
|
|
await expect(cabecera).toBeVisible();
|
|
const caja = await cabecera.boundingBox();
|
|
expect(caja?.y).toBeLessThan(1);
|
|
});
|
|
|
|
test('el botón de tema cierra el menú', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
const ultimo = page.locator('header .cuenta > *').last();
|
|
await expect(ultimo).toHaveRole('button');
|
|
await expect(ultimo).toHaveAttribute('aria-label', /tema/);
|
|
});
|
|
|
|
test('la música se presenta por estilos', async ({ page }) => {
|
|
await page.goto('/musica');
|
|
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Música');
|
|
|
|
// El estilo principal va primero.
|
|
const estilos = page.getByRole('heading', { level: 2 });
|
|
await expect(estilos.first()).toHaveText('Baladas románticas');
|
|
|
|
for (const estilo of ['Clásica', 'Rock', 'Pop melódico']) {
|
|
await expect(page.getByRole('heading', { level: 2, name: estilo })).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('cada estilo tiene su propia página, con su propio título', async ({ page }) => {
|
|
await page.goto('/musica');
|
|
await page.getByRole('heading', { level: 2, name: 'Clásica' }).getByRole('link').click();
|
|
|
|
await expect(page).toHaveURL('/musica/clasica');
|
|
// En su página el estilo es el h1, no un h2 heredado del listado: es de lo
|
|
// que va la página.
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Clásica');
|
|
await expect(page.getByRole('link', { name: 'Preludios para piano' }).first()).toBeVisible();
|
|
});
|
|
|
|
test('la página de un estilo lleva al lado todos los demás', async ({ page }) => {
|
|
await page.goto('/musica/clasica');
|
|
|
|
const lateral = page.getByRole('navigation', { name: 'Estilos' });
|
|
await expect(lateral).toBeVisible();
|
|
await expect(lateral.getByRole('link', { name: /Rock/ })).toBeVisible();
|
|
// Y marca en cuál estás.
|
|
await expect(lateral.getByRole('link', { name: /Clásica/ })).toHaveAttribute(
|
|
'aria-current',
|
|
'page'
|
|
);
|
|
});
|
|
|
|
test('desde un estilo se llega al álbum y del álbum al tema', async ({ page }) => {
|
|
await page.goto('/musica/baladas-romanticas');
|
|
|
|
await page.getByRole('link', { name: 'Como si nada' }).first().click();
|
|
await expect(page).toHaveURL('/albumes/como-si-nada');
|
|
await expect(page.getByRole('heading', { level: 2, name: 'Temas' })).toBeVisible();
|
|
|
|
await page.getByRole('link', { name: 'Media verdad' }).click();
|
|
await expect(page).toHaveURL('/canciones/media-verdad');
|
|
});
|
|
|
|
test('una dirección inexistente muestra la página de error', async ({ page }) => {
|
|
const respuesta = await page.goto('/canciones/no-existe');
|
|
|
|
expect(respuesta?.status()).toBe(404);
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Página no encontrada');
|
|
});
|
|
});
|
|
|
|
test.describe('Carrusel de la portada', () => {
|
|
test('tiene tres láminas y solo se ve una', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
await expect(page.locator('.lamina')).toHaveCount(3);
|
|
await expect(page.locator('.lamina.visible')).toHaveCount(1);
|
|
});
|
|
|
|
test('los puntos llevan a cada lámina', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
await page.getByRole('button', { name: 'Ir a: Aprender a escribir letras' }).click();
|
|
await expect(
|
|
page.getByRole('heading', { name: /Quieres escribir tus propias letras/ })
|
|
).toBeVisible();
|
|
|
|
await page.getByRole('button', { name: 'Ir a: Comprar canciones' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Llévate las canciones' })).toBeVisible();
|
|
});
|
|
|
|
test('el teclado no entra en las láminas que no se ven', async ({ page }) => {
|
|
// Van con `inert`: sin eso, tabular desde la cabecera cae en botones
|
|
// invisibles y el foco se pierde.
|
|
await page.goto('/');
|
|
|
|
const ocultas = page.locator('.lamina:not(.visible)');
|
|
await expect(ocultas).toHaveCount(2);
|
|
for (const lamina of await ocultas.all()) {
|
|
await expect(lamina).toHaveJSProperty('inert', true);
|
|
}
|
|
});
|
|
|
|
test('se puede parar el paso automático', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
const parar = page.getByRole('button', { name: 'Parar el paso automático' });
|
|
await expect(parar).toBeVisible();
|
|
await parar.click();
|
|
await expect(page.getByRole('button', { name: 'Reanudar el paso automático' })).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('Fondo animado de la portada', () => {
|
|
test('se pinta y arranca', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
// La clase solo se pone cuando el programa ha compilado y está dibujando:
|
|
// que exista el canvas no significa que haya salido nada.
|
|
await expect(page.locator('canvas.activo')).toHaveCount(1);
|
|
});
|
|
|
|
test('con movimiento reducido no se monta', async ({ page }) => {
|
|
// Quien pide menos movimiento no quiere una primera pantalla que ondula.
|
|
// En tema oscuro, porque es donde hay imagen debajo que comprobar: en
|
|
// claro el fondo lo pinta el propio shader.
|
|
await page.emulateMedia({ reducedMotion: 'reduce', colorScheme: 'dark' });
|
|
await page.goto('/');
|
|
|
|
await expect(page.locator('canvas.activo')).toHaveCount(0);
|
|
// Y debajo sigue el fondo de siempre, no un hueco.
|
|
await expect(page.locator('.fondo img').first()).toBeAttached();
|
|
});
|
|
|
|
test('deja de dibujar cuando ya no se ve', async ({ page }) => {
|
|
// Un shader a pantalla completa detrás de una página por la que ya has
|
|
// bajado es batería tirada.
|
|
await page.goto('/');
|
|
await expect(page.locator('canvas.activo')).toHaveCount(1);
|
|
|
|
await page.evaluate(() => {
|
|
(window as unknown as { cuadros: number }).cuadros = 0;
|
|
const original = window.requestAnimationFrame;
|
|
window.requestAnimationFrame = (cb) =>
|
|
original(() => {
|
|
(window as unknown as { cuadros: number }).cuadros++;
|
|
cb(performance.now());
|
|
});
|
|
});
|
|
|
|
await page.evaluate(() => window.scrollTo(0, 5000));
|
|
await page.waitForTimeout(600);
|
|
const antes = await page.evaluate(() => (window as unknown as { cuadros: number }).cuadros);
|
|
await page.waitForTimeout(800);
|
|
const despues = await page.evaluate(() => (window as unknown as { cuadros: number }).cuadros);
|
|
|
|
expect(despues - antes).toBe(0);
|
|
});
|
|
|
|
test('solo lo lleva la cabecera, no las tres láminas', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
// Tres shaders a pantalla completa pintando lo mismo a la vez no aportan
|
|
// nada y cuestan el triple.
|
|
await expect(page.locator('canvas')).toHaveCount(1);
|
|
});
|
|
});
|