@decocms/nextjs 7.5.1 → 7.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/setup.test.ts +159 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/nextjs",
3
- "version": "7.5.1",
3
+ "version": "7.5.3",
4
4
  "type": "module",
5
5
  "description": "Deco framework binding for Next.js App Router",
6
6
  "repository": {
@@ -25,8 +25,8 @@
25
25
  "lint:unused": "knip"
26
26
  },
27
27
  "dependencies": {
28
- "@decocms/blocks": "7.5.1",
29
- "@decocms/blocks-admin": "7.5.1"
28
+ "@decocms/blocks": "7.5.3",
29
+ "@decocms/blocks-admin": "7.5.3"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "next": ">=15.0.0",
package/src/setup.test.ts CHANGED
@@ -7,13 +7,34 @@
7
7
  // under vitest's "node" environment rather than the package default
8
8
  // (jsdom, used by the component-rendering tests in this same package) to
9
9
  // match that real invocation context.
10
- import { beforeEach, describe, expect, it, vi } from "vitest";
10
+ import * as fs from "node:fs";
11
+ import * as os from "node:os";
12
+ import * as path from "node:path";
13
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
14
+ import * as cms from "@decocms/blocks/cms";
11
15
  import { listRegisteredSections, loadBlocks, setBlocks } from "@decocms/blocks/cms";
16
+ import { getProductionOrigins } from "@decocms/blocks/sdk/normalizeUrls";
17
+
18
+ // Mirrors routeHandlers.test.ts in this same package: @decocms/blocks-admin
19
+ // is mocked (rather than exercised for real) because it's a heavier graph
20
+ // than the CMS core — see this file's `@example` JSDoc note in setup.ts.
21
+ // The mock applies to every test in this file; existing tests that pass
22
+ // `meta` don't assert on setMetaData's *internal* effects (only that the
23
+ // `meta` loader itself was invoked), so swapping the real setter for a
24
+ // stub doesn't change what they verify.
25
+ const adminMocks = vi.hoisted(() => ({
26
+ setMetaData: vi.fn(),
27
+ setRenderShell: vi.fn(),
28
+ setPreviewWrapper: vi.fn(),
29
+ }));
30
+ vi.mock("@decocms/blocks-admin", () => adminMocks);
31
+
12
32
  import { createNextSetup } from "./setup";
13
33
 
14
34
  describe("createNextSetup", () => {
15
35
  beforeEach(() => {
16
36
  setBlocks({});
37
+ vi.clearAllMocks();
17
38
  });
18
39
 
19
40
  it("returns a memoized ensureSetup that registers blocks, sections, meta", async () => {
@@ -77,4 +98,141 @@ describe("createNextSetup", () => {
77
98
  await expect(ensureSetup()).resolves.toBeUndefined();
78
99
  expect(meta).toHaveBeenCalledTimes(2);
79
100
  });
101
+
102
+ describe("blocksDir", () => {
103
+ let tmpDir: string;
104
+
105
+ beforeEach(() => {
106
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "next-setup-blocksdir-"));
107
+ });
108
+
109
+ afterEach(() => {
110
+ fs.rmSync(tmpDir, { recursive: true, force: true });
111
+ });
112
+
113
+ it("loads a real string blocksDir path (tmp dir with one JSON decofile)", async () => {
114
+ fs.writeFileSync(
115
+ path.join(tmpDir, "myBlock.json"),
116
+ JSON.stringify({ __resolveType: "site/sections/Hero.tsx" }),
117
+ );
118
+
119
+ const ensureSetup = createNextSetup({
120
+ blocksDir: tmpDir,
121
+ sections: { "./sections/Hero.tsx": async () => ({ default: () => null }) },
122
+ });
123
+ await ensureSetup();
124
+
125
+ expect(loadBlocks().myBlock).toEqual({
126
+ __resolveType: "site/sections/Hero.tsx",
127
+ });
128
+ });
129
+
130
+ it("options.blocks wins over blocksDir on an overlapping key (merge precedence)", async () => {
131
+ fs.writeFileSync(
132
+ path.join(tmpDir, "shared.json"),
133
+ JSON.stringify({ source: "dir" }),
134
+ );
135
+
136
+ const ensureSetup = createNextSetup({
137
+ blocksDir: tmpDir,
138
+ blocks: { shared: { source: "override" } },
139
+ sections: {},
140
+ });
141
+ await ensureSetup();
142
+
143
+ expect(loadBlocks().shared).toEqual({ source: "override" });
144
+ });
145
+ });
146
+
147
+ it("reaches blocks-admin's setRenderShell, setPreviewWrapper, and setMetaData with the given args", async () => {
148
+ const meta = vi.fn().mockResolvedValue({ schema: { definitions: {}, root: {} } });
149
+ const renderShell = { css: "https://cdn.example.com/admin.css", fonts: ["Inter"] };
150
+ const PreviewWrapper = () => null;
151
+
152
+ const ensureSetup = createNextSetup({
153
+ blocksDir: false,
154
+ sections: {},
155
+ meta,
156
+ renderShell,
157
+ previewWrapper: PreviewWrapper,
158
+ });
159
+ await ensureSetup();
160
+
161
+ expect(adminMocks.setMetaData).toHaveBeenCalledWith(
162
+ await meta.mock.results[0]!.value,
163
+ );
164
+ expect(adminMocks.setRenderShell).toHaveBeenCalledWith(renderShell);
165
+ expect(adminMocks.setPreviewWrapper).toHaveBeenCalledWith(PreviewWrapper);
166
+ });
167
+
168
+ it("does not touch blocks-admin's setters when meta/renderShell/previewWrapper are all omitted", async () => {
169
+ const ensureSetup = createNextSetup({
170
+ blocksDir: false,
171
+ sections: {},
172
+ });
173
+ await ensureSetup();
174
+
175
+ expect(adminMocks.setMetaData).not.toHaveBeenCalled();
176
+ expect(adminMocks.setRenderShell).not.toHaveBeenCalled();
177
+ expect(adminMocks.setPreviewWrapper).not.toHaveBeenCalled();
178
+ });
179
+
180
+ it("passes productionOrigins, customMatchers, onResolveError, and onDanglingReference through to createSiteSetup", async () => {
181
+ const matcher = vi.fn();
182
+ const onResolveError = vi.fn();
183
+ const onDanglingReference = vi.fn();
184
+
185
+ // onResolveError / onDanglingReference are installed via module-scope
186
+ // setters (setResolveErrorHandler / setDanglingReferenceHandler) with no
187
+ // public getter to read the currently-installed handler back — spy on
188
+ // the real setters on the @decocms/blocks/cms module namespace.
189
+ // createSiteSetup (called by createNextSetup) imports these same two
190
+ // names from the identical resolved module ("./cms/index" internally,
191
+ // "@decocms/blocks/cms" here — same file per package.json's export
192
+ // map), and Vitest's SSR module transform makes named exports mutable
193
+ // properties on a shared namespace object, so spying here intercepts
194
+ // the call made from inside createSiteSetup too.
195
+ const resolveErrorSpy = vi.spyOn(cms, "setResolveErrorHandler");
196
+ const danglingRefSpy = vi.spyOn(cms, "setDanglingReferenceHandler");
197
+
198
+ const ensureSetup = createNextSetup({
199
+ blocksDir: false,
200
+ sections: {},
201
+ productionOrigins: ["https://www.example.com"],
202
+ customMatchers: [matcher],
203
+ onResolveError,
204
+ onDanglingReference,
205
+ });
206
+ await ensureSetup();
207
+
208
+ // Cheapest direct observation, per the option's own doc comment: each
209
+ // customMatchers thunk is called exactly once during setup.
210
+ expect(matcher).toHaveBeenCalledTimes(1);
211
+ // productionOrigins has a real, cheap getter — assert the registered
212
+ // value directly rather than only inferring it went through.
213
+ expect(getProductionOrigins()).toEqual(["https://www.example.com"]);
214
+ expect(resolveErrorSpy).toHaveBeenCalledWith(onResolveError);
215
+ expect(danglingRefSpy).toHaveBeenCalledWith(onDanglingReference);
216
+
217
+ resolveErrorSpy.mockRestore();
218
+ danglingRefSpy.mockRestore();
219
+ });
220
+
221
+ it("calls extend with the merged, loaded blocks", async () => {
222
+ const extend = vi.fn();
223
+ const ensureSetup = createNextSetup({
224
+ blocksDir: false,
225
+ blocks: { myBlock: { __resolveType: "site/sections/Hero.tsx" } },
226
+ sections: { "./sections/Hero.tsx": async () => ({ default: () => null }) },
227
+ extend,
228
+ });
229
+ await ensureSetup();
230
+
231
+ expect(extend).toHaveBeenCalledTimes(1);
232
+ expect(extend).toHaveBeenCalledWith(
233
+ expect.objectContaining({
234
+ myBlock: { __resolveType: "site/sections/Hero.tsx" },
235
+ }),
236
+ );
237
+ });
80
238
  });