Align the notebook browser with the physical LCD, refresh real project captures and navigation, simplify service content, and stabilize hero hydration.
This commit is contained in:
1 parent
e2b20bc699
commit
8b12e51bc5
35 files changed
+2359
-1299
No files matched your search
@@ -1,166 +1,59 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { act } from "@testing-library/react";
|
||||
import { useReducedMotion } from "motion/react";
|
||||
import { hydrateRoot, type Root } from "react-dom/client";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import Hero from "./Hero";
|
||||
|
||||
jest.mock("next/dynamic", () => () => {
|
||||
const DynamicComponent = () => <div data-testid="hero-scene" />;
|
||||
DynamicComponent.displayName = "MockHeroScene";
|
||||
return DynamicComponent;
|
||||
});
|
||||
|
||||
jest.mock("motion/react", () => {
|
||||
const React = require("react");
|
||||
const MotionStateContext = React.createContext({
|
||||
initial: undefined,
|
||||
animate: undefined,
|
||||
});
|
||||
|
||||
const resolveOpacity = (
|
||||
value: unknown,
|
||||
variants: Record<string, { opacity?: number }> | undefined,
|
||||
) => {
|
||||
const state: unknown =
|
||||
typeof value === "string" ? variants?.[value] : value;
|
||||
if (
|
||||
state &&
|
||||
typeof state === "object" &&
|
||||
"opacity" in state &&
|
||||
typeof state.opacity === "number"
|
||||
) {
|
||||
return state.opacity;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const motion = new Proxy(
|
||||
{},
|
||||
{
|
||||
get: (_, tag: string) => {
|
||||
const Component = ({
|
||||
children,
|
||||
initial,
|
||||
animate,
|
||||
variants,
|
||||
transition: _transition,
|
||||
whileHover: _whileHover,
|
||||
whileTap: _whileTap,
|
||||
style,
|
||||
...props
|
||||
}: {
|
||||
children?: ReactNode;
|
||||
initial?: unknown;
|
||||
animate?: unknown;
|
||||
variants?: Record<string, { opacity?: number }>;
|
||||
style?: Record<string, unknown>;
|
||||
} & Record<string, unknown>) => {
|
||||
const inherited = React.useContext(MotionStateContext);
|
||||
const effectiveInitial =
|
||||
initial === undefined ? inherited.initial : initial;
|
||||
const effectiveAnimate =
|
||||
animate === undefined ? inherited.animate : animate;
|
||||
const opacity = resolveOpacity(
|
||||
effectiveInitial === false ? effectiveAnimate : effectiveInitial,
|
||||
variants,
|
||||
);
|
||||
const element = React.createElement(
|
||||
tag,
|
||||
{
|
||||
...props,
|
||||
style: opacity === undefined ? style : { ...style, opacity },
|
||||
},
|
||||
children,
|
||||
);
|
||||
|
||||
return React.createElement(
|
||||
MotionStateContext.Provider,
|
||||
{
|
||||
value: {
|
||||
initial: effectiveInitial,
|
||||
animate: effectiveAnimate,
|
||||
},
|
||||
},
|
||||
element,
|
||||
);
|
||||
};
|
||||
Component.displayName = `motion.${tag}`;
|
||||
return Component;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
AnimatePresence: ({ children }: { children: React.ReactNode }) => children,
|
||||
motion,
|
||||
useInView: () => true,
|
||||
useReducedMotion: () => true,
|
||||
useMotionValue: (initial: number) => ({
|
||||
get: () => initial,
|
||||
set: jest.fn(),
|
||||
}),
|
||||
useSpring: (
|
||||
value: { get: () => number; set: (v: number) => void },
|
||||
_config: unknown,
|
||||
) => value,
|
||||
useTransform: (
|
||||
value: { get: () => number; set: (v: number) => void },
|
||||
_input: number[],
|
||||
_output: number[],
|
||||
) => value,
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock("cobe", () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => ({
|
||||
destroy: jest.fn(),
|
||||
})),
|
||||
jest.mock("motion/react", () => ({
|
||||
...jest.requireActual("motion/react"),
|
||||
useReducedMotion: jest.fn(),
|
||||
}));
|
||||
jest.mock("next/dynamic", () => () => () => null);
|
||||
jest.mock("./cinematic/ControlRoomGlobe", () => ({
|
||||
ControlRoomGlobe: () => null,
|
||||
}));
|
||||
jest.mock("./cinematic/HeroStarfield", () => ({
|
||||
HeroStarfield: () => null,
|
||||
}));
|
||||
|
||||
function mockMatchMedia(matches: boolean) {
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches,
|
||||
media: query,
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it.each([false, true])(
|
||||
"hydrates without attribute mismatches when reduced motion resolves to %s",
|
||||
async (reducedMotion) => {
|
||||
jest.spyOn(window, "matchMedia").mockImplementation((media) => ({
|
||||
media,
|
||||
matches: media === "(prefers-reduced-motion: reduce)" && reducedMotion,
|
||||
onchange: null,
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
}
|
||||
}));
|
||||
const preference = jest.mocked(useReducedMotion);
|
||||
preference.mockReturnValue(null);
|
||||
const container = document.createElement("div");
|
||||
container.innerHTML = renderToString(<Hero />);
|
||||
document.body.append(container);
|
||||
|
||||
describe("Hero reduced motion", () => {
|
||||
beforeEach(() => {
|
||||
mockMatchMedia(true);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("shows active copy and actions immediately without animation timers", () => {
|
||||
render(<Hero />);
|
||||
|
||||
const visibleContent = [
|
||||
screen.getByText(/Disponível para novas oportunidades/i),
|
||||
screen.getByRole("heading", {
|
||||
level: 1,
|
||||
name: /engenharia de software para sistemas/i,
|
||||
}),
|
||||
screen.getByRole("button", {
|
||||
name: /conversar sobre uma oportunidade/i,
|
||||
}),
|
||||
screen.getByRole("link", { name: /ver casos selecionados/i }),
|
||||
screen.getByRole("link", { name: /download cv/i }),
|
||||
];
|
||||
|
||||
for (const element of visibleContent) {
|
||||
expect(element).toBeVisible();
|
||||
preference.mockReturnValue(reducedMotion);
|
||||
const consoleErrors = jest.spyOn(console, "error").mockImplementation();
|
||||
const recoverableErrors: unknown[] = [];
|
||||
let root: Root | undefined;
|
||||
try {
|
||||
await act(async () => {
|
||||
root = hydrateRoot(container, <Hero />, {
|
||||
onRecoverableError: (error) => recoverableErrors.push(error),
|
||||
});
|
||||
});
|
||||
expect(recoverableErrors).toEqual([]);
|
||||
expect(consoleErrors.mock.calls).toEqual([]);
|
||||
} finally {
|
||||
await act(async () => root?.unmount());
|
||||
container.remove();
|
||||
}
|
||||
expect(screen.queryByTestId("hero-scene")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user