Files
Moh e2e06be86e test: add comprehensive automated test coverage
- Vitest multi-project setup (unit / integration / component)
- Real-Postgres integration harness via in-process PGlite (TEST_DATABASE_URL
  override), migrations applied per worker; production code untouched
- Unit: routing, locale, validation/Zod schemas, metadata, mail, site-theme,
  marquee, media, portfolio helpers, plus architecture-rule tests
- Integration: Prisma data layer, API routes, and all server actions
- Component: UI primitives and form components (jsdom + Testing Library)
- 371 tests passing
2026-08-06 02:27:20 +02:00

134 lines
4.2 KiB
TypeScript

import { cleanup } from "@testing-library/react";
import { afterEach, vi } from "vitest";
import "@testing-library/jest-dom/vitest";
import React from "react";
afterEach(() => {
cleanup();
});
// ---------------------------------------------------------------------------
// Global mocks for heavy / browser-only libraries so components render in jsdom.
// (vi.mock in a setup file applies to every test file in the component project.)
// ---------------------------------------------------------------------------
vi.mock("framer-motion", () => {
const passthrough = (tag: string) =>
React.forwardRef(function MotionMock(
{ children, ...props }: Record<string, unknown> & { children?: React.ReactNode },
ref: React.Ref<unknown>,
) {
const domProps: Record<string, unknown> = {};
for (const [key, value] of Object.entries(props)) {
// Drop motion-only props that would warn as unknown DOM attributes.
if (
/^(initial|animate|exit|transition|variants|whileHover|whileTap|whileInView|whileFocus|whileDrag|drag|layout|layoutId|viewport|custom|onAnimationComplete|style)$/.test(
key,
)
) {
if (key === "style") domProps.style = value;
continue;
}
domProps[key] = value;
}
return React.createElement(tag, { ...domProps, ref }, children as React.ReactNode);
});
const motion = new Proxy(
{},
{
get: (_target, tag: string) => passthrough(tag),
},
);
return {
motion,
AnimatePresence: ({ children }: { children?: React.ReactNode }) =>
React.createElement(React.Fragment, null, children),
useReducedMotion: () => true,
useInView: () => true,
useScroll: () => ({ scrollYProgress: { on: () => () => {}, get: () => 0 } }),
useTransform: () => 0,
useMotionValue: (value: unknown) => ({ get: () => value, set: () => {}, on: () => () => {} }),
useAnimate: () => [React.useRef(null), () => Promise.resolve()],
};
});
vi.mock("gsap", () => {
const tween = { kill: () => {}, play: () => {}, pause: () => {}, progress: () => {} };
const gsap = {
to: () => tween,
from: () => tween,
fromTo: () => tween,
set: () => tween,
timeline: () => ({
to: () => ({}),
from: () => ({}),
fromTo: () => ({}),
add: () => ({}),
kill: () => {},
}),
registerPlugin: () => {},
context: (fn: () => void) => {
if (typeof fn === "function") fn();
return { revert: () => {}, kill: () => {} };
},
matchMedia: () => ({ add: () => {}, revert: () => {} }),
utils: { toArray: (v: unknown) => (Array.isArray(v) ? v : [v]) },
};
return { gsap, default: gsap };
});
vi.mock("@gsap/react", () => ({
useGSAP: () => ({ context: {}, contextSafe: (fn: unknown) => fn }),
}));
vi.mock("next/link", () => ({
default: React.forwardRef(function LinkMock(
{ href, children, ...props }: Record<string, unknown> & { href?: unknown; children?: React.ReactNode },
ref: React.Ref<HTMLAnchorElement>,
) {
return React.createElement("a", { href: String(href ?? ""), ref, ...props }, children as React.ReactNode);
}),
}));
vi.mock("next/image", () => ({
default: ({ src, alt, ...props }: Record<string, unknown> & { src?: unknown; alt?: string }) =>
React.createElement("img", { src: String(src ?? ""), alt: alt ?? "", ...props }),
}));
vi.mock("next/navigation", () => ({
usePathname: () => "/",
useRouter: () => ({
push: vi.fn(),
replace: vi.fn(),
refresh: vi.fn(),
back: vi.fn(),
forward: vi.fn(),
prefetch: vi.fn(),
}),
useSearchParams: () => new URLSearchParams(),
useParams: () => ({}),
redirect: vi.fn(),
notFound: vi.fn(),
}));
vi.mock("next-intl", () => ({
useTranslations: () => {
const t = (key: string) => key;
t.rich = (key: string) => key;
t.markup = (key: string) => key;
t.raw = (key: string) => key;
return t;
},
useLocale: () => "de",
useFormatter: () => ({
dateTime: (v: Date) => v.toISOString(),
number: (v: number) => String(v),
relativeTime: (v: unknown) => String(v),
}),
useMessages: () => ({}),
NextIntlClientProvider: ({ children }: { children?: React.ReactNode }) =>
React.createElement(React.Fragment, null, children),
}));