WIP: mejoras de fidelidad, editor, lógica y suite e2e (checkpoint tras reinicio)

This commit is contained in:
urieljareth
2026-09-27 20:46:51 -06:00
parent dc6e31a9d7
commit 9a5f21badf
33 changed files with 7719 additions and 1129 deletions
+107
View File
@@ -0,0 +1,107 @@
// 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");
});
});