Hero e movimento - cena 3D em tempo real com os blocos da marca (kit feito no Blender, 3d/blocos.py): marca, tela, integrações e dados; segue a rolagem, tem vida em repouso, reage ao cursor e, parada no topo, passa sozinha pelos estados com legenda - três níveis de aparelho (full, lite, static); sem WebGL fica o pôster - título novo: "Sites e sistemas sob medida para o jeito que a sua empresa trabalha." - faixa de clientes com o ramo, linha do primeiro passo, hero alinhado às seções - rótulos digitados, entrada dos blocos, cortina de blocos entre páginas e capa compartilhada entre a lista de cases e o case Ofertas - atendimento no WhatsApp com IA e nota fiscal para a reforma tributária (páginas de serviço, cartões da home, formulário e aviso com data de validade) - página /ia atualizada, com fontes - peças 3D em loop nos cartões e topos de página (3d/pecas.py) Design - hover por papel (bloco nos botões, barra nos links), rótulos sem caixa alta - um destino por rótulo: "Falar sobre meu projeto" leva ao formulário, "Chamar no WhatsApp" abre o WhatsApp - logo montado em blocos, 404 redesenhado, grades sem célula vazia - ícones do PWA e manifesto novos Specs em specs/, contexto de design em DESIGN.md e PRODUCT.md.
95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
import { fireEvent, render, screen } from "@testing-library/react";
|
|
import { getShowcaseAssets } from "@/lib/project-showcase";
|
|
import { CasePlayer } from "./CasePlayer";
|
|
|
|
const title = "SEV Moda Íntima";
|
|
|
|
describe("CasePlayer", () => {
|
|
it("shows the poster and an accessible play button instead of a paused video", () => {
|
|
const { container } = render(
|
|
<div className="relative">
|
|
<CasePlayer
|
|
assets={getShowcaseAssets("sev-modaintima", "desktop")}
|
|
title={title}
|
|
device="desktop"
|
|
/>
|
|
</div>,
|
|
);
|
|
|
|
expect(container.querySelector("video")).toBeNull();
|
|
// a imagem é decorativa: o botão já diz o que o quadro mostra
|
|
const poster = container.querySelector("img");
|
|
expect(poster).toHaveAttribute("alt", "");
|
|
expect(poster?.getAttribute("src")).toContain(
|
|
encodeURIComponent("/videos/sev-modaintima/desktop.webp"),
|
|
);
|
|
const button = screen.getByRole("button", {
|
|
name: `Reproduzir o vídeo do site ${title} no computador`,
|
|
});
|
|
expect(button).toHaveAttribute("type", "button");
|
|
// em repouso o play é escuro, com contorno branco; o limão só vem sob o ponteiro ou no foco, com o círculo maior
|
|
const circle = button.querySelector("[aria-hidden='true']");
|
|
const restClasses = (circle?.className ?? "")
|
|
.split(" ")
|
|
.filter((name) => !name.startsWith("group-"));
|
|
expect(restClasses.join(" ")).not.toContain("--slash");
|
|
expect(circle).toHaveClass(
|
|
"group-hover:scale-108",
|
|
"group-hover:bg-[var(--slash)]",
|
|
"group-focus-visible:scale-108",
|
|
"group-focus-visible:bg-[var(--slash)]",
|
|
);
|
|
});
|
|
|
|
it("mounts the video with its sources only after the click, muted and looping, with focus on it", () => {
|
|
const assets = getShowcaseAssets("lavacao-047", "desktop");
|
|
render(
|
|
<div className="relative">
|
|
<CasePlayer assets={assets} title="Lavação 047" device="desktop" />
|
|
</div>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByRole("button"));
|
|
|
|
const video = screen.getByLabelText(
|
|
"Vídeo do site Lavação 047 no computador",
|
|
);
|
|
expect(video).toHaveFocus();
|
|
expect(video).toHaveAttribute("controls");
|
|
expect(video).toHaveAttribute("poster", assets.poster);
|
|
expect(video).toHaveAttribute("width", String(assets.width));
|
|
expect(video).toHaveAttribute("height", String(assets.height));
|
|
expect((video as HTMLVideoElement).muted).toBe(true);
|
|
expect((video as HTMLVideoElement).loop).toBe(true);
|
|
expect(
|
|
[...video.querySelectorAll("source")].map((source) => [
|
|
source.getAttribute("src"),
|
|
source.getAttribute("type"),
|
|
]),
|
|
).toEqual(assets.sources.map(({ src, type }) => [src, type]));
|
|
expect(screen.queryByRole("button")).toBeNull();
|
|
});
|
|
|
|
it("leaves the poster to the phone frame on mobile and names the device in the labels", () => {
|
|
const { container } = render(
|
|
<div className="relative">
|
|
<CasePlayer
|
|
assets={getShowcaseAssets("sc-plus", "mobile")}
|
|
title="SC Plus Aquicultura"
|
|
device="mobile"
|
|
/>
|
|
</div>,
|
|
);
|
|
|
|
expect(container.querySelector("img")).toBeNull();
|
|
fireEvent.click(
|
|
screen.getByRole("button", {
|
|
name: "Reproduzir o vídeo do site SC Plus Aquicultura no celular",
|
|
}),
|
|
);
|
|
expect(
|
|
screen.getByLabelText("Vídeo do site SC Plus Aquicultura no celular"),
|
|
).toHaveAttribute("poster", "/videos/sc-plus/mobile.webp");
|
|
});
|
|
});
|