@squiz/dxp-cli-next 5.31.0-develop.3 → 5.31.0-develop.5

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 (38) hide show
  1. package/lib/cdp/instance/activate/activate.js +6 -7
  2. package/lib/cdp/instance/activate/activate.spec.js +2 -8
  3. package/lib/cdp/utils.d.ts +2 -1
  4. package/lib/cdp/utils.js +4 -2
  5. package/lib/page/layouts/deploy/deploy.js +43 -8
  6. package/lib/page/layouts/deploy/deploy.spec.js +110 -19
  7. package/lib/page/layouts/dev/dev.js +7 -3
  8. package/lib/page/layouts/dev/dev.spec.js +35 -8
  9. package/lib/page/layouts/validation/index.d.ts +2 -0
  10. package/lib/page/layouts/validation/index.js +5 -1
  11. package/lib/page/layouts/validation/property-consistency.d.ts +7 -0
  12. package/lib/page/layouts/validation/property-consistency.js +92 -0
  13. package/lib/page/layouts/validation/property-consistency.spec.d.ts +1 -0
  14. package/lib/page/layouts/validation/property-consistency.spec.js +305 -0
  15. package/lib/page/layouts/validation/validateLayoutFormat.d.ts +2 -0
  16. package/lib/page/layouts/validation/validateLayoutFormat.js +25 -0
  17. package/lib/page/layouts/validation/validateLayoutFormat.spec.d.ts +1 -0
  18. package/lib/page/layouts/validation/validateLayoutFormat.spec.js +40 -0
  19. package/lib/page/layouts/validation/zone-consistency.d.ts +1 -1
  20. package/lib/page/layouts/validation/zone-consistency.js +10 -9
  21. package/lib/page/layouts/validation/zone-consistency.spec.js +32 -34
  22. package/lib/page/utils/definitions.d.ts +346 -49
  23. package/lib/page/utils/definitions.js +102 -21
  24. package/lib/page/utils/definitions.spec.js +460 -267
  25. package/lib/page/utils/normalize.d.ts +8 -0
  26. package/lib/page/utils/normalize.js +61 -0
  27. package/lib/page/utils/normalize.spec.d.ts +1 -0
  28. package/lib/page/utils/normalize.spec.js +315 -0
  29. package/lib/page/utils/parse-args.d.ts +20 -4
  30. package/lib/page/utils/parse-args.js +48 -13
  31. package/lib/page/utils/parse-args.spec.js +159 -21
  32. package/lib/page/utils/render.d.ts +27 -9
  33. package/lib/page/utils/render.js +66 -12
  34. package/lib/page/utils/render.spec.js +14 -14
  35. package/lib/page/utils/server.d.ts +1 -1
  36. package/lib/page/utils/server.js +2 -2
  37. package/lib/page/utils/server.spec.js +13 -13
  38. package/package.json +1 -1
@@ -37,8 +37,60 @@ const definitions_1 = require("./definitions");
37
37
  const validation_1 = require("../templates/validation");
38
38
  jest.mock('node:fs/promises');
39
39
  describe('loadLayoutDefinition', () => {
40
- const paintLayoutFileYaml = './some-dir/page-layout.yaml';
41
- const paintLayoutFileJson = './some-dir/page-layout.json';
40
+ const manifestJson = './some-dir/manifest.json';
41
+ const pageLayoutYaml = './some-dir/page-layout.yaml';
42
+ const jsonContent = JSON.stringify({
43
+ name: 'test-layout',
44
+ displayName: 'Test Layout',
45
+ description: 'A test layout',
46
+ zones: [
47
+ {
48
+ key: 'main',
49
+ displayName: 'Main Zone',
50
+ description: 'Main content area',
51
+ },
52
+ ],
53
+ properties: {
54
+ color: {
55
+ title: 'Color',
56
+ description: 'Color options',
57
+ type: 'string',
58
+ enum: ['red', 'blue'],
59
+ },
60
+ showHeader: {
61
+ title: 'Show Header',
62
+ description: 'Toggle header visibility',
63
+ type: 'boolean',
64
+ },
65
+ customTitle: {
66
+ title: 'Custom Title',
67
+ description: 'Enter a custom title',
68
+ type: 'string',
69
+ },
70
+ },
71
+ entry: 'template.hbs',
72
+ });
73
+ // missing "name" field
74
+ const jsonContentInvalidLayout = JSON.stringify({
75
+ displayName: 'Test Layout',
76
+ description: 'A test layout',
77
+ zones: [
78
+ {
79
+ key: 'main',
80
+ displayName: 'Main Zone',
81
+ description: 'Main content area',
82
+ },
83
+ ],
84
+ properties: {
85
+ color: {
86
+ title: 'Color',
87
+ description: 'Color options',
88
+ type: 'string',
89
+ enum: ['red', 'blue'],
90
+ },
91
+ },
92
+ entry: 'template.hbs',
93
+ });
42
94
  const yamlContent = `
43
95
  name: test-layout
44
96
  displayName: Test Layout
@@ -71,145 +123,56 @@ options:
71
123
  values: ['red', 'blue']
72
124
  entry: template.hbs
73
125
  `;
74
- const jsonContent = JSON.stringify({
75
- name: 'test-layout',
76
- displayName: 'Test Layout',
77
- description: 'A test layout',
78
- zones: {
79
- main: {
80
- displayName: 'Main Zone',
81
- description: 'Main content area',
82
- minNodes: 1,
83
- },
84
- },
85
- options: {
86
- color: {
87
- displayName: 'Color',
88
- description: 'Color options',
89
- values: ['red', 'blue'],
90
- },
91
- },
92
- entry: 'template.hbs',
93
- });
94
- // missing "name" field
95
- const jsonContentInvalidLayout = JSON.stringify({
96
- displayName: 'Test Layout',
97
- description: 'A test layout',
98
- zones: {
99
- main: {
100
- displayName: 'Main Zone',
101
- description: 'Main content area',
102
- minNodes: 1,
103
- },
104
- },
105
- options: {
106
- color: {
107
- displayName: 'Color',
108
- description: 'Color options',
109
- values: ['red', 'blue'],
110
- },
111
- },
112
- entry: 'template.hbs',
113
- });
114
126
  const templateContent = '<div>{{content}}</div>';
115
127
  beforeEach(() => {
116
128
  jest.resetAllMocks();
117
129
  });
118
- it('should load layout definition from YAML file', () => __awaiter(void 0, void 0, void 0, function* () {
130
+ it('should load layout definition from manifest JSON file', () => __awaiter(void 0, void 0, void 0, function* () {
119
131
  fs.readFile.mockImplementation((filePath) => {
120
- if (filePath.endsWith('page-layout.yaml')) {
121
- return yamlContent;
132
+ if (filePath.endsWith('manifest.json')) {
133
+ return jsonContent;
122
134
  }
123
135
  if (filePath.endsWith('template.hbs')) {
124
136
  return templateContent;
125
137
  }
126
138
  throw new Error('File not found');
127
139
  });
128
- const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml);
140
+ const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(manifestJson);
129
141
  expect(layoutDefinition).toEqual({
130
142
  name: 'test-layout',
131
143
  displayName: 'Test Layout',
132
144
  description: 'A test layout',
133
- zones: {
134
- main: {
145
+ zones: [
146
+ {
147
+ key: 'main',
135
148
  displayName: 'Main Zone',
136
149
  description: 'Main content area',
137
- minNodes: 1,
138
150
  },
139
- },
140
- options: {
151
+ ],
152
+ properties: {
141
153
  color: {
142
- displayName: 'Color',
154
+ title: 'Color',
143
155
  description: 'Color options',
144
- values: ['red', 'blue'],
156
+ type: 'string',
157
+ enum: ['red', 'blue'],
145
158
  },
146
- },
147
- template: templateContent,
148
- });
149
- }));
150
- it('should load layout definition from JSON file', () => __awaiter(void 0, void 0, void 0, function* () {
151
- fs.readFile.mockImplementation((filePath) => {
152
- if (filePath.endsWith('page-layout.yaml')) {
153
- throw { code: 'ENOENT' };
154
- }
155
- if (filePath.endsWith('page-layout.json')) {
156
- return jsonContent;
157
- }
158
- if (filePath.endsWith('template.hbs')) {
159
- return templateContent;
160
- }
161
- throw new Error('File not found');
162
- });
163
- const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson);
164
- expect(layoutDefinition).toEqual({
165
- name: 'test-layout',
166
- displayName: 'Test Layout',
167
- description: 'A test layout',
168
- zones: {
169
- main: {
170
- displayName: 'Main Zone',
171
- description: 'Main content area',
172
- minNodes: 1,
159
+ showHeader: {
160
+ title: 'Show Header',
161
+ description: 'Toggle header visibility',
162
+ type: 'boolean',
173
163
  },
174
- },
175
- options: {
176
- color: {
177
- displayName: 'Color',
178
- description: 'Color options',
179
- values: ['red', 'blue'],
164
+ customTitle: {
165
+ title: 'Custom Title',
166
+ description: 'Enter a custom title',
167
+ type: 'string',
180
168
  },
181
169
  },
182
170
  template: templateContent,
183
171
  });
184
172
  }));
185
- it('should throw validation error if invalid data for YAML', () => __awaiter(void 0, void 0, void 0, function* () {
173
+ it('should throw validation error if invalid data for manifest JSON', () => __awaiter(void 0, void 0, void 0, function* () {
186
174
  fs.readFile.mockImplementation((filePath) => {
187
- if (filePath.endsWith('page-layout.yaml')) {
188
- return yamlContentInvalidLayout;
189
- }
190
- if (filePath.endsWith('template.hbs')) {
191
- return templateContent;
192
- }
193
- throw new Error('File not found');
194
- });
195
- yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects
196
- .toThrowErrorMatchingInlineSnapshot(`
197
- "Failed loading layout definition: Failed to parse ./some-dir/page-layout.yaml: [
198
- {
199
- "code": "invalid_type",
200
- "expected": "string",
201
- "received": "undefined",
202
- "path": [
203
- "name"
204
- ],
205
- "message": "Required"
206
- }
207
- ]"
208
- `);
209
- }));
210
- it('should throw validation error if invalid data for JSON', () => __awaiter(void 0, void 0, void 0, function* () {
211
- fs.readFile.mockImplementation((filePath) => {
212
- if (filePath.endsWith('page-layout.json')) {
175
+ if (filePath.endsWith('manifest.json')) {
213
176
  return jsonContentInvalidLayout;
214
177
  }
215
178
  if (filePath.endsWith('template.hbs')) {
@@ -217,49 +180,17 @@ entry: template.hbs
217
180
  }
218
181
  throw new Error('File not found');
219
182
  });
220
- yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson)).rejects
221
- .toThrowErrorMatchingInlineSnapshot(`
222
- "Failed loading layout definition: Failed to parse ./some-dir/page-layout.json: [
223
- {
224
- "code": "invalid_type",
225
- "expected": "string",
226
- "received": "undefined",
227
- "path": [
228
- "name"
229
- ],
230
- "message": "Required"
231
- }
232
- ]"
233
- `);
234
- }));
235
- it('should throw an error if YAML layout file has invalid YAML', () => __awaiter(void 0, void 0, void 0, function* () {
236
- fs.readFile.mockImplementation((filePath) => {
237
- if (filePath.endsWith('page-layout.yaml')) {
238
- return 'name: invalid yaml, entry: template.hbs';
239
- }
240
- if (filePath.endsWith('page-layout.json')) {
241
- return jsonContent;
242
- }
243
- if (filePath.endsWith('template.hbs')) {
244
- return templateContent;
245
- }
246
- throw new Error('File not found');
247
- });
248
- yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects
183
+ yield expect((0, definitions_1.loadLayoutDefinition)(manifestJson)).rejects
249
184
  .toThrowErrorMatchingInlineSnapshot(`
250
- "Failed loading layout definition: Failed to parse ./some-dir/page-layout.yaml: Nested mappings are not allowed in compact mappings at line 1, column 7:
185
+ "Failed loading layout definition: Failed to parse ./some-dir/manifest.json:
251
186
 
252
- name: invalid yaml, entry: template.hbs
253
- ^
254
- "
187
+ Schema validation failed:
188
+ - Required at "name""
255
189
  `);
256
190
  }));
257
- it('should throw an error if JSON layout file has invalid JSON', () => __awaiter(void 0, void 0, void 0, function* () {
191
+ it('should throw an error if JSON layout file has invalid manifest JSON', () => __awaiter(void 0, void 0, void 0, function* () {
258
192
  fs.readFile.mockImplementation((filePath) => {
259
- if (filePath.endsWith('page-layout.yaml')) {
260
- throw { code: 'ENOENT' };
261
- }
262
- if (filePath.endsWith('page-layout.json')) {
193
+ if (filePath.endsWith('manifest.json')) {
263
194
  return '{invalid: json';
264
195
  }
265
196
  if (filePath.endsWith('template.hbs')) {
@@ -267,9 +198,9 @@ entry: template.hbs
267
198
  }
268
199
  throw new Error('File not found');
269
200
  });
270
- yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson)).rejects
201
+ yield expect((0, definitions_1.loadLayoutDefinition)(manifestJson)).rejects
271
202
  .toThrowErrorMatchingInlineSnapshot(`
272
- "Failed loading layout definition: Failed to parse ./some-dir/page-layout.json: Flow map must end with a } at line 1, column 15:
203
+ "Failed loading layout definition: Failed to parse ./some-dir/manifest.json: Flow map must end with a } at line 1, column 15:
273
204
 
274
205
  {invalid: json
275
206
  ^
@@ -277,122 +208,38 @@ entry: template.hbs
277
208
  `);
278
209
  }));
279
210
  it('should throw an error if layout file do not have valid extension', () => __awaiter(void 0, void 0, void 0, function* () {
280
- yield expect((0, definitions_1.loadLayoutDefinition)('/foo/paint-layout.html')).rejects.toThrow('Layout file must have a valid extension: [yaml|json]');
211
+ yield expect((0, definitions_1.loadLayoutDefinition)('/foo/manifest.html')).rejects.toThrow('Layout file must have a valid extension: [yaml|json]');
281
212
  }));
282
213
  it('should throw an error if template file is not found', () => __awaiter(void 0, void 0, void 0, function* () {
283
214
  fs.readFile.mockImplementation((filePath) => {
284
- if (filePath.endsWith('page-layout.yaml')) {
285
- return yamlContent;
215
+ if (filePath.endsWith('manifest.json')) {
216
+ return jsonContent;
286
217
  }
287
218
  throw Error('File not found');
288
219
  });
289
- yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects.toThrow('Failed loading layout definition: Failed loading template file "template.hbs": File not found');
220
+ yield expect((0, definitions_1.loadLayoutDefinition)(manifestJson)).rejects.toThrow('Failed loading layout definition: Failed loading template file "template.hbs": File not found');
290
221
  }));
291
- it('should load layout definition without options field from YAML file', () => __awaiter(void 0, void 0, void 0, function* () {
292
- const yamlContentWithoutOptions = `
293
- name: test-layout
294
- displayName: Test Layout
295
- description: A test layout
296
- zones:
297
- main:
298
- displayName: Main Zone
299
- description: Main content area
300
- minNodes: 1
301
- entry: template.hbs
302
- `;
303
- fs.readFile.mockImplementation((filePath) => {
304
- if (filePath.endsWith('page-layout.yaml')) {
305
- return yamlContentWithoutOptions;
306
- }
307
- if (filePath.endsWith('template.hbs')) {
308
- return templateContent;
309
- }
310
- throw new Error('File not found');
311
- });
312
- const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml);
313
- expect(layoutDefinition).toEqual({
314
- name: 'test-layout',
315
- displayName: 'Test Layout',
316
- description: 'A test layout',
317
- zones: {
318
- main: {
319
- displayName: 'Main Zone',
320
- description: 'Main content area',
321
- minNodes: 1,
322
- },
323
- },
324
- template: templateContent,
325
- });
326
- }));
327
- it('should load layout definition without maxNodes in zones from YAML file', () => __awaiter(void 0, void 0, void 0, function* () {
328
- const yamlContentWithoutMaxNodes = `
329
- name: test-layout
330
- displayName: Test Layout
331
- description: A test layout
332
- zones:
333
- main:
334
- displayName: Main Zone
335
- description: Main content area
336
- minNodes: 1
337
- sidebar:
338
- displayName: Sidebar Zone
339
- description: Sidebar content area
340
- minNodes: 0
341
- entry: template.hbs
342
- `;
343
- fs.readFile.mockImplementation((filePath) => {
344
- if (filePath.endsWith('page-layout.yaml')) {
345
- return yamlContentWithoutMaxNodes;
346
- }
347
- if (filePath.endsWith('template.hbs')) {
348
- return templateContent;
349
- }
350
- throw new Error('File not found');
351
- });
352
- const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml);
353
- expect(layoutDefinition).toEqual({
354
- name: 'test-layout',
355
- displayName: 'Test Layout',
356
- description: 'A test layout',
357
- zones: {
358
- main: {
359
- displayName: 'Main Zone',
360
- description: 'Main content area',
361
- minNodes: 1,
362
- },
363
- sidebar: {
364
- displayName: 'Sidebar Zone',
365
- description: 'Sidebar content area',
366
- minNodes: 0,
367
- },
368
- },
369
- template: templateContent,
370
- });
371
- }));
372
- it('should load layout definition without options and maxNodes from JSON file', () => __awaiter(void 0, void 0, void 0, function* () {
222
+ it('should load layout definition without options and maxNodes from manifest JSON file', () => __awaiter(void 0, void 0, void 0, function* () {
373
223
  const jsonContentWithoutOptionsAndMaxNodes = JSON.stringify({
374
224
  name: 'test-layout',
375
225
  displayName: 'Test Layout',
376
226
  description: 'A test layout',
377
- zones: {
378
- main: {
227
+ zones: [
228
+ {
229
+ key: 'main',
379
230
  displayName: 'Main Zone',
380
231
  description: 'Main content area',
381
- minNodes: 1,
382
232
  },
383
- sidebar: {
233
+ {
234
+ key: 'sidebar',
384
235
  displayName: 'Sidebar Zone',
385
236
  description: 'Sidebar content area',
386
- minNodes: 0,
387
237
  },
388
- },
238
+ ],
389
239
  entry: 'template.hbs',
390
240
  });
391
241
  fs.readFile.mockImplementation((filePath) => {
392
- if (filePath.endsWith('page-layout.yaml')) {
393
- throw { code: 'ENOENT' };
394
- }
395
- if (filePath.endsWith('page-layout.json')) {
242
+ if (filePath.endsWith('manifest.json')) {
396
243
  return jsonContentWithoutOptionsAndMaxNodes;
397
244
  }
398
245
  if (filePath.endsWith('template.hbs')) {
@@ -400,41 +247,387 @@ entry: template.hbs
400
247
  }
401
248
  throw new Error('File not found');
402
249
  });
403
- const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson);
250
+ const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(manifestJson);
404
251
  expect(layoutDefinition).toEqual({
405
252
  name: 'test-layout',
406
253
  displayName: 'Test Layout',
407
254
  description: 'A test layout',
408
- zones: {
409
- main: {
255
+ zones: [
256
+ {
257
+ key: 'main',
410
258
  displayName: 'Main Zone',
411
259
  description: 'Main content area',
412
- minNodes: 1,
413
260
  },
414
- sidebar: {
261
+ {
262
+ key: 'sidebar',
415
263
  displayName: 'Sidebar Zone',
416
264
  description: 'Sidebar content area',
417
- minNodes: 0,
418
265
  },
419
- },
266
+ ],
420
267
  template: templateContent,
421
268
  });
422
269
  }));
270
+ describe('YAML format (deprecated)', () => {
271
+ it('should load layout definition from YAML file', () => __awaiter(void 0, void 0, void 0, function* () {
272
+ fs.readFile.mockImplementation((filePath) => {
273
+ if (filePath.endsWith('page-layout.yaml')) {
274
+ return yamlContent;
275
+ }
276
+ if (filePath.endsWith('template.hbs')) {
277
+ return templateContent;
278
+ }
279
+ throw new Error('File not found');
280
+ });
281
+ const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(pageLayoutYaml);
282
+ expect(layoutDefinition).toEqual({
283
+ name: 'test-layout',
284
+ displayName: 'Test Layout',
285
+ description: 'A test layout',
286
+ zones: [
287
+ {
288
+ key: 'main',
289
+ displayName: 'Main Zone',
290
+ description: 'Main content area',
291
+ },
292
+ ],
293
+ properties: {
294
+ color: {
295
+ title: 'Color',
296
+ description: 'Color options',
297
+ type: 'string',
298
+ enum: ['red', 'blue'],
299
+ },
300
+ },
301
+ template: templateContent,
302
+ });
303
+ }));
304
+ it('should throw validation error if invalid data for YAML', () => __awaiter(void 0, void 0, void 0, function* () {
305
+ fs.readFile.mockImplementation((filePath) => {
306
+ if (filePath.endsWith('page-layout.yaml')) {
307
+ return yamlContentInvalidLayout;
308
+ }
309
+ if (filePath.endsWith('template.hbs')) {
310
+ return templateContent;
311
+ }
312
+ throw new Error('File not found');
313
+ });
314
+ yield expect((0, definitions_1.loadLayoutDefinition)(pageLayoutYaml)).rejects
315
+ .toThrowErrorMatchingInlineSnapshot(`
316
+ "Failed loading layout definition: Failed to parse ./some-dir/page-layout.yaml:
317
+
318
+ Schema validation failed:
319
+ - Required at "name""
320
+ `);
321
+ }));
322
+ it('should throw an error if YAML layout file has invalid YAML', () => __awaiter(void 0, void 0, void 0, function* () {
323
+ fs.readFile.mockImplementation((filePath) => {
324
+ if (filePath.endsWith('page-layout.yaml')) {
325
+ return 'name: invalid yaml, entry: template.hbs';
326
+ }
327
+ if (filePath.endsWith('template.hbs')) {
328
+ return templateContent;
329
+ }
330
+ throw new Error('File not found');
331
+ });
332
+ yield expect((0, definitions_1.loadLayoutDefinition)(pageLayoutYaml)).rejects
333
+ .toThrowErrorMatchingInlineSnapshot(`
334
+ "Failed loading layout definition: Failed to parse ./some-dir/page-layout.yaml: Nested mappings are not allowed in compact mappings at line 1, column 7:
335
+
336
+ name: invalid yaml, entry: template.hbs
337
+ ^
338
+ "
339
+ `);
340
+ }));
341
+ it('should load layout definition without options field from YAML file', () => __awaiter(void 0, void 0, void 0, function* () {
342
+ const yamlContentWithoutOptions = `
343
+ name: test-layout
344
+ displayName: Test Layout
345
+ description: A test layout
346
+ zones:
347
+ main:
348
+ displayName: Main Zone
349
+ description: Main content area
350
+ minNodes: 1
351
+ entry: template.hbs
352
+ `;
353
+ fs.readFile.mockImplementation((filePath) => {
354
+ if (filePath.endsWith('page-layout.yaml')) {
355
+ return yamlContentWithoutOptions;
356
+ }
357
+ if (filePath.endsWith('template.hbs')) {
358
+ return templateContent;
359
+ }
360
+ throw new Error('File not found');
361
+ });
362
+ const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(pageLayoutYaml);
363
+ expect(layoutDefinition).toEqual({
364
+ name: 'test-layout',
365
+ displayName: 'Test Layout',
366
+ description: 'A test layout',
367
+ zones: [
368
+ {
369
+ key: 'main',
370
+ displayName: 'Main Zone',
371
+ description: 'Main content area',
372
+ },
373
+ ],
374
+ template: templateContent,
375
+ });
376
+ }));
377
+ it('should load layout definition without maxNodes in zones from YAML file', () => __awaiter(void 0, void 0, void 0, function* () {
378
+ const yamlContentWithoutMaxNodes = `
379
+ name: test-layout
380
+ displayName: Test Layout
381
+ description: A test layout
382
+ zones:
383
+ main:
384
+ displayName: Main Zone
385
+ description: Main content area
386
+ minNodes: 1
387
+ sidebar:
388
+ displayName: Sidebar Zone
389
+ description: Sidebar content area
390
+ minNodes: 0
391
+ entry: template.hbs
392
+ `;
393
+ fs.readFile.mockImplementation((filePath) => {
394
+ if (filePath.endsWith('page-layout.yaml')) {
395
+ return yamlContentWithoutMaxNodes;
396
+ }
397
+ if (filePath.endsWith('template.hbs')) {
398
+ return templateContent;
399
+ }
400
+ throw new Error('File not found');
401
+ });
402
+ const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(pageLayoutYaml);
403
+ expect(layoutDefinition).toEqual({
404
+ name: 'test-layout',
405
+ displayName: 'Test Layout',
406
+ description: 'A test layout',
407
+ zones: [
408
+ {
409
+ key: 'main',
410
+ displayName: 'Main Zone',
411
+ description: 'Main content area',
412
+ },
413
+ {
414
+ key: 'sidebar',
415
+ displayName: 'Sidebar Zone',
416
+ description: 'Sidebar content area',
417
+ },
418
+ ],
419
+ template: templateContent,
420
+ });
421
+ }));
422
+ });
423
423
  });
424
424
  describe('LayoutDefinitionParse', () => {
425
- it('should allow zone.minNodes to be undefined', () => {
426
- const layoutDefinition = definitions_1.BaseLayoutDefinition.parse({
427
- name: 'test-layout',
428
- displayName: 'Test Layout',
429
- description: 'A test layout',
430
- zones: {
431
- main: {
432
- displayName: 'Main Zone',
433
- description: 'Main content area',
425
+ describe('V1 format - zones objects and options (deprecated)', () => {
426
+ it('should allow zone.minNodes to be undefined', () => {
427
+ const layoutDefinition = definitions_1.InputLayoutDefinitionV1.parse({
428
+ name: 'test-layout',
429
+ displayName: 'Test Layout',
430
+ description: 'A test layout',
431
+ entry: 'template.hbs',
432
+ zones: {
433
+ main: {
434
+ displayName: 'Main Zone',
435
+ description: 'Main content area',
436
+ },
434
437
  },
435
- },
438
+ });
439
+ expect(layoutDefinition.zones.main.minNodes).toBe(0);
440
+ });
441
+ describe('option schema validation', () => {
442
+ it('should accept specified valueTypes', () => {
443
+ var _a, _b, _c;
444
+ const layoutDefinition = definitions_1.InputLayoutDefinitionV1.parse({
445
+ name: 'test-layout',
446
+ displayName: 'Test Layout',
447
+ description: 'A test layout',
448
+ entry: 'template.hbs',
449
+ zones: {
450
+ main: {
451
+ displayName: 'Main Zone',
452
+ description: 'Main content area',
453
+ },
454
+ },
455
+ options: {
456
+ theme: {
457
+ displayName: 'Theme',
458
+ description: 'Color theme',
459
+ valueType: 'string-enum',
460
+ values: ['light', 'dark'],
461
+ },
462
+ showSidebar: {
463
+ displayName: 'Show Sidebar',
464
+ description: 'Toggle sidebar',
465
+ valueType: 'boolean',
466
+ },
467
+ customCss: {
468
+ displayName: 'Custom CSS',
469
+ description: 'Enter custom CSS',
470
+ valueType: 'text',
471
+ },
472
+ },
473
+ });
474
+ expect((_a = layoutDefinition.options) === null || _a === void 0 ? void 0 : _a.theme.valueType).toBe('string-enum');
475
+ expect((_b = layoutDefinition.options) === null || _b === void 0 ? void 0 : _b.showSidebar.valueType).toBe('boolean');
476
+ expect((_c = layoutDefinition.options) === null || _c === void 0 ? void 0 : _c.customCss.valueType).toBe('text');
477
+ });
478
+ it('should reject invalid valueType', () => {
479
+ expect(() => definitions_1.InputLayoutDefinitionV1.parse({
480
+ name: 'test-layout',
481
+ displayName: 'Test Layout',
482
+ description: 'A test layout',
483
+ entry: 'template.hbs',
484
+ zones: {
485
+ main: {
486
+ displayName: 'Main Zone',
487
+ description: 'Main content area',
488
+ },
489
+ },
490
+ options: {
491
+ badOption: {
492
+ displayName: 'Bad Option',
493
+ description: 'Invalid valueType',
494
+ valueType: 'INVALID_TYPE',
495
+ },
496
+ },
497
+ })).toThrow();
498
+ });
499
+ it('should allow valueType and values to be optional', () => {
500
+ var _a, _b, _c, _d, _e, _f, _g, _h;
501
+ const layoutDefinition = definitions_1.InputLayoutDefinitionV1.parse({
502
+ name: 'test-layout',
503
+ displayName: 'Test Layout',
504
+ description: 'A test layout',
505
+ entry: 'template.hbs',
506
+ zones: {
507
+ main: {
508
+ displayName: 'Main Zone',
509
+ description: 'Main content area',
510
+ },
511
+ },
512
+ options: {
513
+ withBoth: {
514
+ displayName: 'With Both',
515
+ description: 'Has valueType and values',
516
+ valueType: 'string-enum',
517
+ values: ['option1', 'option2'],
518
+ },
519
+ withValueTypeOnly: {
520
+ displayName: 'With ValueType Only',
521
+ description: 'Has valueType but no values',
522
+ valueType: 'boolean',
523
+ },
524
+ withValuesOnly: {
525
+ displayName: 'With Values Only',
526
+ description: 'Has values but no valueType (legacy)',
527
+ values: ['legacy1', 'legacy2'],
528
+ },
529
+ withNeither: {
530
+ displayName: 'With Neither',
531
+ description: 'Has neither valueType nor values',
532
+ },
533
+ },
534
+ });
535
+ // Schema accepts all combinations - Business logic validation (e.g., boolean/text can't have values) happens on backend and reported in CLI on deploy.
536
+ expect((_a = layoutDefinition.options) === null || _a === void 0 ? void 0 : _a.withBoth.valueType).toBe('string-enum');
537
+ expect((_b = layoutDefinition.options) === null || _b === void 0 ? void 0 : _b.withBoth.values).toEqual([
538
+ 'option1',
539
+ 'option2',
540
+ ]);
541
+ expect((_c = layoutDefinition.options) === null || _c === void 0 ? void 0 : _c.withValueTypeOnly.valueType).toBe('boolean');
542
+ expect((_d = layoutDefinition.options) === null || _d === void 0 ? void 0 : _d.withValueTypeOnly.values).toBeUndefined();
543
+ expect((_e = layoutDefinition.options) === null || _e === void 0 ? void 0 : _e.withValuesOnly.valueType).toBeUndefined();
544
+ expect((_f = layoutDefinition.options) === null || _f === void 0 ? void 0 : _f.withValuesOnly.values).toEqual([
545
+ 'legacy1',
546
+ 'legacy2',
547
+ ]);
548
+ expect((_g = layoutDefinition.options) === null || _g === void 0 ? void 0 : _g.withNeither.valueType).toBeUndefined();
549
+ expect((_h = layoutDefinition.options) === null || _h === void 0 ? void 0 : _h.withNeither.values).toBeUndefined();
550
+ });
551
+ });
552
+ });
553
+ describe('V2 format - zones arrays and properties', () => {
554
+ it('should not allow minNodes to be defined in a zone', () => {
555
+ expect(() => definitions_1.InputLayoutDefinitionV2.parse({
556
+ name: 'test-layout',
557
+ displayName: 'Test Layout',
558
+ description: 'A test layout',
559
+ entry: 'template.hbs',
560
+ zones: [
561
+ {
562
+ key: 'main',
563
+ displayName: 'Main Zone',
564
+ description: 'Main content area',
565
+ minNodes: 1,
566
+ },
567
+ ],
568
+ })).toThrow();
569
+ });
570
+ describe('property schema validation', () => {
571
+ it('should accept specified types', () => {
572
+ var _a, _b, _c;
573
+ const layoutDefinition = definitions_1.InputLayoutDefinitionV2.parse({
574
+ name: 'test-layout',
575
+ displayName: 'Test Layout',
576
+ description: 'A test layout',
577
+ entry: 'template.hbs',
578
+ zones: [
579
+ {
580
+ key: 'main',
581
+ displayName: 'Main Zone',
582
+ description: 'Main content area',
583
+ },
584
+ ],
585
+ properties: {
586
+ theme: {
587
+ title: 'Theme',
588
+ description: 'Color theme',
589
+ type: 'string',
590
+ enum: ['light', 'dark'],
591
+ },
592
+ showSidebar: {
593
+ title: 'Show Sidebar',
594
+ description: 'Toggle sidebar',
595
+ type: 'boolean',
596
+ },
597
+ customCss: {
598
+ title: 'Custom CSS',
599
+ description: 'Enter custom CSS',
600
+ type: 'string',
601
+ },
602
+ },
603
+ });
604
+ expect((_a = layoutDefinition.properties) === null || _a === void 0 ? void 0 : _a.theme.type).toBe('string');
605
+ expect((_b = layoutDefinition.properties) === null || _b === void 0 ? void 0 : _b.showSidebar.type).toBe('boolean');
606
+ expect((_c = layoutDefinition.properties) === null || _c === void 0 ? void 0 : _c.customCss.type).toBe('string');
607
+ });
608
+ it('should reject invalid type', () => {
609
+ expect(() => definitions_1.InputLayoutDefinitionV2.parse({
610
+ name: 'test-layout',
611
+ displayName: 'Test Layout',
612
+ description: 'A test layout',
613
+ entry: 'template.hbs',
614
+ zones: [
615
+ {
616
+ key: 'main',
617
+ displayName: 'Main Zone',
618
+ description: 'Main content area',
619
+ },
620
+ ],
621
+ properties: {
622
+ badProperty: {
623
+ title: 'Bad Property',
624
+ description: 'Invalid type',
625
+ type: 'INVALID_TYPE',
626
+ },
627
+ },
628
+ })).toThrow();
629
+ });
436
630
  });
437
- expect(layoutDefinition.zones.main.minNodes).toBe(0);
438
631
  });
439
632
  });
440
633
  describe('validateTemplateFile', () => {