import { expect, type Locator, test } from "@playwright/test"; import { DESKTOP, PHONE, PHONE_USE } from "./helpers"; /** * A peça 3D do hero (a marca em blocos): o pôster está no HTML e aparece sem JavaScript; com movimento reduzido a cena * não baixa o modelo nem desenha canvas, e o pôster fica no lugar; no celular, a peça não cobre o título nem o * parágrafo, e os dois botões cabem na primeira tela. */ /** Arquivos da cena 3D: nenhum deles pode ser pedido quando a cena não deve rodar. */ const SCENE_FILES = ["/3d/bloco.glb", "/3d/blocos.json"]; type Box = { x: number; y: number; width: number; height: number }; /** Caixa do elemento na página; falha se ele não tiver caixa (ausente ou com display: none). */ async function boxOf(locator: Locator, what: string): Promise { const box = await locator.boundingBox(); expect(box, `caixa de ${what}`).not.toBeNull(); return box as Box; } function overlaps(a: Box, b: Box) { return ( a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height ); } test.describe("sem JavaScript", () => { test.use({ viewport: DESKTOP, javaScriptEnabled: false }); test("the home shows the headline and ships the block poster in the HTML", async ({ page, }) => { const response = await page.goto("/", { waitUntil: "load" }); expect(response?.status()).toBe(200); await expect(page.getByRole("heading", { level: 1 })).toBeVisible(); const poster = page.locator(".hero-piece img"); await expect(poster).toHaveCount(1); await expect(poster).toHaveAttribute("src", /hero-poster/); }); }); test.describe("movimento reduzido no computador", () => { test.use({ viewport: DESKTOP, reducedMotion: "reduce" }); test("never fetches the 3D scene nor draws a canvas, and keeps the poster visible", async ({ page, }) => { const requested: string[] = []; page.on("request", (request) => requested.push(request.url())); await page.goto("/", { waitUntil: "networkidle" }); await page.mouse.move(DESKTOP.width / 2, DESKTOP.height / 2); await page.mouse.wheel(0, 600); await expect .poll(() => page.evaluate(() => window.scrollY)) .toBeGreaterThan(0); // provar uma ausência pede uma janela de espera: a rolagem poderia disparar o carregamento da cena await page.waitForLoadState("networkidle"); await page.waitForTimeout(1_000); const sceneRequests = requested.filter((url) => SCENE_FILES.some((file) => url.includes(file)), ); expect(sceneRequests).toEqual([]); await expect(page.locator(".hero canvas")).toHaveCount(0); await expect(page.locator(".hero-piece img")).toBeVisible(); }); }); test.describe("celular com movimento reduzido", () => { test.use({ ...PHONE_USE, reducedMotion: "reduce" }); test("keeps both hero buttons whole in the first screen and the piece clear of the headline and its paragraph", async ({ page, }) => { await page.goto("/", { waitUntil: "load" }); const buttons = [ page.locator("#hero-contact-cta"), page.locator('.hero a[href="/cases"]'), ]; for (const button of buttons) { const box = await boxOf(button, "botão do hero"); expect(box.y).toBeGreaterThanOrEqual(0); expect(box.y + box.height).toBeLessThanOrEqual(PHONE.height); } const piece = await boxOf(page.locator(".hero-piece"), "a peça"); const heading = await boxOf(page.locator(".hero h1"), "o título"); const paragraph = await boxOf( page.locator(".hero h1 + p"), "o parágrafo do título", ); expect(overlaps(piece, heading), "a peça cruza o título").toBe(false); expect(overlaps(piece, paragraph), "a peça cruza o parágrafo").toBe(false); }); });