@movable/rollup-plugin-manifest-validator 3.2.0-app-validation
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/.mocharc.yml +3 -0
- package/babel.config.json +11 -0
- package/dist/apps/index.js +15 -0
- package/dist/apps/plugins/app-manifest-validator.js +44 -0
- package/dist/apps/utils/app-manifest-validator.js +44 -0
- package/dist/apps/validators/ajvCompiler.js +43 -0
- package/dist/apps/validators/erroringValidator.js +16 -0
- package/dist/apps/validators/index.js +23 -0
- package/dist/apps/validators/warningValidator.js +16 -0
- package/dist/index.js +21 -0
- package/dist/packages/index.js +15 -0
- package/dist/packages/plugins/package-manifest-validator.js +44 -0
- package/dist/packages/utils/package-manifest-validator.js +44 -0
- package/dist/packages/validators/ajvCompiler.js +43 -0
- package/dist/packages/validators/erroringValidator.js +16 -0
- package/dist/packages/validators/index.js +23 -0
- package/dist/packages/validators/warningValidator.js +16 -0
- package/dist/schemas/enums.js +14 -0
- package/dist/schemas/erroringSchemaApps.js +809 -0
- package/dist/schemas/erroringSchemaPackages.js +17 -0
- package/dist/schemas/index.js +39 -0
- package/dist/schemas/warningSchemaApps.js +8 -0
- package/dist/schemas/warningSchemaPackages.js +504 -0
- package/package.json +42 -0
- package/src/apps/index.js +3 -0
- package/src/apps/plugins/app-manifest-validator.js +22 -0
- package/src/apps/utils/app-manifest-validator.js +31 -0
- package/src/apps/validators/ajvCompiler.js +28 -0
- package/src/apps/validators/erroringValidator.js +4 -0
- package/src/apps/validators/index.js +4 -0
- package/src/apps/validators/warningValidator.js +4 -0
- package/src/index.js +4 -0
- package/src/packages/index.js +3 -0
- package/src/packages/plugins/package-manifest-validator.js +19 -0
- package/src/packages/utils/package-manifest-validator.js +31 -0
- package/src/packages/validators/ajvCompiler.js +28 -0
- package/src/packages/validators/erroringValidator.js +4 -0
- package/src/packages/validators/index.js +4 -0
- package/src/packages/validators/warningValidator.js +4 -0
- package/src/schemas/enums.js +130 -0
- package/src/schemas/erroringSchemaApps.js +468 -0
- package/src/schemas/erroringSchemaPackages.js +8 -0
- package/src/schemas/index.js +6 -0
- package/src/schemas/warningSchemaApps.js +1 -0
- package/src/schemas/warningSchemaPackages.js +304 -0
- package/test/fixtures/blankFile.js +0 -0
- package/test/fixtures/invalidField.yml +3 -0
- package/test/fixtures/master-app-manifest.yml +72 -0
- package/test/fixtures/master-package-manifest.yml +100 -0
- package/test/fixtures/noName.yml +1 -0
- package/test/helpers/lib.js +10 -0
- package/test/plugins/app-manifest-validation.js +34 -0
- package/test/plugins/package-manifest-validation.js +62 -0
- package/test/utils/app-manifest-validation.js +785 -0
- package/test/utils/package-manifest-validation.js +1515 -0
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
import YAML from 'yamljs';
|
|
2
|
+
import { describe, it } from 'mocha';
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
5
|
+
import manifestValidator from '../../src/apps/utils/app-manifest-validator';
|
|
6
|
+
import { fieldTypes } from '../../src/schemas/enums';
|
|
7
|
+
import { MANIFEST_PATH_APPS, ALLOWED_VALUES_MESSAGE } from '../helpers/lib';
|
|
8
|
+
|
|
9
|
+
const parseYAML = (filePath) => YAML.parse(readFileSync(filePath).toString('utf-8'));
|
|
10
|
+
|
|
11
|
+
describe('appManifestValidator utility', async () => {
|
|
12
|
+
it('can validate the master manifest', async () => {
|
|
13
|
+
const masterManifest = parseYAML(MANIFEST_PATH_APPS);
|
|
14
|
+
|
|
15
|
+
const { warnings, errors } = manifestValidator(masterManifest);
|
|
16
|
+
|
|
17
|
+
expect(warnings.length).to.eq(0, 'a valid manifest has no warnings');
|
|
18
|
+
expect(errors.length).to.eq(0, 'a valid manifest thows no errors');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('field', async () => {
|
|
22
|
+
it('cannot have properties that are not allowed and must have required properties', async () => {
|
|
23
|
+
const manifest = {
|
|
24
|
+
name: 'test',
|
|
25
|
+
fields: [
|
|
26
|
+
{
|
|
27
|
+
name: 'test-field',
|
|
28
|
+
label: 'Test Field',
|
|
29
|
+
type: 'text',
|
|
30
|
+
cat: 'dog',
|
|
31
|
+
placeholder: 'Test Field Placeholder'
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
};
|
|
35
|
+
const { errors } = manifestValidator(manifest);
|
|
36
|
+
const params = { additional: [], missing: [] };
|
|
37
|
+
errors.forEach((e) => {
|
|
38
|
+
const missing = e.params.missingProperty;
|
|
39
|
+
if (missing) return params.missing.push(missing);
|
|
40
|
+
const additional = e.params.additionalProperty;
|
|
41
|
+
if (additional) params.additional.push(additional);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(params.additional).to.have.members(
|
|
45
|
+
['cat', 'cat', 'placeholder'],
|
|
46
|
+
'should NOT have additional properties'
|
|
47
|
+
);
|
|
48
|
+
expect(params.missing).to.have.members(['authors'], 'should have required property');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('types must be correct', async () => {
|
|
52
|
+
const manifest = {
|
|
53
|
+
name: 'test',
|
|
54
|
+
fields: [
|
|
55
|
+
{
|
|
56
|
+
type: 1,
|
|
57
|
+
allows_dynamic: 1,
|
|
58
|
+
default: [],
|
|
59
|
+
description: 1,
|
|
60
|
+
label: 1,
|
|
61
|
+
max: 'one',
|
|
62
|
+
name: 1,
|
|
63
|
+
options: [1],
|
|
64
|
+
placeholder: [],
|
|
65
|
+
preview_text: 1,
|
|
66
|
+
provider: 1,
|
|
67
|
+
template: 1,
|
|
68
|
+
value: [],
|
|
69
|
+
heading: 1
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
};
|
|
73
|
+
const { errors } = manifestValidator(manifest);
|
|
74
|
+
|
|
75
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
76
|
+
const { type } = w.params;
|
|
77
|
+
if (type) {
|
|
78
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
79
|
+
acc[nameFromPath] = type;
|
|
80
|
+
}
|
|
81
|
+
return acc;
|
|
82
|
+
}, {});
|
|
83
|
+
|
|
84
|
+
expect(1).to.equal(1);
|
|
85
|
+
|
|
86
|
+
expect(typeLookup).to.deep.eq(
|
|
87
|
+
{
|
|
88
|
+
name: 'string',
|
|
89
|
+
type: 'string',
|
|
90
|
+
label: 'string',
|
|
91
|
+
placeholder: 'string,number',
|
|
92
|
+
provider: 'string',
|
|
93
|
+
max: 'number',
|
|
94
|
+
description: 'string',
|
|
95
|
+
template: 'string',
|
|
96
|
+
heading: 'string',
|
|
97
|
+
value: 'string,number,boolean'
|
|
98
|
+
},
|
|
99
|
+
'types must be correct'
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('only allows valid field types', async () => {
|
|
104
|
+
const manifest = {
|
|
105
|
+
name: 'test',
|
|
106
|
+
authors: ['test'],
|
|
107
|
+
fields: [
|
|
108
|
+
{
|
|
109
|
+
name: 'test',
|
|
110
|
+
label: 'test',
|
|
111
|
+
type: 'dog'
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
const {
|
|
116
|
+
errors: [error]
|
|
117
|
+
} = manifestValidator(manifest);
|
|
118
|
+
|
|
119
|
+
expect(error.message).to.eq(ALLOWED_VALUES_MESSAGE, 'fields may only have valid types');
|
|
120
|
+
expect(error.params.allowedValues).to.have.members(
|
|
121
|
+
fieldTypes,
|
|
122
|
+
'valid field types are correct'
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('nonoption fields may not have an options property', async () => {
|
|
127
|
+
['select', 'radio', 'checkbox'].forEach((type) => {
|
|
128
|
+
const manifest = {
|
|
129
|
+
name: 'test',
|
|
130
|
+
authors: ['test'],
|
|
131
|
+
fields: [
|
|
132
|
+
{
|
|
133
|
+
name: 'test',
|
|
134
|
+
label: 'test',
|
|
135
|
+
type: type
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
};
|
|
139
|
+
const { errors } = manifestValidator(manifest);
|
|
140
|
+
|
|
141
|
+
expect(errors.length).to.eq(0, `${type} type fields may have the options`);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('studio_options', async () => {
|
|
147
|
+
it('cannot have properties that are not allowed', async () => {
|
|
148
|
+
const manifest = {
|
|
149
|
+
authors: ['test'],
|
|
150
|
+
name: 'test',
|
|
151
|
+
studio_options: {
|
|
152
|
+
cat: 'dog'
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const { errors } = manifestValidator(manifest);
|
|
156
|
+
|
|
157
|
+
expect(errors[0]).to.deep.eq(
|
|
158
|
+
{
|
|
159
|
+
message: 'should NOT have additional properties',
|
|
160
|
+
dataPath: '.studio_options',
|
|
161
|
+
params: { additionalProperty: 'cat' }
|
|
162
|
+
},
|
|
163
|
+
'extra properties are not allowed'
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('types must be correct', async () => {
|
|
168
|
+
const manifest = {
|
|
169
|
+
authors: ['test'],
|
|
170
|
+
name: 1,
|
|
171
|
+
studio_options: {
|
|
172
|
+
prohibit_custom_params: 1,
|
|
173
|
+
framework_version: 1,
|
|
174
|
+
tools: [1],
|
|
175
|
+
preview_fields: [1],
|
|
176
|
+
property_groups: [1],
|
|
177
|
+
element_modifiers: [1]
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
const { errors } = manifestValidator(manifest);
|
|
181
|
+
|
|
182
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
183
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
184
|
+
acc[nameFromPath] = w.params.type;
|
|
185
|
+
return acc;
|
|
186
|
+
}, {});
|
|
187
|
+
|
|
188
|
+
expect(typeLookup).to.deep.eq(
|
|
189
|
+
{
|
|
190
|
+
name: 'string',
|
|
191
|
+
prohibit_custom_params: 'boolean',
|
|
192
|
+
'tools[0]': 'object',
|
|
193
|
+
'preview_fields[0]': undefined,
|
|
194
|
+
'element_modifiers[0]': 'object',
|
|
195
|
+
'property_groups[0]': 'object'
|
|
196
|
+
},
|
|
197
|
+
'types must be correct'
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('property_group', async () => {
|
|
203
|
+
it('cannot have properties that are not allowed and must have required properties', async () => {
|
|
204
|
+
const manifest = {
|
|
205
|
+
authors: ['test'],
|
|
206
|
+
name: 'test',
|
|
207
|
+
studio_options: {
|
|
208
|
+
property_groups: [
|
|
209
|
+
{
|
|
210
|
+
cat: 'dog'
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const { errors } = manifestValidator(manifest);
|
|
216
|
+
|
|
217
|
+
const params = { additional: [], missing: [] };
|
|
218
|
+
errors.forEach((w) => {
|
|
219
|
+
const missing = w.params.missingProperty;
|
|
220
|
+
if (missing) return params.missing.push(missing);
|
|
221
|
+
params.additional.push(w.params.additionalProperty);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
expect(params.missing).to.have.members(
|
|
225
|
+
['name', 'label', 'properties'],
|
|
226
|
+
'must have required properties'
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('types must be correct', async () => {
|
|
231
|
+
const manifest = {
|
|
232
|
+
name: 'test',
|
|
233
|
+
studio_options: {
|
|
234
|
+
property_groups: [
|
|
235
|
+
{
|
|
236
|
+
name: 'test', // name tested separately
|
|
237
|
+
label: 1,
|
|
238
|
+
description: 1,
|
|
239
|
+
properties: [1]
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const { errors } = manifestValidator(manifest);
|
|
245
|
+
|
|
246
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
247
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
248
|
+
acc[nameFromPath] = w.params.type;
|
|
249
|
+
return acc;
|
|
250
|
+
}, {});
|
|
251
|
+
|
|
252
|
+
expect(typeLookup).to.deep.eq(
|
|
253
|
+
{
|
|
254
|
+
yml: undefined,
|
|
255
|
+
label: 'string',
|
|
256
|
+
description: 'string',
|
|
257
|
+
'properties[0]': 'object'
|
|
258
|
+
},
|
|
259
|
+
'types must be correct'
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
describe('property', async () => {
|
|
265
|
+
it('cannot have properties that are not allowed and must have required properties', async () => {
|
|
266
|
+
const manifest = {
|
|
267
|
+
name: 'test',
|
|
268
|
+
authors: ['test'],
|
|
269
|
+
studio_options: {
|
|
270
|
+
property_groups: [
|
|
271
|
+
{
|
|
272
|
+
name: 'test',
|
|
273
|
+
label: 'test',
|
|
274
|
+
properties: [
|
|
275
|
+
{
|
|
276
|
+
cat: 'dog'
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
const { errors } = manifestValidator(manifest);
|
|
284
|
+
|
|
285
|
+
const params = { additional: [], missing: [] };
|
|
286
|
+
errors.forEach((w) => {
|
|
287
|
+
const missing = w.params.missingProperty;
|
|
288
|
+
if (missing) return params.missing.push(missing);
|
|
289
|
+
params.additional.push(w.params.additionalProperty);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
expect(params.missing).to.have.members(['name', 'label'], 'must have required properties');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('types must be correct', async () => {
|
|
296
|
+
const manifest = {
|
|
297
|
+
name: 'test',
|
|
298
|
+
studio_options: {
|
|
299
|
+
property_groups: [
|
|
300
|
+
{
|
|
301
|
+
name: 'test',
|
|
302
|
+
label: 'test',
|
|
303
|
+
properties: [
|
|
304
|
+
{
|
|
305
|
+
name: 1,
|
|
306
|
+
type: 'text', // type tested separately
|
|
307
|
+
label: 1,
|
|
308
|
+
description: 1,
|
|
309
|
+
preview_text: [{}],
|
|
310
|
+
preview_values: [{}],
|
|
311
|
+
context_options: [1]
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
const { errors } = manifestValidator(manifest);
|
|
319
|
+
|
|
320
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
321
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
322
|
+
acc[nameFromPath] = w.params.type;
|
|
323
|
+
return acc;
|
|
324
|
+
}, {});
|
|
325
|
+
|
|
326
|
+
expect(typeLookup).to.deep.eq(
|
|
327
|
+
{
|
|
328
|
+
yml: undefined,
|
|
329
|
+
name: 'string',
|
|
330
|
+
label: 'string',
|
|
331
|
+
'context_options[0]': 'object',
|
|
332
|
+
'preview_values[0]': 'string,number,boolean'
|
|
333
|
+
},
|
|
334
|
+
'types must be correct'
|
|
335
|
+
);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('type property must be an allowed value', async () => {
|
|
339
|
+
const manifest = {
|
|
340
|
+
name: 'test',
|
|
341
|
+
authors: ['test'],
|
|
342
|
+
studio_options: {
|
|
343
|
+
property_groups: [
|
|
344
|
+
{
|
|
345
|
+
name: 'test',
|
|
346
|
+
label: 'test',
|
|
347
|
+
properties: [
|
|
348
|
+
{
|
|
349
|
+
name: 'test',
|
|
350
|
+
type: 'dog',
|
|
351
|
+
label: 'test'
|
|
352
|
+
}
|
|
353
|
+
]
|
|
354
|
+
}
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
const { errors } = manifestValidator(manifest);
|
|
359
|
+
|
|
360
|
+
expect(errors[0].message).to.eq(
|
|
361
|
+
'should be equal to one of the allowed values',
|
|
362
|
+
'type property must match an allowed value'
|
|
363
|
+
);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe('context_options', async () => {
|
|
367
|
+
it('cannot have properties that are not allowed and must have required properties', async () => {
|
|
368
|
+
const manifest = {
|
|
369
|
+
name: 'test',
|
|
370
|
+
authors: ['test'],
|
|
371
|
+
studio_options: {
|
|
372
|
+
property_groups: [
|
|
373
|
+
{
|
|
374
|
+
name: 'test',
|
|
375
|
+
label: 'test',
|
|
376
|
+
properties: [
|
|
377
|
+
{
|
|
378
|
+
name: 'test',
|
|
379
|
+
type: 'text',
|
|
380
|
+
label: 'test',
|
|
381
|
+
context_options: [
|
|
382
|
+
{
|
|
383
|
+
cat: 'dog',
|
|
384
|
+
type: 'text' // type tested separately
|
|
385
|
+
}
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
const { errors } = manifestValidator(manifest);
|
|
394
|
+
|
|
395
|
+
const params = { additional: [], missing: [] };
|
|
396
|
+
errors.forEach((w) => {
|
|
397
|
+
const missing = w.params.missingProperty;
|
|
398
|
+
if (missing) return params.missing.push(missing);
|
|
399
|
+
params.additional.push(w.params.additionalProperty);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
expect(params.missing).to.have.members(['name'], 'must have required properties');
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('types must be correct', async () => {
|
|
406
|
+
const manifest = {
|
|
407
|
+
name: 'test',
|
|
408
|
+
authors: ['test'],
|
|
409
|
+
studio_options: {
|
|
410
|
+
property_groups: [
|
|
411
|
+
{
|
|
412
|
+
name: 'test',
|
|
413
|
+
label: 'test',
|
|
414
|
+
properties: [
|
|
415
|
+
{
|
|
416
|
+
name: 'test',
|
|
417
|
+
label: 'test',
|
|
418
|
+
context_options: [
|
|
419
|
+
{
|
|
420
|
+
name: 1,
|
|
421
|
+
type: 'select',
|
|
422
|
+
description: 1,
|
|
423
|
+
defaultValue: 'one',
|
|
424
|
+
options: [
|
|
425
|
+
{
|
|
426
|
+
label: 1,
|
|
427
|
+
value: {}
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
]
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
const { errors } = manifestValidator(manifest);
|
|
439
|
+
|
|
440
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
441
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
442
|
+
acc[nameFromPath] = w.params.type;
|
|
443
|
+
return acc;
|
|
444
|
+
}, {});
|
|
445
|
+
|
|
446
|
+
expect(typeLookup).to.deep.eq(
|
|
447
|
+
{
|
|
448
|
+
name: 'string',
|
|
449
|
+
description: 'string',
|
|
450
|
+
value: 'string,number,boolean',
|
|
451
|
+
label: 'string'
|
|
452
|
+
},
|
|
453
|
+
'types must be correct'
|
|
454
|
+
);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('options cannot have have properties that are not allowed and must have required properties', async () => {
|
|
458
|
+
const manifest = {
|
|
459
|
+
name: 'test',
|
|
460
|
+
authors: ['test'],
|
|
461
|
+
studio_options: {
|
|
462
|
+
property_groups: [
|
|
463
|
+
{
|
|
464
|
+
name: 'test',
|
|
465
|
+
label: 'test',
|
|
466
|
+
properties: [
|
|
467
|
+
{
|
|
468
|
+
name: 'test',
|
|
469
|
+
type: 'text',
|
|
470
|
+
label: 'test',
|
|
471
|
+
context_options: [
|
|
472
|
+
{
|
|
473
|
+
name: 'test',
|
|
474
|
+
type: 'select',
|
|
475
|
+
options: [{ cat: 'dog' }]
|
|
476
|
+
}
|
|
477
|
+
]
|
|
478
|
+
}
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
]
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
const { errors } = manifestValidator(manifest);
|
|
485
|
+
|
|
486
|
+
const params = { additional: [], missing: [] };
|
|
487
|
+
errors.forEach((w) => {
|
|
488
|
+
const missing = w.params.missingProperty;
|
|
489
|
+
if (missing) return params.missing.push(missing);
|
|
490
|
+
params.additional.push(w.params.additionalProperty);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
expect(params.missing).to.have.members(['label', 'value'], 'must have required properties');
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
describe('tool', async () => {
|
|
499
|
+
it('cannot have properties that are not allowed and must have required properties', async () => {
|
|
500
|
+
const manifest = {
|
|
501
|
+
authors: ['test'],
|
|
502
|
+
name: 'test',
|
|
503
|
+
studio_options: {
|
|
504
|
+
tools: [
|
|
505
|
+
{
|
|
506
|
+
cat: 'dog'
|
|
507
|
+
}
|
|
508
|
+
]
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
const { errors } = manifestValidator(manifest);
|
|
512
|
+
|
|
513
|
+
const params = { additional: [], missing: [] };
|
|
514
|
+
errors.forEach((w) => {
|
|
515
|
+
const missing = w.params.missingProperty;
|
|
516
|
+
if (missing) return params.missing.push(missing);
|
|
517
|
+
params.additional.push(w.params.additionalProperty);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
expect(params.additional).to.have.members(
|
|
521
|
+
['cat'],
|
|
522
|
+
'cannot have properties that are not allowed'
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
expect(params.missing).to.have.members(
|
|
526
|
+
['icon', 'type', 'name'],
|
|
527
|
+
'must have required properties'
|
|
528
|
+
);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('types must be correct', async () => {
|
|
532
|
+
const manifest = {
|
|
533
|
+
authors: ['test'],
|
|
534
|
+
name: 'test',
|
|
535
|
+
studio_options: {
|
|
536
|
+
tools: [
|
|
537
|
+
{
|
|
538
|
+
class_names: 1,
|
|
539
|
+
fields: 1,
|
|
540
|
+
icon: 'avatar', // icon tested separately
|
|
541
|
+
label: 1,
|
|
542
|
+
name: 1,
|
|
543
|
+
type: 1,
|
|
544
|
+
locked: 'one',
|
|
545
|
+
dynamic_fields: {},
|
|
546
|
+
defaults: []
|
|
547
|
+
}
|
|
548
|
+
]
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
const { errors } = manifestValidator(manifest);
|
|
552
|
+
|
|
553
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
554
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
555
|
+
acc[nameFromPath] = w.params.type;
|
|
556
|
+
return acc;
|
|
557
|
+
}, {});
|
|
558
|
+
|
|
559
|
+
expect(typeLookup).to.deep.eq(
|
|
560
|
+
{
|
|
561
|
+
name: 'string',
|
|
562
|
+
type: 'string',
|
|
563
|
+
class_names: 'string',
|
|
564
|
+
locked: 'boolean',
|
|
565
|
+
fields: 'array',
|
|
566
|
+
dynamic_fields: 'array'
|
|
567
|
+
},
|
|
568
|
+
'types must be correct'
|
|
569
|
+
);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it('icon must be an allowed value', async () => {
|
|
573
|
+
const manifest = {
|
|
574
|
+
authors: ['test'],
|
|
575
|
+
name: 'test',
|
|
576
|
+
studio_options: {
|
|
577
|
+
tools: [
|
|
578
|
+
{
|
|
579
|
+
name: 'test',
|
|
580
|
+
label: 'test',
|
|
581
|
+
icon: 'bananas',
|
|
582
|
+
type: 'current-value'
|
|
583
|
+
}
|
|
584
|
+
]
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
const {
|
|
589
|
+
errors: [{ dataPath, message }]
|
|
590
|
+
} = manifestValidator(manifest);
|
|
591
|
+
|
|
592
|
+
expect({ dataPath, message }).to.deep.equal(
|
|
593
|
+
{
|
|
594
|
+
message: ALLOWED_VALUES_MESSAGE,
|
|
595
|
+
dataPath: '.studio_options.tools[0].icon'
|
|
596
|
+
},
|
|
597
|
+
'icon must be an allowed value'
|
|
598
|
+
);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
describe('dynamic_fields', async () => {
|
|
602
|
+
it('cannot have properties that are not allowed and must have required properties', async () => {
|
|
603
|
+
const manifest = {
|
|
604
|
+
authors: ['test'],
|
|
605
|
+
name: 'test',
|
|
606
|
+
studio_options: {
|
|
607
|
+
tools: [
|
|
608
|
+
{
|
|
609
|
+
name: 'test',
|
|
610
|
+
icon: 'avatar',
|
|
611
|
+
label: 'test',
|
|
612
|
+
type: 'test-value',
|
|
613
|
+
dynamic_fields: [
|
|
614
|
+
{
|
|
615
|
+
type: 'text', // type tested separately
|
|
616
|
+
cat: 'dog'
|
|
617
|
+
}
|
|
618
|
+
]
|
|
619
|
+
}
|
|
620
|
+
]
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
const { errors } = manifestValidator(manifest);
|
|
624
|
+
|
|
625
|
+
const params = { additional: [], missing: [] };
|
|
626
|
+
errors.forEach((w) => {
|
|
627
|
+
const missing = w.params.missingProperty;
|
|
628
|
+
if (missing) return params.missing.push(missing);
|
|
629
|
+
params.additional.push(w.params.additionalProperty);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
expect(params.missing).to.have.members(['name'], 'must have required properties');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('types must be correct', async () => {
|
|
636
|
+
const manifest = {
|
|
637
|
+
name: 'test',
|
|
638
|
+
authors: ['test'],
|
|
639
|
+
studio_options: {
|
|
640
|
+
tools: [
|
|
641
|
+
{
|
|
642
|
+
name: 'test',
|
|
643
|
+
icon: 'avatar',
|
|
644
|
+
label: 'test',
|
|
645
|
+
type: 'test-value',
|
|
646
|
+
dynamic_fields: [
|
|
647
|
+
{
|
|
648
|
+
name: 1,
|
|
649
|
+
label: 1,
|
|
650
|
+
type: 'text', // type tested separately
|
|
651
|
+
allow_static_option: 'one'
|
|
652
|
+
}
|
|
653
|
+
]
|
|
654
|
+
}
|
|
655
|
+
]
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
const { errors } = manifestValidator(manifest);
|
|
659
|
+
|
|
660
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
661
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
662
|
+
acc[nameFromPath] = w.params.type;
|
|
663
|
+
return acc;
|
|
664
|
+
}, {});
|
|
665
|
+
|
|
666
|
+
expect(typeLookup).to.deep.eq(
|
|
667
|
+
{ name: 'string', label: 'string', allow_static_option: 'boolean' },
|
|
668
|
+
'types must be correct'
|
|
669
|
+
);
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
describe('defaults', async () => {
|
|
674
|
+
it('types must be correct', async () => {
|
|
675
|
+
const manifest = {
|
|
676
|
+
name: 'test',
|
|
677
|
+
authors: ['test'],
|
|
678
|
+
studio_options: {
|
|
679
|
+
tools: [
|
|
680
|
+
{
|
|
681
|
+
name: 'test',
|
|
682
|
+
icon: 'avatar',
|
|
683
|
+
label: 'test',
|
|
684
|
+
type: 'test-value',
|
|
685
|
+
defaults: {
|
|
686
|
+
width: 'one',
|
|
687
|
+
height: 'one',
|
|
688
|
+
dynamicProperty: 1
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
]
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
const { errors } = manifestValidator(manifest);
|
|
695
|
+
|
|
696
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
697
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
698
|
+
acc[nameFromPath] = w.params.type;
|
|
699
|
+
return acc;
|
|
700
|
+
}, {});
|
|
701
|
+
|
|
702
|
+
expect(typeLookup).to.deep.eq({ dynamicProperty: 'object' }, 'types must be correct');
|
|
703
|
+
});
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
describe('dynamicProperty', async () => {
|
|
708
|
+
it('cannot have properties that are not allowed and must have required properties', async () => {
|
|
709
|
+
const manifest = {
|
|
710
|
+
name: 'test',
|
|
711
|
+
authors: ['test'],
|
|
712
|
+
studio_options: {
|
|
713
|
+
tools: [
|
|
714
|
+
{
|
|
715
|
+
name: 'test',
|
|
716
|
+
icon: 'avatar',
|
|
717
|
+
label: 'test',
|
|
718
|
+
type: 'test-value',
|
|
719
|
+
defaults: {
|
|
720
|
+
dynamicProperty: {
|
|
721
|
+
cat: 'dog'
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
]
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
const { errors } = manifestValidator(manifest);
|
|
729
|
+
|
|
730
|
+
const params = { additional: [], missing: [] };
|
|
731
|
+
errors.forEach((w) => {
|
|
732
|
+
const missing = w.params.missingProperty;
|
|
733
|
+
if (missing) return params.missing.push(missing);
|
|
734
|
+
params.additional.push(w.params.additionalProperty);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
expect(params.missing).to.have.members(['propertyPath'], 'must have required properties');
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
it('types must be correct', async () => {
|
|
741
|
+
const manifest = {
|
|
742
|
+
name: 'test',
|
|
743
|
+
authors: ['test'],
|
|
744
|
+
studio_options: {
|
|
745
|
+
tools: [
|
|
746
|
+
{
|
|
747
|
+
name: 'test',
|
|
748
|
+
icon: 'avatar',
|
|
749
|
+
label: 'test',
|
|
750
|
+
type: 'test-value',
|
|
751
|
+
defaults: {
|
|
752
|
+
dynamicProperty: {
|
|
753
|
+
propertyPath: 1,
|
|
754
|
+
propertyGroupKey: 1,
|
|
755
|
+
propertyPreview: [],
|
|
756
|
+
propertyFallback: [],
|
|
757
|
+
propertyValue: [],
|
|
758
|
+
context: []
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
]
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
const { errors } = manifestValidator(manifest);
|
|
766
|
+
|
|
767
|
+
const typeLookup = errors.reduce((acc, w) => {
|
|
768
|
+
const nameFromPath = w.dataPath.split('.').pop();
|
|
769
|
+
acc[nameFromPath] = w.params.type;
|
|
770
|
+
return acc;
|
|
771
|
+
}, {});
|
|
772
|
+
|
|
773
|
+
expect(typeLookup).to.deep.eq(
|
|
774
|
+
{
|
|
775
|
+
propertyPath: 'string',
|
|
776
|
+
propertyPreview: 'string',
|
|
777
|
+
propertyFallback: 'string',
|
|
778
|
+
propertyGroupKey: 'string',
|
|
779
|
+
propertyValue: 'string'
|
|
780
|
+
},
|
|
781
|
+
'types must be correct'
|
|
782
|
+
);
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
});
|