AppShell.test.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /* @vitest-environment node */
  2. import { describe, it, expect, vi } from "vitest";
  3. import React from "react";
  4. import { renderToString } from "react-dom/server";
  5. /**
  6. * Mock `next/link` so this unit test does not depend on the Next.js runtime.
  7. * For a server-render smoke test, a minimal <a href="..."> replacement is enough.
  8. */
  9. vi.mock("next/link", async () => {
  10. const ReactNs = await import("react");
  11. const React = ReactNs.default ?? ReactNs;
  12. function LinkMock({ href, children, ...props }) {
  13. const resolvedHref =
  14. typeof href === "string"
  15. ? href
  16. : href?.pathname
  17. ? String(href.pathname)
  18. : "";
  19. return React.createElement("a", { href: resolvedHref, ...props }, children);
  20. }
  21. return { default: LinkMock };
  22. });
  23. /**
  24. * Mock `next/image` to avoid Next.js runtime constraints in SSR unit tests
  25. * (e.g. width/height enforcement, internal config dependencies).
  26. */
  27. vi.mock("next/image", async () => {
  28. const ReactNs = await import("react");
  29. const React = ReactNs.default ?? ReactNs;
  30. function ImageMock(props) {
  31. const {
  32. src,
  33. alt,
  34. width,
  35. height,
  36. fill,
  37. priority,
  38. quality,
  39. loader,
  40. blurDataURL,
  41. placeholder,
  42. sizes,
  43. ...rest
  44. } = props || {};
  45. const resolvedSrc =
  46. typeof src === "string" ? src : src?.src ? String(src.src) : "";
  47. const imgProps = {
  48. src: resolvedSrc,
  49. alt: alt || "",
  50. ...rest,
  51. };
  52. // Only attach width/height if present (fill-mode images won't have them).
  53. if (width !== undefined) imgProps.width = width;
  54. if (height !== undefined) imgProps.height = height;
  55. return React.createElement("img", imgProps);
  56. }
  57. return { default: ImageMock };
  58. });
  59. /**
  60. * AppShell renders TopNav which includes QuickNav (client component).
  61. * QuickNav uses next/navigation hooks (useRouter/usePathname).
  62. *
  63. * In a pure react-dom/server smoke test, we must mock these hooks to avoid
  64. * runtime errors and keep this test deterministic.
  65. */
  66. vi.mock("next/navigation", () => {
  67. return {
  68. usePathname: () => "/",
  69. useRouter: () => ({
  70. push: () => {},
  71. replace: () => {},
  72. refresh: () => {},
  73. prefetch: () => {},
  74. }),
  75. };
  76. });
  77. // Import AFTER the mocks so the module under test uses the mocked Next modules.
  78. import AppShell from "./AppShell";
  79. describe("components/app-shell/AppShell", () => {
  80. it("renders the shell without crashing and includes key areas", () => {
  81. const element = React.createElement(
  82. AppShell,
  83. null,
  84. React.createElement("div", null, "Child content"),
  85. );
  86. const html = renderToString(element);
  87. // Sidebar placeholder heading (German)
  88. expect(html).toContain("Seitenleiste");
  89. // Rendered children
  90. expect(html).toContain("Child content");
  91. });
  92. });