feat: harden portfolio and add SEO foundations

This commit is contained in:
Evandro Fonseca Junior committed 2026-09-04 19:25:30 -03:00
1 parent b079afdd73
commit 62bcfbd8fa
121 files changed
+7769 -10469

No files matched your search

+67 -42
View File
@@ -1,18 +1,37 @@
import { render, screen, waitFor } from "@testing-library/react";
import { render, screen } from "@testing-library/react";
import type { ReactNode } from "react";
import Hero from "./Hero";
jest.mock("next/dynamic", () => () => {
const DynamicComponent = ({ variant }: { variant?: string }) => (
<div data-testid="hero-scene" data-variant={variant} />
);
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(
{},
@@ -22,26 +41,47 @@ jest.mock("motion/react", () => {
children,
initial,
animate,
variants,
transition: _transition,
whileHover: _whileHover,
whileTap: _whileTap,
variants: _variants,
transition: _transition,
style,
...props
}: {
children?: ReactNode;
initial?: unknown;
animate?: unknown;
variants?: Record<string, { opacity?: number }>;
style?: Record<string, unknown>;
} & Record<string, unknown>) => {
const dataProps = {
...(initial !== undefined
? { "data-initial": JSON.stringify(initial) }
: {}),
...(animate !== undefined
? { "data-animate": JSON.stringify(animate) }
: {}),
};
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(tag, { ...props, ...dataProps }, children);
return React.createElement(
MotionStateContext.Provider,
{
value: {
initial: effectiveInitial,
animate: effectiveAnimate,
},
},
element,
);
};
Component.displayName = `motion.${tag}`;
return Component;
@@ -96,46 +136,31 @@ function mockMatchMedia(matches: boolean) {
describe("Hero reduced motion", () => {
beforeEach(() => {
mockMatchMedia(true);
jest.spyOn(Math, "random").mockReturnValue(0);
});
afterEach(() => {
jest.restoreAllMocks();
});
it("keeps essential hero content visible with reduced motion enabled", async () => {
it("shows active copy and actions immediately without animation timers", () => {
render(<Hero />);
await waitFor(() =>
expect(screen.queryByTestId("hero-scene")).not.toBeInTheDocument(),
);
expect(
const visibleContent = [
screen.getByText(/Disponível para novas oportunidades/i),
).toBeInTheDocument();
expect(
screen.getByRole("heading", {
level: 1,
name: /engenharia de software para sistemas/i,
}),
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: /conversar sobre uma oportunidade/i }),
).toBeInTheDocument();
});
screen.getByRole("button", {
name: /conversar sobre uma oportunidade/i,
}),
screen.getByRole("link", { name: /ver casos selecionados/i }),
screen.getByRole("link", { name: /download cv/i }),
];
it("keeps motion blocks declarative instead of requiring 3D", async () => {
const { container } = render(<Hero />);
await waitFor(() =>
expect(screen.queryByTestId("hero-scene")).not.toBeInTheDocument(),
);
const rootMotionBlock = container.querySelector(
"div[data-initial][data-animate]",
);
expect(rootMotionBlock).toBeInTheDocument();
expect(rootMotionBlock?.getAttribute("data-initial")).toContain("hidden");
expect(rootMotionBlock?.getAttribute("data-animate")).toContain("visible");
for (const element of visibleContent) {
expect(element).toBeVisible();
}
expect(screen.queryByTestId("hero-scene")).not.toBeInTheDocument();
});
});