// Los 13 tipos: alta desde la paleta, edición en el formulario y reflejo en el chat. import { test, expect, estado, vaciar, anadirTipo, campo, filaChat, tarjetas, modoUrl, PNG_1PX, TIPOS, } from "./fixtures.mjs"; const DATA_PNG = "data:image/png;base64," + PNG_1PX.toString("base64"); /** * Por tipo: selector que debe existir en la burbuja recién creada, ediciones * [ruta relativa, valor] y comprobaciones sobre la fila del chat tras editar. */ const CASOS = { text: { base: ".bubble .wa-texto", editar: [["texto", "Pedido *listo* para _recoger_"]], comprobar: async (fila) => { await expect(fila.locator(".wa-texto strong")).toHaveText("listo"); await expect(fila.locator(".wa-texto em")).toHaveText("recoger"); }, }, image: { base: "img.wa-media", url: "src", editar: [["src", DATA_PNG], ["caption", "Foto del local"]], comprobar: async (fila) => { await expect(fila.locator("img.wa-media")).toHaveAttribute("src", DATA_PNG); await expect(fila.locator(".wa-caption")).toContainText("Foto del local"); }, }, video: { base: ".wa-media-wrap .wa-play-badge", editar: [["caption", "Tour virtual"]], comprobar: async (fila) => { await expect(fila.locator(".wa-caption")).toContainText("Tour virtual"); }, }, audio: { base: ".wa-audio .audio-wave", editar: [["duracion", "1:05"]], comprobar: async (fila) => { await expect(fila.locator(".audio-dur")).toHaveText("1:05"); await expect(fila.locator(".audio-mic")).toBeVisible(); }, }, document: { base: ".wa-doc .doc-panel", editar: [["filename", "informe-trimestral.xlsx"], ["caption", "Resultados"]], comprobar: async (fila) => { await expect(fila.locator(".doc-nombre")).toHaveText("informe-trimestral.xlsx"); await expect(fila.locator(".doc-sub")).toContainText(/xlsx/i); await expect(fila.locator(".wa-caption")).toContainText("Resultados"); }, }, sticker: { base: ".sticker-box .sticker-img", url: "src", editar: [["src", DATA_PNG]], comprobar: async (fila) => { await expect(fila.locator(".sticker-img")).toHaveAttribute("src", DATA_PNG); }, }, location: { base: ".wa-loc .loc-mapa", editar: [["nombre", "Sucursal Norte"], ["direccion", "Av. Reforma 500"]], comprobar: async (fila) => { await expect(fila.locator(".loc-nombre")).toHaveText("Sucursal Norte"); await expect(fila.locator(".loc-dir")).toHaveText("Av. Reforma 500"); }, }, contact: { base: ".wa-contact .contact-nombre", editar: [["nombre", "Ana Torres"], ["telefono", "+52 55 1234 5678"]], comprobar: async (fila) => { await expect(fila.locator(".contact-nombre")).toHaveText("Ana Torres"); }, }, template: { base: ".bubble.tpl .tpl-body", editar: [["body", "Tu cita es mañana"], ["footer", "Clínica Demo"], ["botones.0.texto", "Confirmo"]], comprobar: async (fila) => { await expect(fila.locator(".tpl-body")).toHaveText("Tu cita es mañana"); await expect(fila.locator(".template-footer")).toHaveText("Clínica Demo"); await expect(fila.locator(".qr-btn .btn-label")).toHaveText(["Confirmo"]); }, }, buttons: { base: ".bubble.tpl button.qr-btn", editar: [["body", "¿Te ayudo?"], ["botones.0.texto", "Sí, por favor"]], comprobar: async (fila) => { await expect(fila.locator(".tpl-body")).toHaveText("¿Te ayudo?"); await expect(fila.locator("button.qr-btn .btn-label")).toHaveText(["Sí, por favor"]); }, }, list: { base: ".bubble.tpl button.qr-btn[data-flujo]", editar: [["header", "Catálogo"], ["body", "Elige tu plan"], ["boton", "Ver planes"]], comprobar: async (fila) => { await expect(fila.locator(".tpl-header-txt")).toHaveText("Catálogo"); await expect(fila.locator(".tpl-body")).toHaveText("Elige tu plan"); await expect(fila.locator("button.qr-btn")).toHaveText("Ver planes"); }, }, cta_url: { base: ".bubble.tpl .qr-btn", editar: [["textoBoton", "Ir a la web"], ["url", "https://example.org/promo"], ["body", "Promo de temporada"]], comprobar: async (fila) => { const a = fila.locator("a.qr-btn"); await expect(a).toHaveAttribute("href", "https://example.org/promo"); await expect(a).toHaveAttribute("target", "_blank"); await expect(a).toContainText("Ir a la web"); await expect(fila.locator(".tpl-body")).toHaveText("Promo de temporada"); }, }, carousel: { base: ".carousel .card", editar: [["body", "Novedades"], ["cards.0.texto", "Plan Básico"], ["cards.1.textoBoton", "Lo quiero"]], comprobar: async (fila) => { await expect(fila.locator(".card")).toHaveCount(2); await expect(fila.locator(".wa-stack > .bubble .wa-texto")).toContainText("Novedades"); await expect(fila.locator(".card").nth(0).locator(".card-body")).toHaveText("Plan Básico"); await expect(fila.locator(".card").nth(1).locator(".btn-label")).toHaveText("Lo quiero"); }, }, }; test.describe("Los 13 tipos de mensaje", () => { test("la paleta ofrece exactamente los 13 tipos", async ({ app }) => { const tipos = await app.locator('#nuevo-tipo [data-acc="anadir-tipo"]').evaluateAll((els) => els.map((e) => e.dataset.tipo)); expect([...tipos].sort()).toEqual([...TIPOS].sort()); }); for (const tipo of TIPOS) { test(`${tipo}: añadir, editar y ver el reflejo en el chat`, async ({ app }) => { const caso = CASOS[tipo]; await vaciar(app); const id = await anadirTipo(app, tipo); // Tarjeta abierta y burbuja en el chat const t = app.locator(`#lista-mensajes > .ed-tarjeta[data-id="${id}"]`); await expect(tarjetas(app)).toHaveCount(1); await expect(t).toHaveClass(/ed-abierta/); const fila = filaChat(app, id); await expect(fila).toBeVisible(); await expect(fila.locator(caso.base).first()).toBeVisible(); // Alta: el foco va a un campo de la tarjeta nueva await expect.poll(() => app.evaluate((i) => { const a = document.activeElement; return !!(a && a.closest(`.ed-tarjeta[data-id="${i}"]`)); }, id)).toBe(true); // Edición for (const [rel, valor] of caso.editar) { const ruta = `mensajes.0.${rel}`; const inp = caso.url === rel ? await modoUrl(app, ruta) : campo(app, ruta); await inp.fill(valor); } await caso.comprobar(fila); // El estado vivo y el persistido reflejan la edición const e = await estado(app); for (const [rel, valor] of caso.editar) { const v = rel.split(".").reduce((o, k) => (o == null ? o : o[k]), e.mensajes[0]); expect(v, rel).toBe(valor); } const guardado = await app.evaluate(() => { WAM.guardarYa(); return JSON.parse(localStorage.getItem("wam-mockup-v1")); }); expect(guardado.mensajes[0].tipo).toBe(tipo); }); } test("cambiar la dirección a «Salida» pinta ticks y el selector de estado", async ({ app }) => { await vaciar(app); const id = await anadirTipo(app, "text"); const fila = filaChat(app, id); await expect(fila).toHaveClass(/\bin\b/); await app.locator('#lista-mensajes label.ed-seg-op:has(input[data-rol="dir"][value="out"])').click(); await expect(fila).toHaveClass(/\bout\b/); await expect(fila.locator(".tick")).toHaveCount(1); const sel = campo(app, "mensajes.0.estado"); await sel.selectOption("read"); await expect(filaChat(app, id).locator(".tick-read")).toBeVisible(); await sel.selectOption("delivered"); await expect(filaChat(app, id).locator(".tick")).toBeVisible(); await expect(filaChat(app, id).locator(".tick-read")).toHaveCount(0); expect((await estado(app)).mensajes[0]).toMatchObject({ dir: "out", estado: "delivered" }); }); test("editar la hora se refleja en la burbuja", async ({ app }) => { await vaciar(app); const id = await anadirTipo(app, "text"); await campo(app, "mensajes.0.hora").fill("09:41"); await expect(filaChat(app, id).locator(".meta")).toContainText("09:41"); }); test("audio: desactivar «nota de voz» cambia a audio con auriculares", async ({ app }) => { await vaciar(app); const id = await anadirTipo(app, "audio"); await campo(app, "mensajes.0.voz").uncheck(); await expect(filaChat(app, id).locator(".audio-musica")).toBeVisible(); await expect(filaChat(app, id).locator(".audio-mic")).toHaveCount(0); expect((await estado(app)).mensajes[0].voz).toBe(false); }); test("plantilla: cabecera opcional de texto, imagen y documento", async ({ app }) => { await vaciar(app); const id = await anadirTipo(app, "template"); const fila = filaChat(app, id); await app.locator("#lista-mensajes label.ed-switch", { hasText: "Mostrar cabecera" }).click(); await expect(app.locator('#lista-mensajes input[data-rol="header-toggle"]')).toBeChecked(); const tipoCab = (v) => app.locator(`#lista-mensajes [data-acc="header-tipo"][data-valor="${v}"]`); await tipoCab("texto").click(); await campo(app, "mensajes.0.headerTexto").fill("Aviso importante"); await expect(fila.locator(".tpl-header-txt")).toHaveText("Aviso importante"); await tipoCab("imagen").click(); await expect(fila.locator("img.tpl-media")).toBeVisible(); await tipoCab("documento").click(); await expect(fila.locator(".tpl-header-doc .doc-nombre")).toBeVisible(); expect((await estado(app)).mensajes[0].headerTipo).toBe("documento"); }); });