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.
107 lines
3.1 KiB
TypeScript
107 lines
3.1 KiB
TypeScript
import "@testing-library/jest-dom";
|
|
|
|
if (typeof global.ResizeObserver === "undefined") {
|
|
class ResizeObserver {
|
|
observe() {}
|
|
unobserve() {}
|
|
disconnect() {}
|
|
}
|
|
global.ResizeObserver = ResizeObserver as typeof global.ResizeObserver;
|
|
}
|
|
|
|
// Mock IntersectionObserver if not available in JSDOM
|
|
if (typeof global.IntersectionObserver === "undefined") {
|
|
class IntersectionObserver {
|
|
root: Element | Document | null = null;
|
|
rootMargin: string = "";
|
|
thresholds: ReadonlyArray<number> = [];
|
|
private callback: IntersectionObserverCallback;
|
|
private elements: Set<Element> = new Set();
|
|
|
|
constructor(
|
|
callback: IntersectionObserverCallback,
|
|
options?: IntersectionObserverInit,
|
|
) {
|
|
this.callback = callback;
|
|
this.root = options?.root || null;
|
|
this.rootMargin = options?.rootMargin || "";
|
|
const threshold = options?.threshold ?? 0;
|
|
this.thresholds = Array.isArray(threshold) ? threshold : [threshold];
|
|
}
|
|
|
|
observe(target: Element) {
|
|
this.elements.add(target);
|
|
setTimeout(() => {
|
|
this.callback(
|
|
[
|
|
{
|
|
target,
|
|
isIntersecting: true,
|
|
intersectionRatio: 1,
|
|
boundingClientRect: target.getBoundingClientRect(),
|
|
intersectionRect: target.getBoundingClientRect(),
|
|
rootBounds:
|
|
this.root instanceof Element
|
|
? this.root.getBoundingClientRect()
|
|
: null,
|
|
time: Date.now(),
|
|
} as IntersectionObserverEntry,
|
|
],
|
|
this,
|
|
);
|
|
}, 0);
|
|
}
|
|
|
|
unobserve(target: Element) {
|
|
this.elements.delete(target);
|
|
}
|
|
|
|
disconnect() {
|
|
this.elements.clear();
|
|
}
|
|
|
|
takeRecords(): IntersectionObserverEntry[] {
|
|
return [];
|
|
}
|
|
}
|
|
global.IntersectionObserver =
|
|
IntersectionObserver as typeof global.IntersectionObserver;
|
|
}
|
|
|
|
// Mock PointerEvent if not available in JSDOM
|
|
if (typeof global.PointerEvent === "undefined") {
|
|
class PointerEvent extends MouseEvent {
|
|
pointerId: number;
|
|
pointerType: string;
|
|
isPrimary: boolean;
|
|
constructor(type: string, params: PointerEventInit = {}) {
|
|
super(type, params);
|
|
this.pointerId = params.pointerId || 0;
|
|
this.pointerType = params.pointerType || "mouse";
|
|
this.isPrimary = params.isPrimary || false;
|
|
}
|
|
}
|
|
global.PointerEvent = PointerEvent as typeof global.PointerEvent;
|
|
}
|
|
|
|
// Mock window.matchMedia
|
|
Object.defineProperty(window, "matchMedia", {
|
|
writable: true,
|
|
value: jest.fn().mockImplementation((query) => ({
|
|
matches: false,
|
|
media: query,
|
|
onchange: null,
|
|
addListener: jest.fn(),
|
|
removeListener: jest.fn(),
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
dispatchEvent: jest.fn(),
|
|
})),
|
|
});
|
|
|
|
// O React estável do Jest não exporta <ViewTransition> (só o canary do Next): aqui ele só repassa os filhos.
|
|
// Testes que conferem as props dele trocam este dublê pelo seu (jest.mock("react")).
|
|
const actualReact = jest.requireActual("react");
|
|
actualReact.ViewTransition ??= ({ children }: { children?: unknown }) =>
|
|
children;
|