- Deshacer/rehacer guardan y reponen «wam-demo-activa» con cada instantánea
({estado, activa}); «Actualizar» pide un segundo clic si el contenido cambió
de cuenta o de número de mensajes respecto a la demo guardada.
- WAM.puntoDeshacer cierra los avisos con acción: su «Deshacer» ya no
desharía el cambio que anuncian.
- «Todas las opciones» replica botonAccion: URL bloqueada o no válida se
pinta como fila inerte (aria-disabled, sin data-opcion) en el grupo de
enlaces.
- Hojas inferiores: enfocan la primera fila (o la X), retienen Tab dentro del
panel y devuelven el foco al cerrar (origen, fila repintada o
#chat-cuerpo); no roban el foco durante «Reproducir demo».
- Tests e2e de regresión en tests/e2e/regresiones.spec.mjs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
153 lines
7.2 KiB
JavaScript
153 lines
7.2 KiB
JavaScript
// Regresiones: deshacer/rehacer con demos guardadas, avisos «Deshacer»
|
|
// obsoletos, hoja «Todas las opciones» con URLs bloqueadas y foco en las hojas.
|
|
import { test, expect, estado, filaChat, cargarEstado, rapido } from "./fixtures.mjs";
|
|
|
|
async function nombreCuenta(page, v) {
|
|
await page.evaluate(() => { const d = document.getElementById("bloque-cuenta"); if (d) d.open = true; });
|
|
await page.locator("#cuenta-nombre").fill(v);
|
|
await expect.poll(async () => (await estado(page)).cuenta.nombre).toBe(v);
|
|
}
|
|
|
|
const activaLS = (page) => page.evaluate(() => localStorage.getItem("wam-demo-activa"));
|
|
|
|
async function guardarComo(page, nombre) {
|
|
await page.locator("#btn-demos").click();
|
|
const dlg = page.locator("#dlg-demos");
|
|
await dlg.locator("#dlg-demo-nombre").fill(nombre);
|
|
await dlg.getByRole("button", { name: /Guardar/ }).click();
|
|
await expect(dlg.locator(".wam-demo-nombre", { hasText: new RegExp("^" + nombre + "$") })).toBeVisible();
|
|
await dlg.locator(".wam-dlg-pie").getByRole("button", { name: "Cerrar" }).click();
|
|
}
|
|
|
|
test.describe("Deshacer y demos guardadas", () => {
|
|
test("deshacer / rehacer reponen la demo activa y «Actualizar» no pisa otra demo", async ({ app }) => {
|
|
await guardarComo(app, "A");
|
|
const idA = await activaLS(app);
|
|
// cargar un escenario y guardarlo como B
|
|
await app.locator("#btn-demos").click();
|
|
await app.locator("#dlg-demos .wam-esc").first().click();
|
|
await nombreCuenta(app, "Cuenta B");
|
|
await guardarComo(app, "B");
|
|
const idB = await activaLS(app);
|
|
expect(idB).not.toBe(idA);
|
|
// abrir A
|
|
await app.locator("#btn-demos").click();
|
|
await app.locator("#dlg-demos .wam-demo").filter({ has: app.locator(".wam-demo-nombre", { hasText: /^A$/ }) })
|
|
.getByRole("button", { name: "Abrir" }).click();
|
|
expect(await activaLS(app)).toBe(idA);
|
|
// deshacer: vuelve el contenido de B y también la demo activa B
|
|
await app.locator("#btn-deshacer").click();
|
|
expect((await estado(app)).cuenta.nombre).toBe("Cuenta B");
|
|
expect(await activaLS(app)).toBe(idB);
|
|
await app.locator("#btn-demos").click();
|
|
await expect(app.locator("#dlg-demo-nombre")).toHaveValue("B");
|
|
await app.locator("#dlg-demos .wam-dlg-pie").getByRole("button", { name: "Cerrar" }).click();
|
|
// rehacer: vuelve A
|
|
await app.locator("#btn-rehacer").click();
|
|
expect(await activaLS(app)).toBe(idA);
|
|
// deshacer un escenario (que borra la clave activa) la repone
|
|
await app.locator("#btn-demos").click();
|
|
await app.locator("#dlg-demos .wam-esc").first().click();
|
|
expect(await activaLS(app)).toBeNull();
|
|
await app.locator("#btn-deshacer").click();
|
|
expect(await activaLS(app)).toBe(idA);
|
|
// el hueco de A sigue siendo A
|
|
const slotA = await app.evaluate((id) => JSON.parse(localStorage.getItem("wam-demo-" + id) || "null"), idA);
|
|
expect(slotA && slotA.cuenta.nombre).not.toBe("Cuenta B");
|
|
});
|
|
|
|
test("«Actualizar» pide un segundo clic si cambió la cuenta", async ({ app }) => {
|
|
await guardarComo(app, "A");
|
|
const idA = await activaLS(app);
|
|
const nombreOriginal = (await estado(app)).cuenta.nombre;
|
|
await nombreCuenta(app, "Otra cuenta");
|
|
await app.locator("#btn-demos").click();
|
|
const bGuardar = app.locator("#dlg-demos .wam-dlg-primario");
|
|
await expect(bGuardar).toHaveText("Actualizar");
|
|
await bGuardar.click();
|
|
await expect(bGuardar).toHaveText(/Sobrescribir/);
|
|
let slot = await app.evaluate((id) => JSON.parse(localStorage.getItem("wam-demo-" + id)), idA);
|
|
expect(slot.cuenta.nombre).toBe(nombreOriginal);
|
|
await bGuardar.click();
|
|
slot = await app.evaluate((id) => JSON.parse(localStorage.getItem("wam-demo-" + id)), idA);
|
|
expect(slot.cuenta.nombre).toBe("Otra cuenta");
|
|
});
|
|
|
|
test("el «Deshacer» de un aviso desaparece cuando hay otro cambio", async ({ app }) => {
|
|
const n = (await estado(app)).mensajes.length;
|
|
await app.evaluate(() => WAM.borrarMensaje(0));
|
|
const accion = app.locator("#ed-toasts .ed-toast-accion");
|
|
await expect(accion).toHaveCount(1);
|
|
await nombreCuenta(app, "Nombre nuevo");
|
|
// sin esperar: el aviso caduca solo a los 6 s y enmascararía el fallo
|
|
expect(await accion.count()).toBe(0);
|
|
expect((await estado(app)).mensajes.length).toBe(n - 1);
|
|
});
|
|
});
|
|
|
|
const PLANTILLA = [
|
|
{
|
|
id: "m1", dir: "in", tipo: "template", hora: "10:00", body: "Elige",
|
|
botones: [
|
|
{ texto: "Uno" }, { texto: "Dos" },
|
|
{ texto: "Bloqueado", url: "javascript:alert(1)" },
|
|
{ texto: "Relativa", url: "/menu" },
|
|
{ texto: "Cinco" },
|
|
],
|
|
},
|
|
{
|
|
id: "m2", dir: "in", tipo: "list", hora: "10:01", body: "Menú", boton: "Ver menú",
|
|
secciones: [{ titulo: "S", filas: [{ id: "f1", titulo: "Fila 1" }, { id: "f2", titulo: "Fila 2" }] }],
|
|
},
|
|
];
|
|
|
|
test.describe("Hojas inferiores", () => {
|
|
test.beforeEach(async ({ app }) => {
|
|
await rapido(app);
|
|
await cargarEstado(app, { mensajes: PLANTILLA });
|
|
await expect(filaChat(app, "m1")).toHaveCount(1);
|
|
});
|
|
|
|
test("«Todas las opciones» pinta inertes los botones con URL bloqueada o no válida", async ({ app }) => {
|
|
const fila = filaChat(app, "m1");
|
|
const inertesBurbuja = await fila.locator(".qr-btn-inactiva").allTextContents();
|
|
expect(inertesBurbuja.map((t) => t.trim())).toEqual(["Bloqueado", "Relativa"]);
|
|
await fila.locator("[data-ver-todas]").click();
|
|
const hoja = app.locator("#sheet");
|
|
const inertesHoja = await hoja.locator('.sheet-fila.qr-btn-inactiva[aria-disabled="true"]').allTextContents();
|
|
expect(inertesHoja.map((t) => t.trim())).toEqual(inertesBurbuja.map((t) => t.trim()));
|
|
await expect(hoja.locator("[data-opcion]")).toHaveCount(3);
|
|
await expect(hoja.locator('[data-opcion="2"], [data-opcion="3"]')).toHaveCount(0);
|
|
await expect(hoja.locator(".sheet-fila")).toHaveCount(5);
|
|
});
|
|
|
|
test("foco: la hoja lo recibe, lo retiene con Tab y lo devuelve al cerrar", async ({ app }) => {
|
|
const toggle = filaChat(app, "m1").locator("[data-ver-todas]");
|
|
await toggle.focus();
|
|
await app.keyboard.press("Enter");
|
|
await expect(app.locator("#sheet")).toHaveCount(1);
|
|
const enHoja = () => app.evaluate(() => !!document.activeElement.closest("#sheet"));
|
|
expect(await enHoja()).toBe(true);
|
|
for (let k = 0; k < 6; k++) {
|
|
await app.keyboard.press("Tab");
|
|
expect(await enHoja()).toBe(true);
|
|
}
|
|
await app.keyboard.press("Shift+Tab");
|
|
expect(await enHoja()).toBe(true);
|
|
await app.keyboard.press("Escape");
|
|
await expect(app.locator("#sheet")).toHaveCount(0);
|
|
expect(await app.evaluate(() => document.activeElement.hasAttribute("data-ver-todas"))).toBe(true);
|
|
});
|
|
|
|
test("foco: la lista «Ver opciones» enfoca su primera fila", async ({ app }) => {
|
|
const abridor = filaChat(app, "m2").locator("[data-flujo]:not([data-btn]):not([data-card])").first();
|
|
await abridor.focus();
|
|
await app.keyboard.press("Enter");
|
|
await expect(app.locator("#sheet")).toHaveCount(1);
|
|
expect(await app.evaluate(() => document.activeElement.getAttribute("data-fila"))).toBe("0");
|
|
await app.locator("#sheet .sheet-cerrar").click();
|
|
await expect(app.locator("#sheet")).toHaveCount(0);
|
|
expect(await app.evaluate(() => document.activeElement.closest('[data-id="m2"]') !== null)).toBe(true);
|
|
});
|
|
});
|