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.
163 lines
6.3 KiB
163 lines
6.3 KiB
|
1 month ago
|
import { expect, test, type Page } from '@playwright/test';
|
||
|
|
|
||
|
|
/** Añade un tema desde el listado de un álbum, por su título. */
|
||
|
|
async function anadir(page: Page, titulo: string) {
|
||
|
|
const fila = page.getByRole('listitem').filter({ hasText: titulo }).first();
|
||
|
|
await fila.getByRole('button', { name: 'Añadir' }).click();
|
||
|
|
await expect(fila.getByRole('button', { name: 'Quitar' })).toBeVisible();
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Deja el carrito con las cinco canciones de un pack completo. */
|
||
|
|
async function llenarUnPack(page: Page) {
|
||
|
|
await page.goto('/albumes/como-si-nada');
|
||
|
|
for (const titulo of ['Como si nada', 'Media verdad', 'El último tren', 'Domingo largo']) {
|
||
|
|
await anadir(page, titulo);
|
||
|
|
}
|
||
|
|
await page.goto('/albumes/dos-orillas');
|
||
|
|
await anadir(page, 'Dos orillas');
|
||
|
|
}
|
||
|
|
|
||
|
|
test.describe('Carrito', () => {
|
||
|
|
test('se puede llenar sin haber entrado', async ({ page }) => {
|
||
|
|
await page.goto('/albumes/como-si-nada');
|
||
|
|
|
||
|
|
const carrito = page.getByRole('link', { name: /^Carrito/ });
|
||
|
|
await anadir(page, 'Media verdad');
|
||
|
|
|
||
|
|
await expect(carrito).toContainText('1');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('el pago se bloquea mientras el pack está a medias', async ({ page }) => {
|
||
|
|
await page.goto('/albumes/como-si-nada');
|
||
|
|
await anadir(page, 'Media verdad');
|
||
|
|
await page.goto('/carrito');
|
||
|
|
|
||
|
|
await expect(
|
||
|
|
page.getByText('Faltan 4 canciones para completar el pack.').first()
|
||
|
|
).toBeVisible();
|
||
|
|
// Sin sesión el botón de pagar ni siquiera aparece: primero hay que entrar.
|
||
|
|
await expect(page.getByRole('link', { name: 'Entrar para comprar' })).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('propone canciones para cerrar el pack', async ({ page }) => {
|
||
|
|
await page.goto('/albumes/como-si-nada');
|
||
|
|
await anadir(page, 'Media verdad');
|
||
|
|
await page.goto('/carrito');
|
||
|
|
|
||
|
|
const sugerencias = page.getByRole('heading', { level: 2, name: 'Completa el pack' });
|
||
|
|
await expect(sugerencias).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('con cinco canciones cuenta un pack y su precio', async ({ page }) => {
|
||
|
|
await llenarUnPack(page);
|
||
|
|
await page.goto('/carrito');
|
||
|
|
|
||
|
|
await expect(page.getByText('1 pack de 5 canciones.')).toBeVisible();
|
||
|
|
await expect(page.getByText('1,50 €').first()).toBeVisible();
|
||
|
|
// Al estar completo, ya no hay nada que sugerir.
|
||
|
|
await expect(page.getByRole('heading', { level: 2, name: 'Completa el pack' })).toHaveCount(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('se pueden quitar canciones', async ({ page }) => {
|
||
|
|
await page.goto('/albumes/como-si-nada');
|
||
|
|
await anadir(page, 'Media verdad');
|
||
|
|
await page.goto('/carrito');
|
||
|
|
|
||
|
|
await page.getByRole('button', { name: 'Quitar' }).first().click();
|
||
|
|
await expect(page.getByText('No has añadido ninguna canción todavía.')).toBeVisible();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('Cuenta', () => {
|
||
|
|
test('la cuenta exige entrar', async ({ page }) => {
|
||
|
|
await page.goto('/cuenta');
|
||
|
|
|
||
|
|
await expect(page).toHaveURL(/\/entrar/);
|
||
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Entrar');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('pedir un código deja el alta en marcha', async ({ page }) => {
|
||
|
|
/*
|
||
|
|
* Correo distinto en cada ejecución: los límites del formulario de acceso
|
||
|
|
* viven en la base de datos y sobreviven entre pasadas, así que repetir la
|
||
|
|
* suite con una dirección fija acabaría chocando con el tope por correo.
|
||
|
|
*/
|
||
|
|
const correo = `prueba.e2e.${Date.now()}@example.com`;
|
||
|
|
|
||
|
|
await page.goto('/entrar');
|
||
|
|
await page.getByLabel('Correo electrónico').fill(correo);
|
||
|
|
await page.getByRole('button', { name: 'Enviarme el código' }).click();
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Lo que se comprueba es que la acción se ejecuta y responde algo
|
||
|
|
* coherente: o pasa al paso del código, o dice por qué no. Cuál de las dos
|
||
|
|
* toca depende del entorno —si hay proveedor de correo, si se han pedido
|
||
|
|
* muchos códigos desde esta IP—, así que se acepta cualquiera en vez de
|
||
|
|
* enumerar mensajes, que es lo que hacía frágil este test.
|
||
|
|
*/
|
||
|
|
await expect(page.getByLabel('Código').or(page.getByRole('alert'))).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('el paso del código se puede retomar sin pedir otro', async ({ page }) => {
|
||
|
|
// Recargar la página, o volver a ella desde el correo, no debería obligar
|
||
|
|
// a gastar un código nuevo.
|
||
|
|
await page.goto('/entrar?paso=codigo&email=prueba.e2e%40example.com');
|
||
|
|
|
||
|
|
await expect(page.getByLabel('Código')).toBeVisible();
|
||
|
|
await expect(page.getByText('prueba.e2e@example.com')).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('el navegador no deja enviar un código incompleto', async ({ page }) => {
|
||
|
|
await page.goto('/entrar?paso=codigo&email=prueba.e2e%40example.com');
|
||
|
|
|
||
|
|
const campo = page.getByLabel('Código');
|
||
|
|
await campo.fill('12');
|
||
|
|
await page.getByRole('button', { name: 'Entrar' }).click();
|
||
|
|
|
||
|
|
// El `pattern` del campo corta el envío sin salir del navegador, así que
|
||
|
|
// no llega a verse el mensaje del servidor: seguimos en el mismo paso.
|
||
|
|
await expect(campo).toBeVisible();
|
||
|
|
expect(await campo.evaluate((el) => (el as HTMLInputElement).validity.valid)).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('rechaza un código que no existe', async ({ page }) => {
|
||
|
|
await page.goto('/entrar?paso=codigo&email=nadie.e2e%40example.com');
|
||
|
|
|
||
|
|
await page.getByLabel('Código').fill('000000');
|
||
|
|
await page.getByRole('button', { name: 'Entrar' }).click();
|
||
|
|
|
||
|
|
await expect(page.getByText(/El código ha caducado o ya se ha usado/)).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('rechaza un correo mal formado', async ({ page }) => {
|
||
|
|
await page.goto('/entrar');
|
||
|
|
// `type="email"` ya filtra lo obvio; esto pasa el filtro del navegador y
|
||
|
|
// lo tiene que rechazar el servidor.
|
||
|
|
await page.getByLabel('Correo electrónico').fill('alguien@example');
|
||
|
|
await page.getByRole('button', { name: 'Enviarme el código' }).click();
|
||
|
|
|
||
|
|
await expect(page.getByText('Ese correo no parece válido.')).toBeVisible();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('Descargas', () => {
|
||
|
|
test('el audio completo no se sirve sin sesión', async ({ request }) => {
|
||
|
|
const respuesta = await request.get('/descargas/como-si-nada');
|
||
|
|
expect(respuesta.status()).toBe(401);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('el fragmento de muestra sí es público', async ({ request }) => {
|
||
|
|
const respuesta = await request.get('/muestras/como-si-nada/01-como-si-nada.wav');
|
||
|
|
|
||
|
|
expect(respuesta.ok()).toBe(true);
|
||
|
|
expect(respuesta.headers()['content-type']).toContain('audio');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('no hay ruta pública al audio completo', async ({ request }) => {
|
||
|
|
// La carpeta /audio existía antes de que el catálogo se pusiera a la
|
||
|
|
// venta; que siga muerta es parte de lo que protege los masters.
|
||
|
|
const respuesta = await request.get('/audio/como-si-nada/01-como-si-nada.wav');
|
||
|
|
expect(respuesta.status()).toBe(404);
|
||
|
|
});
|
||
|
|
});
|