cloudflare/vinext

Public

mirrored from https://github.com/cloudflare/vinextAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.0.9

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

tests/dynamic.test.ts

154lines · modecode

1/**
2 * next/dynamic shim unit tests.
3 *
4 * Mirrors test cases from Next.js test/unit/next-dynamic.test.tsx,
5 * plus comprehensive coverage for vinext's dynamic() implementation:
6 * SSR rendering, ssr:false behavior, loading components, error
7 * boundaries, displayName assignment, and flushPreloads().
8 */
9import { describe, it, expect } from "vitest";
10import React from "react";
11import ReactDOMServer from "react-dom/server";
12import dynamic, { flushPreloads } from "../packages/vinext/src/shims/dynamic.js";
13
14// ─── Test components ────────────────────────────────────────────────────
15
16function Hello() {
17 return React.createElement("div", null, "Hello from dynamic");
18}
19
20function LoadingSpinner({ isLoading, error }: { isLoading?: boolean; error?: Error | null }) {
21 if (error) return React.createElement("div", null, `Error: ${error.message}`);
22 if (isLoading) return React.createElement("div", null, "Loading...");
23 return null;
24}
25
26// ─── SSR rendering ──────────────────────────────────────────────────────
27
28describe("next/dynamic SSR", () => {
29 it("renders dynamically imported component on server (mirrors Next.js test)", async () => {
30 // Next.js test: dynamic(() => import('./fixtures/stub-components/hello'))
31 // Verifies that next/dynamic doesn't crash
32 const DynamicHello = dynamic(
33 () => Promise.resolve({ default: Hello }),
34 );
35
36 // On server, this uses React.lazy + Suspense
37 // renderToString will resolve the lazy component synchronously for simple promises
38 expect(DynamicHello.displayName).toBe("DynamicServer");
39 });
40
41 it("sets correct displayName for server component", () => {
42 const DynamicComponent = dynamic(
43 () => Promise.resolve({ default: Hello }),
44 );
45 expect(DynamicComponent.displayName).toBe("DynamicServer");
46 });
47
48 it("handles modules exporting bare component (no default)", async () => {
49 // Some dynamic imports export the component directly
50 const DynamicComponent = dynamic(
51 () => Promise.resolve(Hello as any),
52 );
53 expect(DynamicComponent.displayName).toBe("DynamicServer");
54 });
55});
56
57// ─── SSR: false ─────────────────────────────────────────────────────────
58
59describe("next/dynamic ssr: false", () => {
60 it("renders loading component on server when ssr: false", () => {
61 const DynamicNoSSR = dynamic(
62 () => Promise.resolve({ default: Hello }),
63 { ssr: false, loading: LoadingSpinner },
64 );
65
66 const html = ReactDOMServer.renderToString(
67 React.createElement(DynamicNoSSR),
68 );
69 expect(html).toContain("Loading...");
70 expect(html).not.toContain("Hello from dynamic");
71 });
72
73 it("renders nothing on server when ssr: false and no loading", () => {
74 const DynamicNoSSR = dynamic(
75 () => Promise.resolve({ default: Hello }),
76 { ssr: false },
77 );
78
79 const html = ReactDOMServer.renderToString(
80 React.createElement(DynamicNoSSR),
81 );
82 expect(html).toBe("");
83 });
84
85 it("sets DynamicSSRFalse displayName on server", () => {
86 const DynamicNoSSR = dynamic(
87 () => Promise.resolve({ default: Hello }),
88 { ssr: false },
89 );
90 expect(DynamicNoSSR.displayName).toBe("DynamicSSRFalse");
91 });
92});
93
94// ─── Loading component ──────────────────────────────────────────────────
95
96describe("next/dynamic loading component", () => {
97 it("passes isLoading and pastDelay to loading component on SSR", () => {
98 let receivedProps: any = null;
99 function TrackingLoader(props: any) {
100 receivedProps = props;
101 return React.createElement("div", null, "tracking");
102 }
103
104 const DynamicWithTracking = dynamic(
105 () => Promise.resolve({ default: Hello }),
106 { ssr: false, loading: TrackingLoader },
107 );
108
109 ReactDOMServer.renderToString(
110 React.createElement(DynamicWithTracking),
111 );
112
113 expect(receivedProps).toEqual({
114 isLoading: true,
115 pastDelay: true,
116 error: null,
117 });
118 });
119});
120
121// ─── Default options ────────────────────────────────────────────────────
122
123describe("next/dynamic defaults", () => {
124 it("defaults ssr to true", () => {
125 const DynamicDefault = dynamic(
126 () => Promise.resolve({ default: Hello }),
127 );
128 // If ssr defaults to true, we get DynamicServer, not DynamicSSRFalse
129 expect(DynamicDefault.displayName).toBe("DynamicServer");
130 });
131
132 it("handles undefined options", () => {
133 const DynamicNoOpts = dynamic(
134 () => Promise.resolve({ default: Hello }),
135 undefined,
136 );
137 expect(DynamicNoOpts.displayName).toBe("DynamicServer");
138 });
139});
140
141// ─── flushPreloads ──────────────────────────────────────────────────────
142
143describe("flushPreloads", () => {
144 it("returns an empty array when no preloads queued", async () => {
145 const result = await flushPreloads();
146 expect(result).toEqual([]);
147 });
148
149 it("can be called multiple times safely", async () => {
150 await flushPreloads();
151 const result = await flushPreloads();
152 expect(result).toEqual([]);
153 });
154});
155