// Arranque, render de los 13 tipos de la demo, consola limpia y file://. import { test, expect, irA, estado, filasChat, filaChat, tarjetas, URL_FILE, TIPOS } from "./fixtures.mjs"; test.describe("Carga inicial", () => { test("la demo carga sin errores de consola ni red externa", async ({ app }) => { await expect(app).toHaveTitle(/WhatsApp Cloud API/); await expect(app.locator("#chat-nombre")).toHaveText("Café Aurora"); await expect(app.locator("#chat-verificado")).toBeVisible(); await expect(app.locator("#chat-subtitulo")).toHaveText("en línea"); // «Hoy» + aviso de cifrado await expect(app.locator("#chat-cuerpo > li.wa-sistema")).toHaveCount(2); await expect(app.locator(".wa-fecha")).toHaveText("Hoy"); await expect(app.locator(".wa-aviso")).toContainText("servicio seguro de Meta"); // El fixture comprueba al final que no hubo errores ni peticiones externas }); test("la demo por defecto recorre los 13 tipos, uno por burbuja", async ({ app }) => { const e = await estado(app); const tipos = new Set(e.mensajes.map((m) => m.tipo)); for (const t of TIPOS) expect(tipos, `tipo ${t}`).toContain(t); await expect(filasChat(app)).toHaveCount(e.mensajes.length); await expect(tarjetas(app)).toHaveCount(e.mensajes.length); const porTipo = {}; for (const m of e.mensajes) porTipo[m.tipo] ??= m; // el primero de cada tipo const fila = (t) => filaChat(app, porTipo[t].id); await expect(fila("text").locator(".wa-texto")).toContainText("Bienvenido a Café Aurora"); await expect(fila("image").locator("img.wa-media")).toBeVisible(); await expect(fila("image").locator(".wa-caption")).toContainText("Nuevo horario"); await expect(fila("video").locator(".wa-play-badge")).toBeVisible(); await expect(fila("audio").locator(".audio-dur")).toHaveText("0:12"); await expect(fila("audio").locator(".audio-mic")).toBeVisible(); // nota de voz await expect(fila("document").locator(".doc-nombre")).toHaveText("carta-cafe-aurora.pdf"); await expect(fila("document").locator(".doc-sub")).toContainText("PDF", { ignoreCase: true }); await expect(fila("sticker").locator(".sticker-img")).toBeVisible(); await expect(fila("sticker").locator(".bubble")).toHaveCount(0); // sin burbuja await expect(fila("location").locator(".loc-nombre")).toHaveText("Café Aurora"); await expect(fila("location").locator(".loc-dir")).toContainText("Calle Mayor"); await expect(fila("contact").locator(".contact-nombre")).toHaveText("Luna Mendoza"); await expect(fila("contact").locator(".contact-accion")).toHaveText(["Mensaje", "Guardar contacto"]); await expect(fila("template").locator(".tpl-media")).toBeVisible(); await expect(fila("template").locator(".qr-btn")).toHaveCount(2); await expect(fila("template").locator(".template-footer")).toHaveText("Café Aurora · Reservas"); await expect(fila("buttons").locator("button.qr-btn")).toHaveCount(3); await expect(fila("list").locator("button.qr-btn")).toHaveText("Ver opciones"); await expect(fila("cta_url").locator("a.qr-btn")).toHaveAttribute("href", "https://cafeaurora.example/tienda"); await expect(fila("cta_url").locator("a.qr-btn")).toHaveAttribute("target", "_blank"); await expect(fila("carousel").locator(".carousel .card")).toHaveCount(3); }); test("solo los mensajes salientes llevan ticks; la cola va en el último del grupo", async ({ app }) => { const e = await estado(app); for (const m of e.mensajes) { const ticks = filaChat(app, m.id).locator(":scope > * .meta .tick"); if (m.dir === "out") await expect(ticks.first()).toBeVisible(); else await expect(ticks).toHaveCount(0); } const out = e.mensajes.find((m) => m.dir === "out" && m.estado === "read"); await expect(filaChat(app, out.id).locator(".tick-read")).toHaveCount(1); // m1 (in) y m2 (out) son grupos de uno → ambos .grupo.ultimo await expect(filaChat(app, "m1")).toHaveClass(/grupo/); await expect(filaChat(app, "m1")).toHaveClass(/ultimo/); await expect(filaChat(app, "m3")).toHaveClass(/grupo/); await expect(filaChat(app, "m3")).not.toHaveClass(/ultimo/); }); test("el carrusel se desplaza en horizontal", async ({ app }) => { const e = await estado(app); const id = e.mensajes.find((m) => m.tipo === "carousel").id; const car = filaChat(app, id).locator(".carousel"); await car.scrollIntoViewIfNeeded(); const { ancho, visible } = await car.evaluate((el) => ({ ancho: el.scrollWidth, visible: el.clientWidth })); expect(ancho).toBeGreaterThan(visible); await car.evaluate((el) => { el.scrollLeft = 200; }); expect(await car.evaluate((el) => el.scrollLeft)).toBeGreaterThan(0); }); test("el marco del iPhone tiene isla, status bar e indicador de inicio", async ({ app }) => { await expect(app.locator("#iphone")).toBeVisible(); await expect(app.locator("#isla")).toBeVisible(); await expect(app.locator("#statusbar .sb-hora")).toHaveText("9:41"); await expect(app.locator("#home-indicator")).toBeVisible(); const caja = await app.locator("#iphone").boundingBox(); // proporción 393×852 (con tolerancia por el escalado) expect(caja.height / caja.width).toBeGreaterThan(2.0); expect(caja.height / caja.width).toBeLessThan(2.3); }); }); test.describe("Apertura por file://", () => { test("index.html funciona abierto como archivo local", async ({ page }) => { await irA(page, URL_FILE); expect(page.url()).toMatch(/^file:\/\//); await expect(page.locator("#chat-nombre")).toHaveText("Café Aurora"); const n = (await estado(page)).mensajes.length; await expect(filasChat(page)).toHaveCount(n); await expect(tarjetas(page)).toHaveCount(n); // interacción básica: composer y persistencia await page.locator("#composer-input").fill("Hola desde file://"); await page.locator("#composer-input").press("Enter"); await expect(page.locator("#chat-cuerpo .wa-row.out").last()).toContainText("Hola desde file://"); await page.locator("#btn-tema").click(); await expect(page.locator("body")).toHaveAttribute("data-tema", "oscuro"); await page.reload(); await expect(page.locator("body")).toHaveAttribute("data-tema", "oscuro"); }); });