Projetos sobem sobre o palco do hero e o celular tem hero de uma tela sem mergulho; passagens com animation-timeline em CSS, sem GSAP nem motion; troca da vitrine sem texto duplicado; moldura de celular só com pôster e scrub reencodado em 960 px (9,4 para 5,2 MB); 3D monta na primeira interação e para quando parado; transição entre páginas do mesmo segmento; foco em anel duplo; texto de 15 px no celular; código morto removido.
108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
import { act, render, screen } from "@testing-library/react";
|
|
import { LazyRevealImage } from "./LazyRevealImage";
|
|
|
|
/**
|
|
* Testes de caminhos-base de LazyRevealImage (complemento de Reveal.test.tsx): a imagem que ainda carrega, o desenho de baixa resolução só para
|
|
* imagem importada e os valores padrão de `sizes` e da classe da imagem.
|
|
*/
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
function mockComplete(complete: boolean, naturalWidth: number) {
|
|
jest
|
|
.spyOn(HTMLImageElement.prototype, "complete", "get")
|
|
.mockReturnValue(complete);
|
|
jest
|
|
.spyOn(HTMLImageElement.prototype, "naturalWidth", "get")
|
|
.mockReturnValue(naturalWidth);
|
|
}
|
|
|
|
async function renderImage(ui: React.ReactElement) {
|
|
let view: ReturnType<typeof render> | undefined;
|
|
await act(async () => {
|
|
view = render(ui);
|
|
});
|
|
if (!view) throw new Error("render failed");
|
|
return view;
|
|
}
|
|
|
|
describe("LazyRevealImage", () => {
|
|
it("path: not complete yet -> waits for the load event, then marks it", async () => {
|
|
mockComplete(false, 0);
|
|
|
|
const { container } = await renderImage(
|
|
<LazyRevealImage src="/foto.webp" alt="Foto" />,
|
|
);
|
|
const wrapper = container.firstElementChild as HTMLElement;
|
|
expect(wrapper).not.toHaveAttribute("data-loaded");
|
|
|
|
await act(async () => {
|
|
screen.getByAltText("Foto").dispatchEvent(new Event("load"));
|
|
});
|
|
|
|
expect(wrapper).toHaveAttribute("data-loaded");
|
|
});
|
|
|
|
it("path: defaults -> lazy loading, the half-width size hint and the cover class on the image", async () => {
|
|
const { container } = await renderImage(
|
|
<LazyRevealImage src="/foto.webp" alt="Foto" />,
|
|
);
|
|
|
|
const image = screen.getByAltText("Foto");
|
|
expect(image).toHaveAttribute("loading", "lazy");
|
|
expect(image).toHaveAttribute("sizes", "(max-width: 1024px) 100vw, 50vw");
|
|
expect(image).toHaveClass("object-cover");
|
|
expect(container.firstElementChild?.className).toBe(
|
|
"ds-lazy-reveal relative",
|
|
);
|
|
});
|
|
|
|
it("path: custom sizes and image class -> used instead of the defaults", async () => {
|
|
await renderImage(
|
|
<LazyRevealImage
|
|
src="/foto.webp"
|
|
alt="Foto"
|
|
sizes="320px"
|
|
imageClassName="object-contain"
|
|
className="h-40"
|
|
/>,
|
|
);
|
|
|
|
const image = screen.getByAltText("Foto");
|
|
expect(image).toHaveAttribute("sizes", "320px");
|
|
expect(image).toHaveClass("object-contain");
|
|
expect(image).not.toHaveClass("object-cover");
|
|
});
|
|
|
|
it("path: an imported image (with a blur) -> the low-resolution preview style is on the image until it loads", async () => {
|
|
mockComplete(false, 0);
|
|
await renderImage(
|
|
<LazyRevealImage
|
|
src={{
|
|
src: "/importada.png",
|
|
width: 40,
|
|
height: 20,
|
|
blurDataURL: "data:image/png;base64,AAAA",
|
|
}}
|
|
alt="Importada"
|
|
/>,
|
|
);
|
|
|
|
// o jsdom descarta o `background-image` do SVG que o next/image monta, mas o restante do estilo do desfoque fica
|
|
expect(screen.getByAltText("Importada").getAttribute("style")).toContain(
|
|
"background-size: cover",
|
|
);
|
|
});
|
|
|
|
it("path: a path string -> no preview style on the image", async () => {
|
|
mockComplete(false, 0);
|
|
await renderImage(<LazyRevealImage src="/foto.webp" alt="Foto" />);
|
|
|
|
expect(
|
|
screen.getByAltText("Foto").getAttribute("style") ?? "",
|
|
).not.toContain("background-size");
|
|
});
|
|
});
|