@squiz/dxp-cli-next 5.31.0-develop.1 → 5.31.0-develop.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.
- package/lib/cdp/instance/activate/activate.js +1 -1
- package/lib/cdp/instance/activate/activate.spec.js +4 -4
- package/lib/page/layouts/deploy/deploy.js +6 -32
- package/lib/page/layouts/deploy/deploy.spec.js +2 -55
- package/lib/page/layouts/dev/dev.js +1 -3
- package/lib/page/layouts/dev/dev.spec.js +0 -27
- package/lib/page/utils/definitions.d.ts +15 -30
- package/lib/page/utils/definitions.js +1 -3
- package/lib/page/utils/definitions.spec.js +0 -128
- package/lib/page/utils/parse-args.d.ts +1 -17
- package/lib/page/utils/parse-args.js +2 -38
- package/lib/page/utils/parse-args.spec.js +0 -138
- package/lib/page/utils/render.d.ts +3 -14
- package/lib/page/utils/render.js +2 -15
- package/lib/page/utils/render.spec.js +1 -1
- package/lib/page/utils/server.d.ts +1 -1
- package/package.json +1 -1
|
@@ -77,7 +77,7 @@ const createActivateCommand = () => {
|
|
|
77
77
|
}
|
|
78
78
|
(0, utils_1.logDebug)(`PUT ${apiUrl}`);
|
|
79
79
|
const activateInstanceAndDeploySchemaResponse = (yield apiService.client
|
|
80
|
-
.put(apiUrl, {
|
|
80
|
+
.put(apiUrl, undefined, {
|
|
81
81
|
headers: Object.assign({ 'Content-Type': 'application/json' }, (!hasRegion && { 'x-dxp-tenant': tenant })),
|
|
82
82
|
})
|
|
83
83
|
.catch((err) => {
|
|
@@ -102,7 +102,7 @@ describe('cdpInstanceCommand', () => {
|
|
|
102
102
|
axios_1.default.create.mockReturnValue(mockedAxiosInstance);
|
|
103
103
|
const program = (0, activate_1.default)();
|
|
104
104
|
yield program.parseAsync((0, utils_1.createMockActivateArgs)(region));
|
|
105
|
-
expect(mockedAxiosInstance.put).toHaveBeenCalledWith(`http://localhost:9999/__dxp/${region}/scv-deploy/${mockTenant}`, {
|
|
105
|
+
expect(mockedAxiosInstance.put).toHaveBeenCalledWith(`http://localhost:9999/__dxp/${region}/scv-deploy/${mockTenant}`, undefined, {
|
|
106
106
|
headers: {
|
|
107
107
|
'Content-Type': 'application/json',
|
|
108
108
|
},
|
|
@@ -138,7 +138,7 @@ describe('cdpInstanceCommand', () => {
|
|
|
138
138
|
axios_1.default.create.mockReturnValue(mockedAxios);
|
|
139
139
|
const program = (0, activate_1.default)();
|
|
140
140
|
yield program.parseAsync((0, utils_1.createMockActivateArgs)(region));
|
|
141
|
-
expect(mockedAxios.put).toHaveBeenCalledWith(`http://localhost:9999/__dxp/${region}/scv-deploy/${mockTenant}`, {
|
|
141
|
+
expect(mockedAxios.put).toHaveBeenCalledWith(`http://localhost:9999/__dxp/${region}/scv-deploy/${mockTenant}`, undefined, {
|
|
142
142
|
headers: {
|
|
143
143
|
'Content-Type': 'application/json',
|
|
144
144
|
},
|
|
@@ -207,7 +207,7 @@ describe('cdpInstanceCommand', () => {
|
|
|
207
207
|
expect(`${mockDomain}${mockPath}`).toEqual(mockDomainWithPath);
|
|
208
208
|
const program = (0, activate_1.default)();
|
|
209
209
|
yield program.parseAsync(createMockArgs());
|
|
210
|
-
expect(mockedAxiosInstance.put).toHaveBeenCalledWith('http://localhost:9999/__dxp/service/scv-deploy', {
|
|
210
|
+
expect(mockedAxiosInstance.put).toHaveBeenCalledWith('http://localhost:9999/__dxp/service/scv-deploy', undefined, {
|
|
211
211
|
headers: {
|
|
212
212
|
'Content-Type': 'application/json',
|
|
213
213
|
'x-dxp-tenant': 'myTenant',
|
|
@@ -248,7 +248,7 @@ describe('cdpInstanceCommand', () => {
|
|
|
248
248
|
expect(`${mockDomain}${mockPath}`).toEqual(mockDomainWithPath);
|
|
249
249
|
const program = (0, activate_1.default)();
|
|
250
250
|
yield program.parseAsync(createMockArgs());
|
|
251
|
-
expect(mockedAxios.put).toHaveBeenCalledWith('http://localhost:9999/__dxp/service/scv-deploy', {
|
|
251
|
+
expect(mockedAxios.put).toHaveBeenCalledWith('http://localhost:9999/__dxp/service/scv-deploy', undefined, {
|
|
252
252
|
headers: {
|
|
253
253
|
'Content-Type': 'application/json',
|
|
254
254
|
'x-dxp-tenant': 'myTenant',
|
|
@@ -90,34 +90,8 @@ const createDeployCommand = () => {
|
|
|
90
90
|
return deployCommand;
|
|
91
91
|
};
|
|
92
92
|
exports.default = createDeployCommand;
|
|
93
|
-
/**
|
|
94
|
-
* Formats backend error responses into user-friendly error messages.
|
|
95
|
-
* Handles both validation errors (400s) and unknown server errors (500s).
|
|
96
|
-
*
|
|
97
|
-
* @param data - The error response data from the backend
|
|
98
|
-
* @param statusCode - The HTTP status code (optional)
|
|
99
|
-
* @returns Formatted error message string
|
|
100
|
-
*/
|
|
101
|
-
function formatErrorResponse(data, statusCode) {
|
|
102
|
-
// For 500 errors or unknown errors, show a generic message
|
|
103
|
-
if (statusCode && statusCode >= 500) {
|
|
104
|
-
return data.message || data.data || 'An unknown error occurred';
|
|
105
|
-
}
|
|
106
|
-
// For validation errors (400s), format with details
|
|
107
|
-
let errorMessage = data.message || 'Validation failed';
|
|
108
|
-
// Options validation errors come as an object with field keys
|
|
109
|
-
if (data.details &&
|
|
110
|
-
typeof data.details === 'object' &&
|
|
111
|
-
!Array.isArray(data.details)) {
|
|
112
|
-
const details = Object.entries(data.details)
|
|
113
|
-
.map(([field, error]) => ` - ${field}: ${error.message}`)
|
|
114
|
-
.join('\n');
|
|
115
|
-
errorMessage += `\n${details}`;
|
|
116
|
-
}
|
|
117
|
-
return errorMessage;
|
|
118
|
-
}
|
|
119
93
|
function uploadLayout(client, layout, contentServiceUrl, dryRun) {
|
|
120
|
-
var _a;
|
|
94
|
+
var _a, _b;
|
|
121
95
|
return __awaiter(this, void 0, void 0, function* () {
|
|
122
96
|
try {
|
|
123
97
|
const queryParam = dryRun ? '?_dryRun=true' : '';
|
|
@@ -127,13 +101,13 @@ function uploadLayout(client, layout, contentServiceUrl, dryRun) {
|
|
|
127
101
|
if (response.status === 200) {
|
|
128
102
|
return response;
|
|
129
103
|
}
|
|
130
|
-
|
|
104
|
+
const error = new Error(response.data.message);
|
|
105
|
+
if ((_b = (_a = response.data.details) === null || _a === void 0 ? void 0 : _a.input) === null || _b === void 0 ? void 0 : _b.message) {
|
|
106
|
+
error.message += `: ${response.data.details.input.message}`;
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
131
109
|
}
|
|
132
110
|
catch (error) {
|
|
133
|
-
// Extract error details from axios error response
|
|
134
|
-
if ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) {
|
|
135
|
-
throw new Error(formatErrorResponse(error.response.data, error.response.status));
|
|
136
|
-
}
|
|
137
111
|
throw error;
|
|
138
112
|
}
|
|
139
113
|
});
|
|
@@ -170,7 +170,7 @@ describe('deployCommand', () => {
|
|
|
170
170
|
const program = (0, deploy_1.default)();
|
|
171
171
|
errorSpy = jest.spyOn(program, 'error').mockImplementation();
|
|
172
172
|
yield program.parseAsync(createMockArgs({ contentServiceUrl }));
|
|
173
|
-
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('
|
|
173
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('An unknown error occurred'));
|
|
174
174
|
}));
|
|
175
175
|
it('should log additional details when layout validation fails', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
176
176
|
const dxpBaseUrl = 'http://dxp-base-url.com';
|
|
@@ -187,61 +187,8 @@ describe('deployCommand', () => {
|
|
|
187
187
|
const program = (0, deploy_1.default)();
|
|
188
188
|
errorSpy = jest.spyOn(program, 'error').mockImplementation();
|
|
189
189
|
yield program.parseAsync(createMockArgs({ dxpBaseUrl }));
|
|
190
|
-
|
|
191
|
-
expect(errorCall).toContain('- input: ERROR: Validation failed: "version" is an excess property and therefore is not allowed');
|
|
190
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Layout validation failed: ERROR: Validation failed: "version" is an excess property and therefore is not allowed'));
|
|
192
191
|
}));
|
|
193
|
-
describe('backend option validation error reporting', () => {
|
|
194
|
-
it('should display clean error when single option validation fails', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
195
|
-
const contentServiceUrl = 'http://localhost:9999';
|
|
196
|
-
(0, nock_1.default)(contentServiceUrl)
|
|
197
|
-
.post('/page-layout')
|
|
198
|
-
.reply(400, {
|
|
199
|
-
message: 'Validation failed',
|
|
200
|
-
details: {
|
|
201
|
-
'input.options.myBoolean': {
|
|
202
|
-
message: "Option 'myBoolean' with valueType 'boolean' can not have values specified",
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
const program = (0, deploy_1.default)();
|
|
207
|
-
errorSpy = jest.spyOn(program, 'error').mockImplementation();
|
|
208
|
-
yield program.parseAsync(createMockArgs({ contentServiceUrl }));
|
|
209
|
-
const errorCall = errorSpy.mock.calls[0][0];
|
|
210
|
-
expect(errorCall).toContain('Validation failed');
|
|
211
|
-
expect(errorCall).toContain("- input.options.myBoolean: Option 'myBoolean' with valueType 'boolean' can not have values specified");
|
|
212
|
-
}));
|
|
213
|
-
it('should display multiple option validation errors cleanly', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
214
|
-
const contentServiceUrl = 'http://localhost:9999';
|
|
215
|
-
(0, nock_1.default)(contentServiceUrl)
|
|
216
|
-
.post('/page-layout')
|
|
217
|
-
.reply(400, {
|
|
218
|
-
message: 'Validation failed',
|
|
219
|
-
details: {
|
|
220
|
-
'input.options.badBoolean': {
|
|
221
|
-
message: "Option 'badBoolean' with valueType 'boolean' can not have values specified",
|
|
222
|
-
},
|
|
223
|
-
'input.options.badText': {
|
|
224
|
-
message: "Option 'badText' with valueType 'text' can not have values specified",
|
|
225
|
-
},
|
|
226
|
-
'input.options.emptyEnum': {
|
|
227
|
-
message: "Option 'emptyEnum' with valueType 'string-enum' must have at least one value defined",
|
|
228
|
-
},
|
|
229
|
-
'input.options.badOption.valueType': {
|
|
230
|
-
message: "Option 'badOption' has invalid valueType 'INVALID'. Must be one of: string-enum, boolean, text",
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
const program = (0, deploy_1.default)();
|
|
235
|
-
errorSpy = jest.spyOn(program, 'error').mockImplementation();
|
|
236
|
-
yield program.parseAsync(createMockArgs({ contentServiceUrl }));
|
|
237
|
-
const errorCall = errorSpy.mock.calls[0][0];
|
|
238
|
-
expect(errorCall).toContain('Validation failed');
|
|
239
|
-
expect(errorCall).toContain("- input.options.badBoolean: Option 'badBoolean' with valueType 'boolean' can not have values specified");
|
|
240
|
-
expect(errorCall).toContain("- input.options.badText: Option 'badText' with valueType 'text' can not have values specified");
|
|
241
|
-
expect(errorCall).toContain("- input.options.emptyEnum: Option 'emptyEnum' with valueType 'string-enum' must have at least one value defined");
|
|
242
|
-
expect(errorCall).toContain("- input.options.badOption.valueType: Option 'badOption' has invalid valueType 'INVALID'. Must be one of: string-enum, boolean, text");
|
|
243
|
-
}));
|
|
244
|
-
});
|
|
245
192
|
describe('zone consistency validation', () => {
|
|
246
193
|
it('should handle zone consistency validation errors where zones are used but not defined in the layout', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
247
194
|
const file = './src/__tests__/layout.yaml';
|
|
@@ -45,14 +45,12 @@ const createDevCommand = () => {
|
|
|
45
45
|
}
|
|
46
46
|
// Confirm for entry property
|
|
47
47
|
const layoutDefinition = Object.assign({}, rawLayoutDefinition);
|
|
48
|
-
// Normalize options to convert string booleans to actual booleans
|
|
49
|
-
const normalizedOptions = (0, parse_args_1.normalizeLayoutOptions)(options.options, layoutDefinition);
|
|
50
48
|
exports.logger.info('Starting development server...');
|
|
51
49
|
yield (0, server_1.startDevServer)({
|
|
52
50
|
configPath: path_1.default.resolve(options.config),
|
|
53
51
|
layoutDefinition,
|
|
54
52
|
zoneContent: options.zones,
|
|
55
|
-
layoutOptions:
|
|
53
|
+
layoutOptions: options.options,
|
|
56
54
|
stylesheet: options.stylesheet
|
|
57
55
|
? path_1.default.resolve(options.stylesheet)
|
|
58
56
|
: undefined,
|
|
@@ -136,33 +136,6 @@ describe('devCommand', () => {
|
|
|
136
136
|
layoutOptions,
|
|
137
137
|
}));
|
|
138
138
|
}));
|
|
139
|
-
it('normalizes boolean options from strings to actual booleans', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
140
|
-
const mockLayout = {
|
|
141
|
-
name: 'Test Layout',
|
|
142
|
-
options: {
|
|
143
|
-
showFooter: { valueType: 'boolean' },
|
|
144
|
-
showHeader: { valueType: 'boolean' },
|
|
145
|
-
theme: { valueType: 'string-enum' },
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
|
-
definitions_1.loadLayoutDefinition.mockResolvedValue(mockLayout);
|
|
149
|
-
const layoutOptions = {
|
|
150
|
-
showFooter: 'true',
|
|
151
|
-
showHeader: 'false',
|
|
152
|
-
theme: 'dark',
|
|
153
|
-
};
|
|
154
|
-
const program = (0, dev_1.default)();
|
|
155
|
-
const args = createMockArgs({ layoutOptions });
|
|
156
|
-
process.argv = args;
|
|
157
|
-
yield program.parseAsync(args);
|
|
158
|
-
expect(server_1.startDevServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
159
|
-
layoutOptions: {
|
|
160
|
-
showFooter: true,
|
|
161
|
-
showHeader: false,
|
|
162
|
-
theme: 'dark', // Remains string
|
|
163
|
-
},
|
|
164
|
-
}));
|
|
165
|
-
}));
|
|
166
139
|
it('handles failure to load layout definition', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
167
140
|
const config = './src/__tests__/layout.yaml';
|
|
168
141
|
definitions_1.loadLayoutDefinition.mockResolvedValue(undefined);
|
|
@@ -49,18 +49,15 @@ export declare const BaseLayoutDefinition: z.ZodObject<{
|
|
|
49
49
|
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
50
50
|
displayName: z.ZodString;
|
|
51
51
|
description: z.ZodString;
|
|
52
|
-
|
|
53
|
-
values: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
52
|
+
values: z.ZodArray<z.ZodString, "many">;
|
|
54
53
|
}, "strip", z.ZodTypeAny, {
|
|
54
|
+
values: string[];
|
|
55
55
|
description: string;
|
|
56
56
|
displayName: string;
|
|
57
|
-
values?: string[] | undefined;
|
|
58
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
59
57
|
}, {
|
|
58
|
+
values: string[];
|
|
60
59
|
description: string;
|
|
61
60
|
displayName: string;
|
|
62
|
-
values?: string[] | undefined;
|
|
63
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
64
61
|
}>>>;
|
|
65
62
|
}, "strip", z.ZodTypeAny, {
|
|
66
63
|
name: string;
|
|
@@ -73,10 +70,9 @@ export declare const BaseLayoutDefinition: z.ZodObject<{
|
|
|
73
70
|
maxNodes?: number | undefined;
|
|
74
71
|
}>;
|
|
75
72
|
options?: Record<string, {
|
|
73
|
+
values: string[];
|
|
76
74
|
description: string;
|
|
77
75
|
displayName: string;
|
|
78
|
-
values?: string[] | undefined;
|
|
79
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
80
76
|
}> | undefined;
|
|
81
77
|
}, {
|
|
82
78
|
name: string;
|
|
@@ -89,10 +85,9 @@ export declare const BaseLayoutDefinition: z.ZodObject<{
|
|
|
89
85
|
maxNodes?: number | undefined;
|
|
90
86
|
}>;
|
|
91
87
|
options?: Record<string, {
|
|
88
|
+
values: string[];
|
|
92
89
|
description: string;
|
|
93
90
|
displayName: string;
|
|
94
|
-
values?: string[] | undefined;
|
|
95
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
96
91
|
}> | undefined;
|
|
97
92
|
}>;
|
|
98
93
|
export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
|
|
@@ -138,18 +133,15 @@ export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape
|
|
|
138
133
|
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
139
134
|
displayName: z.ZodString;
|
|
140
135
|
description: z.ZodString;
|
|
141
|
-
|
|
142
|
-
values: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
136
|
+
values: z.ZodArray<z.ZodString, "many">;
|
|
143
137
|
}, "strip", z.ZodTypeAny, {
|
|
138
|
+
values: string[];
|
|
144
139
|
description: string;
|
|
145
140
|
displayName: string;
|
|
146
|
-
values?: string[] | undefined;
|
|
147
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
148
141
|
}, {
|
|
142
|
+
values: string[];
|
|
149
143
|
description: string;
|
|
150
144
|
displayName: string;
|
|
151
|
-
values?: string[] | undefined;
|
|
152
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
153
145
|
}>>>;
|
|
154
146
|
}, {
|
|
155
147
|
entry: z.ZodString;
|
|
@@ -165,10 +157,9 @@ export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape
|
|
|
165
157
|
maxNodes?: number | undefined;
|
|
166
158
|
}>;
|
|
167
159
|
options?: Record<string, {
|
|
160
|
+
values: string[];
|
|
168
161
|
description: string;
|
|
169
162
|
displayName: string;
|
|
170
|
-
values?: string[] | undefined;
|
|
171
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
172
163
|
}> | undefined;
|
|
173
164
|
}, {
|
|
174
165
|
name: string;
|
|
@@ -182,10 +173,9 @@ export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape
|
|
|
182
173
|
maxNodes?: number | undefined;
|
|
183
174
|
}>;
|
|
184
175
|
options?: Record<string, {
|
|
176
|
+
values: string[];
|
|
185
177
|
description: string;
|
|
186
178
|
displayName: string;
|
|
187
|
-
values?: string[] | undefined;
|
|
188
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
189
179
|
}> | undefined;
|
|
190
180
|
}>;
|
|
191
181
|
export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
|
|
@@ -231,18 +221,15 @@ export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
231
221
|
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
232
222
|
displayName: z.ZodString;
|
|
233
223
|
description: z.ZodString;
|
|
234
|
-
|
|
235
|
-
values: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
224
|
+
values: z.ZodArray<z.ZodString, "many">;
|
|
236
225
|
}, "strip", z.ZodTypeAny, {
|
|
226
|
+
values: string[];
|
|
237
227
|
description: string;
|
|
238
228
|
displayName: string;
|
|
239
|
-
values?: string[] | undefined;
|
|
240
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
241
229
|
}, {
|
|
230
|
+
values: string[];
|
|
242
231
|
description: string;
|
|
243
232
|
displayName: string;
|
|
244
|
-
values?: string[] | undefined;
|
|
245
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
246
233
|
}>>>;
|
|
247
234
|
}, {
|
|
248
235
|
template: z.ZodString;
|
|
@@ -258,10 +245,9 @@ export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
258
245
|
}>;
|
|
259
246
|
template: string;
|
|
260
247
|
options?: Record<string, {
|
|
248
|
+
values: string[];
|
|
261
249
|
description: string;
|
|
262
250
|
displayName: string;
|
|
263
|
-
values?: string[] | undefined;
|
|
264
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
265
251
|
}> | undefined;
|
|
266
252
|
}, {
|
|
267
253
|
name: string;
|
|
@@ -275,10 +261,9 @@ export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
275
261
|
}>;
|
|
276
262
|
template: string;
|
|
277
263
|
options?: Record<string, {
|
|
264
|
+
values: string[];
|
|
278
265
|
description: string;
|
|
279
266
|
displayName: string;
|
|
280
|
-
values?: string[] | undefined;
|
|
281
|
-
valueType?: "boolean" | "text" | "string-enum" | undefined;
|
|
282
267
|
}> | undefined;
|
|
283
268
|
}>;
|
|
284
269
|
export declare type InputLayoutDefinition = z.infer<typeof InputLayoutDefinition>;
|
|
@@ -56,7 +56,6 @@ const zod_1 = require("zod");
|
|
|
56
56
|
const yaml_1 = require("yaml");
|
|
57
57
|
// Local
|
|
58
58
|
const validation_1 = require("../templates/validation");
|
|
59
|
-
const render_1 = require("./render");
|
|
60
59
|
function loadLayoutDefinition(layoutFile) {
|
|
61
60
|
return __awaiter(this, void 0, void 0, function* () {
|
|
62
61
|
try {
|
|
@@ -150,8 +149,7 @@ exports.BaseLayoutDefinition = zod_1.z.object({
|
|
|
150
149
|
.record(zod_1.z.string(), zod_1.z.object({
|
|
151
150
|
displayName: zod_1.z.string(),
|
|
152
151
|
description: zod_1.z.string(),
|
|
153
|
-
|
|
154
|
-
values: zod_1.z.array(zod_1.z.string()).optional(),
|
|
152
|
+
values: zod_1.z.array(zod_1.z.string()),
|
|
155
153
|
}))
|
|
156
154
|
.optional(),
|
|
157
155
|
});
|
|
@@ -88,16 +88,6 @@ entry: template.hbs
|
|
|
88
88
|
description: 'Color options',
|
|
89
89
|
values: ['red', 'blue'],
|
|
90
90
|
},
|
|
91
|
-
showHeader: {
|
|
92
|
-
displayName: 'Show Header',
|
|
93
|
-
description: 'Toggle header visibility',
|
|
94
|
-
valueType: 'boolean',
|
|
95
|
-
},
|
|
96
|
-
customTitle: {
|
|
97
|
-
displayName: 'Custom Title',
|
|
98
|
-
description: 'Enter a custom title',
|
|
99
|
-
valueType: 'text',
|
|
100
|
-
},
|
|
101
91
|
},
|
|
102
92
|
entry: 'template.hbs',
|
|
103
93
|
});
|
|
@@ -188,16 +178,6 @@ entry: template.hbs
|
|
|
188
178
|
description: 'Color options',
|
|
189
179
|
values: ['red', 'blue'],
|
|
190
180
|
},
|
|
191
|
-
showHeader: {
|
|
192
|
-
displayName: 'Show Header',
|
|
193
|
-
description: 'Toggle header visibility',
|
|
194
|
-
valueType: 'boolean',
|
|
195
|
-
},
|
|
196
|
-
customTitle: {
|
|
197
|
-
displayName: 'Custom Title',
|
|
198
|
-
description: 'Enter a custom title',
|
|
199
|
-
valueType: 'text',
|
|
200
|
-
},
|
|
201
181
|
},
|
|
202
182
|
template: templateContent,
|
|
203
183
|
});
|
|
@@ -456,114 +436,6 @@ describe('LayoutDefinitionParse', () => {
|
|
|
456
436
|
});
|
|
457
437
|
expect(layoutDefinition.zones.main.minNodes).toBe(0);
|
|
458
438
|
});
|
|
459
|
-
describe('option schema validation', () => {
|
|
460
|
-
it('should accept specified valueTypes', () => {
|
|
461
|
-
var _a, _b, _c;
|
|
462
|
-
const layoutDefinition = definitions_1.BaseLayoutDefinition.parse({
|
|
463
|
-
name: 'test-layout',
|
|
464
|
-
displayName: 'Test Layout',
|
|
465
|
-
description: 'A test layout',
|
|
466
|
-
zones: {
|
|
467
|
-
main: {
|
|
468
|
-
displayName: 'Main Zone',
|
|
469
|
-
description: 'Main content area',
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
options: {
|
|
473
|
-
theme: {
|
|
474
|
-
displayName: 'Theme',
|
|
475
|
-
description: 'Color theme',
|
|
476
|
-
valueType: 'string-enum',
|
|
477
|
-
values: ['light', 'dark'],
|
|
478
|
-
},
|
|
479
|
-
showSidebar: {
|
|
480
|
-
displayName: 'Show Sidebar',
|
|
481
|
-
description: 'Toggle sidebar',
|
|
482
|
-
valueType: 'boolean',
|
|
483
|
-
},
|
|
484
|
-
customCss: {
|
|
485
|
-
displayName: 'Custom CSS',
|
|
486
|
-
description: 'Enter custom CSS',
|
|
487
|
-
valueType: 'text',
|
|
488
|
-
},
|
|
489
|
-
},
|
|
490
|
-
});
|
|
491
|
-
expect((_a = layoutDefinition.options) === null || _a === void 0 ? void 0 : _a.theme.valueType).toBe('string-enum');
|
|
492
|
-
expect((_b = layoutDefinition.options) === null || _b === void 0 ? void 0 : _b.showSidebar.valueType).toBe('boolean');
|
|
493
|
-
expect((_c = layoutDefinition.options) === null || _c === void 0 ? void 0 : _c.customCss.valueType).toBe('text');
|
|
494
|
-
});
|
|
495
|
-
it('should reject invalid valueType', () => {
|
|
496
|
-
expect(() => definitions_1.BaseLayoutDefinition.parse({
|
|
497
|
-
name: 'test-layout',
|
|
498
|
-
displayName: 'Test Layout',
|
|
499
|
-
description: 'A test layout',
|
|
500
|
-
zones: {
|
|
501
|
-
main: {
|
|
502
|
-
displayName: 'Main Zone',
|
|
503
|
-
description: 'Main content area',
|
|
504
|
-
},
|
|
505
|
-
},
|
|
506
|
-
options: {
|
|
507
|
-
badOption: {
|
|
508
|
-
displayName: 'Bad Option',
|
|
509
|
-
description: 'Invalid valueType',
|
|
510
|
-
valueType: 'INVALID_TYPE',
|
|
511
|
-
},
|
|
512
|
-
},
|
|
513
|
-
})).toThrow();
|
|
514
|
-
});
|
|
515
|
-
it('should allow valueType and values to be optional', () => {
|
|
516
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
517
|
-
const layoutDefinition = definitions_1.BaseLayoutDefinition.parse({
|
|
518
|
-
name: 'test-layout',
|
|
519
|
-
displayName: 'Test Layout',
|
|
520
|
-
description: 'A test layout',
|
|
521
|
-
zones: {
|
|
522
|
-
main: {
|
|
523
|
-
displayName: 'Main Zone',
|
|
524
|
-
description: 'Main content area',
|
|
525
|
-
},
|
|
526
|
-
},
|
|
527
|
-
options: {
|
|
528
|
-
withBoth: {
|
|
529
|
-
displayName: 'With Both',
|
|
530
|
-
description: 'Has valueType and values',
|
|
531
|
-
valueType: 'string-enum',
|
|
532
|
-
values: ['option1', 'option2'],
|
|
533
|
-
},
|
|
534
|
-
withValueTypeOnly: {
|
|
535
|
-
displayName: 'With ValueType Only',
|
|
536
|
-
description: 'Has valueType but no values',
|
|
537
|
-
valueType: 'boolean',
|
|
538
|
-
},
|
|
539
|
-
withValuesOnly: {
|
|
540
|
-
displayName: 'With Values Only',
|
|
541
|
-
description: 'Has values but no valueType (legacy)',
|
|
542
|
-
values: ['legacy1', 'legacy2'],
|
|
543
|
-
},
|
|
544
|
-
withNeither: {
|
|
545
|
-
displayName: 'With Neither',
|
|
546
|
-
description: 'Has neither valueType nor values',
|
|
547
|
-
},
|
|
548
|
-
},
|
|
549
|
-
});
|
|
550
|
-
// Schema accepts all combinations - Business logic validation (e.g., boolean/text can't have values) happens on backend and reported in CLI on deploy.
|
|
551
|
-
expect((_a = layoutDefinition.options) === null || _a === void 0 ? void 0 : _a.withBoth.valueType).toBe('string-enum');
|
|
552
|
-
expect((_b = layoutDefinition.options) === null || _b === void 0 ? void 0 : _b.withBoth.values).toEqual([
|
|
553
|
-
'option1',
|
|
554
|
-
'option2',
|
|
555
|
-
]);
|
|
556
|
-
expect((_c = layoutDefinition.options) === null || _c === void 0 ? void 0 : _c.withValueTypeOnly.valueType).toBe('boolean');
|
|
557
|
-
expect((_d = layoutDefinition.options) === null || _d === void 0 ? void 0 : _d.withValueTypeOnly.values).toBeUndefined();
|
|
558
|
-
expect((_e = layoutDefinition.options) === null || _e === void 0 ? void 0 : _e.withValuesOnly.valueType).toBeUndefined();
|
|
559
|
-
expect((_f = layoutDefinition.options) === null || _f === void 0 ? void 0 : _f.withValuesOnly.values).toEqual([
|
|
560
|
-
'legacy1',
|
|
561
|
-
'legacy2',
|
|
562
|
-
]);
|
|
563
|
-
expect((_g = layoutDefinition.options) === null || _g === void 0 ? void 0 : _g.withNeither.valueType).toBeUndefined();
|
|
564
|
-
expect((_h = layoutDefinition.options) === null || _h === void 0 ? void 0 : _h.withNeither.values).toBeUndefined();
|
|
565
|
-
});
|
|
566
|
-
});
|
|
567
439
|
});
|
|
568
440
|
describe('validateTemplateFile', () => {
|
|
569
441
|
describe('valid templates', () => {
|
|
@@ -19,26 +19,10 @@ export declare function parseZonesList(value: string, previous: Record<string, s
|
|
|
19
19
|
* - Comma-separated list: 'key1=value1,key2=value2' or 'key1:value1,key2:value2'
|
|
20
20
|
* - Multiple --options flags: later values override earlier ones
|
|
21
21
|
*
|
|
22
|
-
* Note: All values are returned as strings. Use normalizeLayoutOptions() to convert
|
|
23
|
-
* boolean options to actual boolean types based on the layout definition.
|
|
24
|
-
*
|
|
25
22
|
* @param value Current value being processed
|
|
26
23
|
* @param previous Previously processed value (used for multiple flags)
|
|
27
|
-
* @returns Record of option names to
|
|
24
|
+
* @returns Record of option names to values
|
|
28
25
|
*/
|
|
29
26
|
export declare function parseOptionsList(value: string, previous: Record<string, string>): {
|
|
30
27
|
[x: string]: string;
|
|
31
28
|
};
|
|
32
|
-
/**
|
|
33
|
-
* Normalizes layout options by converting string boolean values to actual booleans
|
|
34
|
-
* based on the layout definition's option types.
|
|
35
|
-
*
|
|
36
|
-
* @param options The parsed options from CLI (all strings)
|
|
37
|
-
* @param layoutDefinition The layout definition with option type information
|
|
38
|
-
* @returns Options with boolean values converted to actual booleans
|
|
39
|
-
*/
|
|
40
|
-
export declare function normalizeLayoutOptions(options: Record<string, string>, layoutDefinition: {
|
|
41
|
-
options?: Record<string, {
|
|
42
|
-
valueType?: string;
|
|
43
|
-
}>;
|
|
44
|
-
}): Record<string, string | boolean>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const render_1 = require("./render");
|
|
3
|
+
exports.parseOptionsList = exports.parseZonesList = void 0;
|
|
5
4
|
/**
|
|
6
5
|
* Parse zones list from command line
|
|
7
6
|
* Supports multiple formats:
|
|
@@ -51,12 +50,9 @@ exports.parseZonesList = parseZonesList;
|
|
|
51
50
|
* - Comma-separated list: 'key1=value1,key2=value2' or 'key1:value1,key2:value2'
|
|
52
51
|
* - Multiple --options flags: later values override earlier ones
|
|
53
52
|
*
|
|
54
|
-
* Note: All values are returned as strings. Use normalizeLayoutOptions() to convert
|
|
55
|
-
* boolean options to actual boolean types based on the layout definition.
|
|
56
|
-
*
|
|
57
53
|
* @param value Current value being processed
|
|
58
54
|
* @param previous Previously processed value (used for multiple flags)
|
|
59
|
-
* @returns Record of option names to
|
|
55
|
+
* @returns Record of option names to values
|
|
60
56
|
*/
|
|
61
57
|
function parseOptionsList(value, previous) {
|
|
62
58
|
const result = Object.assign({}, previous);
|
|
@@ -86,35 +82,3 @@ function parseOptionsList(value, previous) {
|
|
|
86
82
|
return result;
|
|
87
83
|
}
|
|
88
84
|
exports.parseOptionsList = parseOptionsList;
|
|
89
|
-
/**
|
|
90
|
-
* Normalizes layout options by converting string boolean values to actual booleans
|
|
91
|
-
* based on the layout definition's option types.
|
|
92
|
-
*
|
|
93
|
-
* @param options The parsed options from CLI (all strings)
|
|
94
|
-
* @param layoutDefinition The layout definition with option type information
|
|
95
|
-
* @returns Options with boolean values converted to actual booleans
|
|
96
|
-
*/
|
|
97
|
-
function normalizeLayoutOptions(options, layoutDefinition) {
|
|
98
|
-
const normalized = Object.assign({}, options);
|
|
99
|
-
if (!layoutDefinition.options) {
|
|
100
|
-
return normalized;
|
|
101
|
-
}
|
|
102
|
-
// Convert string "true"/"false" to actual booleans for boolean-type options
|
|
103
|
-
for (const [key, optionDef] of Object.entries(layoutDefinition.options)) {
|
|
104
|
-
if (optionDef.valueType === render_1.LayoutOptionValueType.boolean &&
|
|
105
|
-
key in normalized) {
|
|
106
|
-
const value = normalized[key];
|
|
107
|
-
if (value === 'true') {
|
|
108
|
-
normalized[key] = true;
|
|
109
|
-
}
|
|
110
|
-
else if (value === 'false') {
|
|
111
|
-
normalized[key] = false;
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
throw new Error(`Invalid boolean value "${value}" for option "${key}". Must be "true" or "false".`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return normalized;
|
|
119
|
-
}
|
|
120
|
-
exports.normalizeLayoutOptions = normalizeLayoutOptions;
|
|
@@ -144,141 +144,3 @@ describe('parseOptionsList', () => {
|
|
|
144
144
|
});
|
|
145
145
|
});
|
|
146
146
|
});
|
|
147
|
-
describe('normalizeLayoutOptions', () => {
|
|
148
|
-
it('returns empty object when given no options', () => {
|
|
149
|
-
const result = (0, parse_args_1.normalizeLayoutOptions)({}, {});
|
|
150
|
-
expect(result).toEqual({});
|
|
151
|
-
});
|
|
152
|
-
it('returns options unchanged when layoutDefinition has no options', () => {
|
|
153
|
-
const options = {
|
|
154
|
-
theme: 'dark',
|
|
155
|
-
size: 'large',
|
|
156
|
-
};
|
|
157
|
-
const result = (0, parse_args_1.normalizeLayoutOptions)(options, {});
|
|
158
|
-
expect(result).toEqual(options);
|
|
159
|
-
});
|
|
160
|
-
it('converts string "true" to boolean true for boolean-type options', () => {
|
|
161
|
-
const options = {
|
|
162
|
-
showFooter: 'true',
|
|
163
|
-
};
|
|
164
|
-
const layoutDefinition = {
|
|
165
|
-
options: {
|
|
166
|
-
showFooter: {
|
|
167
|
-
valueType: 'boolean',
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
|
|
172
|
-
expect(result).toEqual({
|
|
173
|
-
showFooter: true,
|
|
174
|
-
});
|
|
175
|
-
expect(typeof result.showFooter).toBe('boolean');
|
|
176
|
-
});
|
|
177
|
-
it('converts string "false" to boolean false for boolean-type options', () => {
|
|
178
|
-
const options = {
|
|
179
|
-
showFooter: 'false',
|
|
180
|
-
};
|
|
181
|
-
const layoutDefinition = {
|
|
182
|
-
options: {
|
|
183
|
-
showFooter: {
|
|
184
|
-
valueType: 'boolean',
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
};
|
|
188
|
-
const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
|
|
189
|
-
expect(result).toEqual({
|
|
190
|
-
showFooter: false,
|
|
191
|
-
});
|
|
192
|
-
expect(typeof result.showFooter).toBe('boolean');
|
|
193
|
-
});
|
|
194
|
-
it('leaves string options unchanged for non-boolean types', () => {
|
|
195
|
-
const options = {
|
|
196
|
-
theme: 'dark',
|
|
197
|
-
customTitle: 'My Title',
|
|
198
|
-
};
|
|
199
|
-
const layoutDefinition = {
|
|
200
|
-
options: {
|
|
201
|
-
theme: {
|
|
202
|
-
valueType: 'string-enum',
|
|
203
|
-
},
|
|
204
|
-
customTitle: {
|
|
205
|
-
valueType: 'text',
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
};
|
|
209
|
-
const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
|
|
210
|
-
expect(result).toEqual(options);
|
|
211
|
-
});
|
|
212
|
-
it('handles mixed boolean and string options', () => {
|
|
213
|
-
const options = {
|
|
214
|
-
showFooter: 'true',
|
|
215
|
-
showHeader: 'false',
|
|
216
|
-
theme: 'dark',
|
|
217
|
-
customTitle: 'Test',
|
|
218
|
-
};
|
|
219
|
-
const layoutDefinition = {
|
|
220
|
-
options: {
|
|
221
|
-
showFooter: { valueType: 'boolean' },
|
|
222
|
-
showHeader: { valueType: 'boolean' },
|
|
223
|
-
theme: { valueType: 'string-enum' },
|
|
224
|
-
customTitle: { valueType: 'text' },
|
|
225
|
-
},
|
|
226
|
-
};
|
|
227
|
-
const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
|
|
228
|
-
expect(result).toEqual({
|
|
229
|
-
showFooter: true,
|
|
230
|
-
showHeader: false,
|
|
231
|
-
theme: 'dark',
|
|
232
|
-
customTitle: 'Test',
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
it('throws error for invalid boolean value', () => {
|
|
236
|
-
const options = {
|
|
237
|
-
showFooter: 'yes',
|
|
238
|
-
};
|
|
239
|
-
const layoutDefinition = {
|
|
240
|
-
options: {
|
|
241
|
-
showFooter: {
|
|
242
|
-
valueType: 'boolean',
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
};
|
|
246
|
-
expect(() => (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition)).toThrow('Invalid boolean value "yes" for option "showFooter". Must be "true" or "false".');
|
|
247
|
-
});
|
|
248
|
-
it('only normalizes options that exist in layoutDefinition', () => {
|
|
249
|
-
const options = {
|
|
250
|
-
showFooter: 'true',
|
|
251
|
-
unknownOption: 'true',
|
|
252
|
-
};
|
|
253
|
-
const layoutDefinition = {
|
|
254
|
-
options: {
|
|
255
|
-
showFooter: {
|
|
256
|
-
valueType: 'boolean',
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
};
|
|
260
|
-
const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
|
|
261
|
-
expect(result).toEqual({
|
|
262
|
-
showFooter: true,
|
|
263
|
-
unknownOption: 'true', // Left as string because not in layoutDefinition
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
it('preserves options not present in layoutDefinition options', () => {
|
|
267
|
-
const options = {
|
|
268
|
-
showFooter: 'true',
|
|
269
|
-
theme: 'dark',
|
|
270
|
-
};
|
|
271
|
-
const layoutDefinition = {
|
|
272
|
-
options: {
|
|
273
|
-
showFooter: {
|
|
274
|
-
valueType: 'boolean',
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
};
|
|
278
|
-
const result = (0, parse_args_1.normalizeLayoutOptions)(options, layoutDefinition);
|
|
279
|
-
expect(result).toEqual({
|
|
280
|
-
showFooter: true,
|
|
281
|
-
theme: 'dark', // Preserved even though not in layoutDefinition
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
});
|
|
@@ -16,29 +16,18 @@ export interface ZoneDefinition {
|
|
|
16
16
|
minNodes?: number;
|
|
17
17
|
maxNodes?: number;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Layout option value types.
|
|
21
|
-
*/
|
|
22
|
-
export declare const LayoutOptionValueType: {
|
|
23
|
-
readonly stringEnum: "string-enum";
|
|
24
|
-
readonly boolean: "boolean";
|
|
25
|
-
readonly text: "text";
|
|
26
|
-
};
|
|
27
|
-
export declare type LayoutOptionValueType = typeof LayoutOptionValueType[keyof typeof LayoutOptionValueType];
|
|
28
|
-
export declare const LayoutOptionValueTypes: readonly ["string-enum", "boolean", "text"];
|
|
29
19
|
export interface OptionDefinition {
|
|
30
20
|
displayName: string;
|
|
31
21
|
description: string;
|
|
32
|
-
|
|
33
|
-
values?: string[];
|
|
22
|
+
values: string[];
|
|
34
23
|
}
|
|
35
24
|
/**
|
|
36
25
|
* Renders a layout using Handlebars templating
|
|
37
26
|
*
|
|
38
27
|
* @param templateContent The Handlebars template string
|
|
39
28
|
* @param zoneContents Content for each zone as single concatenated strings
|
|
40
|
-
* @param layoutOptions Options for the layout
|
|
29
|
+
* @param layoutOptions Options for the layout
|
|
41
30
|
* @param layoutDefinition Optional layout definition object to pass to the template
|
|
42
31
|
* @returns The rendered HTML
|
|
43
32
|
*/
|
|
44
|
-
export declare function renderLayout(templateContent: string, zoneContents: Record<string, string>, layoutOptions: Record<string,
|
|
33
|
+
export declare function renderLayout(templateContent: string, zoneContents: Record<string, string>, layoutOptions: Record<string, any>, layoutDefinition?: ExtendedLayoutDefinition): Promise<string>;
|
package/lib/page/utils/render.js
CHANGED
|
@@ -12,27 +12,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.renderLayout =
|
|
15
|
+
exports.renderLayout = void 0;
|
|
16
16
|
const handlebars_1 = __importDefault(require("handlebars"));
|
|
17
|
-
/**
|
|
18
|
-
* Layout option value types.
|
|
19
|
-
*/
|
|
20
|
-
exports.LayoutOptionValueType = {
|
|
21
|
-
stringEnum: 'string-enum',
|
|
22
|
-
boolean: 'boolean',
|
|
23
|
-
text: 'text',
|
|
24
|
-
};
|
|
25
|
-
exports.LayoutOptionValueTypes = [
|
|
26
|
-
exports.LayoutOptionValueType.stringEnum,
|
|
27
|
-
exports.LayoutOptionValueType.boolean,
|
|
28
|
-
exports.LayoutOptionValueType.text,
|
|
29
|
-
];
|
|
30
17
|
/**
|
|
31
18
|
* Renders a layout using Handlebars templating
|
|
32
19
|
*
|
|
33
20
|
* @param templateContent The Handlebars template string
|
|
34
21
|
* @param zoneContents Content for each zone as single concatenated strings
|
|
35
|
-
* @param layoutOptions Options for the layout
|
|
22
|
+
* @param layoutOptions Options for the layout
|
|
36
23
|
* @param layoutDefinition Optional layout definition object to pass to the template
|
|
37
24
|
* @returns The rendered HTML
|
|
38
25
|
*/
|
|
@@ -68,7 +68,7 @@ describe('renderLayout', () => {
|
|
|
68
68
|
main: '<p>Content</p>',
|
|
69
69
|
};
|
|
70
70
|
const layoutOptions = {
|
|
71
|
-
theme: 'dark',
|
|
71
|
+
theme: { selectedValue: 'dark' },
|
|
72
72
|
};
|
|
73
73
|
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents, layoutOptions);
|
|
74
74
|
expect(result).toContain('<div class="theme-dark">');
|
|
@@ -3,7 +3,7 @@ interface DevServerOptions {
|
|
|
3
3
|
configPath: string;
|
|
4
4
|
layoutDefinition: ExtendedLayoutDefinition;
|
|
5
5
|
zoneContent: Record<string, string[]>;
|
|
6
|
-
layoutOptions: Record<string, string
|
|
6
|
+
layoutOptions: Record<string, string>;
|
|
7
7
|
stylesheet?: string;
|
|
8
8
|
port: number;
|
|
9
9
|
openBrowser: boolean;
|