import { expect, test, type Page } from "@playwright/test"; import { ANCHOS, RUTAS_MUESTRA } from "./rutas"; /** * Responsividad. Corre en los cuatro proyectos de la configuración, incluidos * los dos de WebKit: Safari e iOS son donde de verdad se rompe el diseño, y el * cliente lo pidió por nombre. * * La falla que perseguimos es el desbordamiento horizontal del documento. Una * tabla que se desplaza dentro de su contenedor está bien; el cuerpo de la * página desplazándose en horizontal no lo está nunca. */ /** * El barrido de anchos corre en Chromium y en WebKit de escritorio, no en los * cuatro proyectos. El desborde horizontal lo produce el CSS, no el motor, así * que repetir 5 anchos × 10 rutas en cuatro navegadores multiplicaba el tiempo * de la suite sin agregar información. WebKit sigue incluido porque es donde * aparecen las diferencias reales de `dvh`, `env()` y flexbox, y los proyectos * móviles conservan las pruebas de interacción de más abajo. */ test.describe.configure({ mode: "parallel" }); async function desbordaEnHorizontal(page: Page): Promise<{ desborda: boolean; ancho: number; visible: number }> { return page.evaluate(() => { const doc = document.documentElement; return { // 2px de tolerancia por redondeo de subpíxel desborda: doc.scrollWidth > doc.clientWidth + 2, ancho: doc.scrollWidth, visible: doc.clientWidth, }; }); } /** Devuelve los elementos que sobresalen del ancho de la ventana, para poder señalarlos. */ async function culpablesDeDesborde(page: Page): Promise { return page.evaluate(() => { const limite = document.documentElement.clientWidth; const malos: string[] = []; for (const el of Array.from(document.querySelectorAll("body *"))) { const r = el.getBoundingClientRect(); if (r.width === 0 || r.height === 0) continue; if (r.right > limite + 2 || r.left < -2) { // ignoramos lo que desborda a propósito dentro de su propio contenedor con scroll let padre: HTMLElement | null = el.parentElement; let contenido = false; while (padre && padre !== document.body) { const estilo = getComputedStyle(padre); if (estilo.overflowX === "auto" || estilo.overflowX === "scroll" || estilo.overflowX === "hidden") { contenido = true; break; } padre = padre.parentElement; } if (contenido) continue; const clases = typeof el.className === "string" ? el.className.slice(0, 60) : ""; malos.push(`${el.tagName.toLowerCase()}${clases ? "." + clases.trim().split(/\s+/).join(".") : ""}`); if (malos.length >= 6) break; } } return malos; }); } for (const ruta of RUTAS_MUESTRA) { test(`sin desborde horizontal en ${ruta}`, async ({ page }, info) => { test.skip( !["escritorio-chromium", "escritorio-webkit"].includes(info.project.name), "El barrido de anchos corre solo en los dos proyectos de escritorio; ver la nota de arriba.", ); await page.goto(ruta, { waitUntil: "networkidle" }); for (const ancho of ANCHOS) { await page.setViewportSize({ width: ancho, height: 900 }); // dar chance al reflow y a las imágenes await page.waitForTimeout(220); const r = await desbordaEnHorizontal(page); if (r.desborda) { const culpables = await culpablesDeDesborde(page); expect( r.desborda, `${ruta} desborda a ${ancho}px (documento ${r.ancho}px sobre ventana ${r.visible}px). Sospechosos: ${culpables.join(", ") || "no identificados"}`, ).toBe(false); } } }); } test("el menú móvil abre, es navegable y cierra", async ({ page }) => { await page.setViewportSize({ width: 375, height: 780 }); await page.goto("/", { waitUntil: "domcontentloaded" }); const disparador = page.getByRole("button", { name: /abrir men[úu]/i }); await expect(disparador).toBeVisible(); await disparador.click(); const menu = page.locator("#menu-movil"); await expect(menu).toBeVisible(); await expect(menu.getByRole("link").first()).toBeVisible(); await page.getByRole("button", { name: /cerrar men[úu]/i }).click(); await expect(menu).toBeHidden(); }); test("el widget de WhatsApp no tapa contenido ni desborda en móvil", async ({ page }) => { await page.setViewportSize({ width: 320, height: 640 }); await page.goto("/", { waitUntil: "domcontentloaded" }); await page.getByRole("button", { name: /contactar por whatsapp/i }).click(); const panel = page.locator("#panel-whatsapp"); await expect(panel).toBeVisible(); const caja = await panel.boundingBox(); expect(caja, "el panel debe tener caja").not.toBeNull(); expect(caja!.x, "el panel se sale por la izquierda").toBeGreaterThanOrEqual(-1); expect(caja!.x + caja!.width, "el panel se sale por la derecha").toBeLessThanOrEqual(321); const r = await desbordaEnHorizontal(page); expect(r.desborda, "el widget abierto no debe provocar desborde del documento").toBe(false); }); test("las tablas anchas se desplazan dentro de su contenedor", async ({ page }) => { await page.setViewportSize({ width: 375, height: 780 }); await page.goto("/catalogo", { waitUntil: "networkidle" }); const r = await desbordaEnHorizontal(page); expect(r.desborda, "el comparador no debe desplazar el cuerpo de la página").toBe(false); });