Files

146 lines
4.3 KiB
TypeScript

import { act, render, screen } from "@testing-library/react";
import type { ReactNode } from "react";
import Projects from "./Projects";
/**
* "Ver os cases" na coluna dos botões da vitrine: na vitrine fixa o conjunto de aparelhos (`.ds-reel-set`, com a legenda
* e os botões no canto esquerdo dele) fica centrado no palco, e esse canto muda com a altura da janela. Projects mede o
* canto e dá ao bloco do link o mesmo recuo; sem vitrine fixa (celular, movimento reduzido) o recuo é zero.
*/
jest.mock("next/link", () => ({
__esModule: true,
default: ({
children,
prefetch: _prefetch,
...props
}: { children?: ReactNode; prefetch?: boolean | null } & Record<
string,
unknown
>) => <a {...props}>{children}</a>,
}));
jest.mock("./Section", () => ({
Section: ({ children, title }: { children: ReactNode; title: string }) => (
<section>
<h2>{title}</h2>
{children}
</section>
),
}));
jest.mock("./showcase/ShowcaseProvider", () => ({
ShowcaseProvider: ({ children }: { children: ReactNode }) => <>{children}</>,
}));
let pinned = true;
jest.mock("./showcase/ProjectReel", () => ({
ProjectReel: () => (pinned ? <div className="ds-reel-set" /> : <ul />),
}));
/** Os cantos esquerdos que a página teria: o do bloco do link e o do conjunto de aparelhos. */
const lefts = { exit: 100, set: 503 };
class FakeResizeObserver {
static instances: FakeResizeObserver[] = [];
observed = new Set<Element>();
constructor(private readonly callback: () => void) {
FakeResizeObserver.instances.push(this);
}
observe(target: Element) {
this.observed.add(target);
}
unobserve(target: Element) {
this.observed.delete(target);
}
disconnect() {
this.observed.clear();
}
fire() {
this.callback();
}
}
const originalRect = Element.prototype.getBoundingClientRect;
const originalObserver = global.ResizeObserver;
beforeEach(() => {
pinned = true;
lefts.exit = 100;
lefts.set = 503;
FakeResizeObserver.instances = [];
global.ResizeObserver =
FakeResizeObserver as unknown as typeof ResizeObserver;
Element.prototype.getBoundingClientRect = function rect(this: Element) {
const left = this.classList.contains("ds-reel-set")
? lefts.set
: this.getAttribute("data-reel-stagger") === "left"
? lefts.exit
: 0;
return {
left,
right: left,
top: 0,
bottom: 0,
width: 0,
height: 0,
} as DOMRect;
};
});
afterEach(() => {
Element.prototype.getBoundingClientRect = originalRect;
global.ResizeObserver = originalObserver;
});
const exitBlock = () =>
screen.getByRole("link", { name: "Ver os cases" })
.parentElement as HTMLElement;
describe("Projects: saída da vitrine", () => {
it("indents the link to the left corner of the pinned reel, where the caption and the buttons start", () => {
render(<Projects projects={[]} />);
expect(exitBlock().style.paddingLeft).toBe("403px");
});
it("follows the reel when the window changes size (the stage measures itself), and when the reel is observed", () => {
render(<Projects projects={[]} />);
expect(exitBlock().style.paddingLeft).toBe("403px");
lefts.set = 567;
act(() => {
for (const observer of FakeResizeObserver.instances) observer.fire();
});
expect(exitBlock().style.paddingLeft).toBe("467px");
// observa a seção (largura da janela) e o conjunto de aparelhos (a barra de cookies muda a altura do palco)
const observed = FakeResizeObserver.instances.flatMap((observer) => [
...observer.observed,
]);
expect(observed.some((element) => element.tagName === "SECTION")).toBe(
true,
);
expect(
observed.some((element) => element.classList.contains("ds-reel-set")),
).toBe(true);
});
it("keeps the link at the start of the column without a pinned reel (phone, reduced motion, short window)", () => {
pinned = false;
render(<Projects projects={[]} />);
expect(exitBlock().style.paddingLeft).toBe("");
});
it("never indents to the left of its own column, and works where there is no ResizeObserver", () => {
lefts.set = 40;
global.ResizeObserver = undefined as unknown as typeof ResizeObserver;
render(<Projects projects={[]} />);
expect(exitBlock().style.paddingLeft).toBe("");
});
});