|
@@ -5,7 +5,7 @@ import React from "react";
|
|
|
import { renderToString } from "react-dom/server";
|
|
import { renderToString } from "react-dom/server";
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * We mock `next/link` so this unit test does not depend on the Next.js runtime.
|
|
|
|
|
|
|
+ * Mock `next/link` so this unit test does not depend on the Next.js runtime.
|
|
|
* For a server-render smoke test, a minimal <a href="..."> replacement is enough.
|
|
* For a server-render smoke test, a minimal <a href="..."> replacement is enough.
|
|
|
*/
|
|
*/
|
|
|
vi.mock("next/link", async () => {
|
|
vi.mock("next/link", async () => {
|
|
@@ -17,8 +17,8 @@ vi.mock("next/link", async () => {
|
|
|
typeof href === "string"
|
|
typeof href === "string"
|
|
|
? href
|
|
? href
|
|
|
: href?.pathname
|
|
: href?.pathname
|
|
|
- ? String(href.pathname)
|
|
|
|
|
- : "";
|
|
|
|
|
|
|
+ ? String(href.pathname)
|
|
|
|
|
+ : "";
|
|
|
|
|
|
|
|
return React.createElement("a", { href: resolvedHref, ...props }, children);
|
|
return React.createElement("a", { href: resolvedHref, ...props }, children);
|
|
|
}
|
|
}
|
|
@@ -26,6 +26,69 @@ vi.mock("next/link", async () => {
|
|
|
return { default: LinkMock };
|
|
return { default: LinkMock };
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * Mock `next/image` to avoid Next.js runtime constraints in SSR unit tests
|
|
|
|
|
+ * (e.g. width/height enforcement, internal config dependencies).
|
|
|
|
|
+ */
|
|
|
|
|
+vi.mock("next/image", async () => {
|
|
|
|
|
+ const ReactNs = await import("react");
|
|
|
|
|
+ const React = ReactNs.default ?? ReactNs;
|
|
|
|
|
+
|
|
|
|
|
+ function ImageMock(props) {
|
|
|
|
|
+ const {
|
|
|
|
|
+ src,
|
|
|
|
|
+ alt,
|
|
|
|
|
+ width,
|
|
|
|
|
+ height,
|
|
|
|
|
+ fill,
|
|
|
|
|
+ priority,
|
|
|
|
|
+ quality,
|
|
|
|
|
+ loader,
|
|
|
|
|
+ blurDataURL,
|
|
|
|
|
+ placeholder,
|
|
|
|
|
+ sizes,
|
|
|
|
|
+ ...rest
|
|
|
|
|
+ } = props || {};
|
|
|
|
|
+
|
|
|
|
|
+ const resolvedSrc =
|
|
|
|
|
+ typeof src === "string" ? src : src?.src ? String(src.src) : "";
|
|
|
|
|
+
|
|
|
|
|
+ const imgProps = {
|
|
|
|
|
+ src: resolvedSrc,
|
|
|
|
|
+ alt: alt || "",
|
|
|
|
|
+ ...rest,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // Only attach width/height if present (fill-mode images won't have them).
|
|
|
|
|
+ if (width !== undefined) imgProps.width = width;
|
|
|
|
|
+ if (height !== undefined) imgProps.height = height;
|
|
|
|
|
+
|
|
|
|
|
+ return React.createElement("img", imgProps);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return { default: ImageMock };
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * AppShell renders TopNav which includes QuickNav (client component).
|
|
|
|
|
+ * QuickNav uses next/navigation hooks (useRouter/usePathname).
|
|
|
|
|
+ *
|
|
|
|
|
+ * In a pure react-dom/server smoke test, we must mock these hooks to avoid
|
|
|
|
|
+ * runtime errors and keep this test deterministic.
|
|
|
|
|
+ */
|
|
|
|
|
+vi.mock("next/navigation", () => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ usePathname: () => "/",
|
|
|
|
|
+ useRouter: () => ({
|
|
|
|
|
+ push: () => {},
|
|
|
|
|
+ replace: () => {},
|
|
|
|
|
+ refresh: () => {},
|
|
|
|
|
+ prefetch: () => {},
|
|
|
|
|
+ }),
|
|
|
|
|
+ };
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// Import AFTER the mocks so the module under test uses the mocked Next modules.
|
|
|
import AppShell from "./AppShell";
|
|
import AppShell from "./AppShell";
|
|
|
|
|
|
|
|
describe("components/app-shell/AppShell", () => {
|
|
describe("components/app-shell/AppShell", () => {
|
|
@@ -33,14 +96,11 @@ describe("components/app-shell/AppShell", () => {
|
|
|
const element = React.createElement(
|
|
const element = React.createElement(
|
|
|
AppShell,
|
|
AppShell,
|
|
|
null,
|
|
null,
|
|
|
- React.createElement("div", null, "Child content")
|
|
|
|
|
|
|
+ React.createElement("div", null, "Child content"),
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
const html = renderToString(element);
|
|
const html = renderToString(element);
|
|
|
|
|
|
|
|
- // TopNav brand
|
|
|
|
|
- expect(html).toContain("RHL Lieferscheine");
|
|
|
|
|
-
|
|
|
|
|
// Sidebar placeholder heading (German)
|
|
// Sidebar placeholder heading (German)
|
|
|
expect(html).toContain("Seitenleiste");
|
|
expect(html).toContain("Seitenleiste");
|
|
|
|
|
|