docusaurus-plugin-generate-schema-docs 1.8.3 → 1.8.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.
- package/README.md +12 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-not-allof.json +11 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof-multi.json +12 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof.json +30 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-edge-cases.json +24 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-non-object.json +15 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +6 -0
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +6 -0
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +15 -0
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +6 -0
- package/__tests__/components/PropertiesTable.test.js +66 -0
- package/__tests__/components/PropertyRow.test.js +85 -4
- package/__tests__/components/SchemaJsonViewer.test.js +118 -0
- package/__tests__/generateEventDocs.anchor.test.js +1 -1
- package/__tests__/generateEventDocs.nested.test.js +1 -1
- package/__tests__/generateEventDocs.partials.test.js +1 -1
- package/__tests__/generateEventDocs.test.js +506 -1
- package/__tests__/generateEventDocs.versioned.test.js +1 -1
- package/__tests__/helpers/buildExampleFromSchema.test.js +240 -0
- package/__tests__/helpers/constraintSchemaPaths.test.js +208 -0
- package/__tests__/helpers/continuingLinesStyle.test.js +492 -0
- package/__tests__/helpers/example-helper.test.js +12 -0
- package/__tests__/helpers/exampleModel.test.js +209 -0
- package/__tests__/helpers/file-system.test.js +73 -1
- package/__tests__/helpers/getConstraints.test.js +43 -0
- package/__tests__/helpers/mergeSchema.test.js +94 -0
- package/__tests__/helpers/processSchema.test.js +309 -1
- package/__tests__/helpers/schema-doc-template.test.js +54 -0
- package/__tests__/helpers/schema-processing.test.js +122 -2
- package/__tests__/helpers/schemaToExamples.test.js +1007 -0
- package/__tests__/helpers/schemaToTableData.mutations.test.js +970 -0
- package/__tests__/helpers/schemaToTableData.test.js +157 -0
- package/__tests__/helpers/schemaTraversal.test.js +110 -0
- package/__tests__/helpers/snippetTargets.test.js +432 -0
- package/__tests__/helpers/trackingTargets.test.js +319 -0
- package/__tests__/helpers/validator.test.js +385 -1
- package/__tests__/index.test.js +436 -0
- package/__tests__/syncGtm.test.js +366 -6
- package/__tests__/update-schema-ids.test.js +70 -1
- package/__tests__/validateSchemas-integration.test.js +2 -2
- package/__tests__/validateSchemas.test.js +192 -1
- package/components/PropertiesTable.js +32 -2
- package/components/PropertyRow.js +29 -2
- package/components/SchemaJsonViewer.js +234 -131
- package/components/SchemaRows.css +40 -0
- package/components/SchemaViewer.js +11 -2
- package/generateEventDocs.js +21 -1
- package/helpers/constraintSchemaPaths.js +10 -14
- package/helpers/example-helper.js +2 -2
- package/helpers/getConstraints.js +20 -0
- package/helpers/processSchema.js +32 -1
- package/helpers/schema-doc-template.js +4 -0
- package/helpers/schemaToExamples.js +29 -35
- package/helpers/schemaToTableData.js +538 -492
- package/helpers/schemaTraversal.cjs +148 -0
- package/helpers/trackingTargets.js +26 -3
- package/helpers/validator.js +18 -4
- package/index.js +1 -2
- package/package.json +1 -1
- package/scripts/sync-gtm.js +65 -34
- package/test-data/payloadContracts.js +35 -0
- package/validateSchemas.js +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-env jest */
|
|
2
2
|
/**
|
|
3
|
-
* @jest-environment node
|
|
3
|
+
* @jest-environment @stryker-mutator/jest-runner/jest-env/node
|
|
4
4
|
*/
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
@@ -79,7 +79,7 @@ describe('getVariablesFromSchemas', () => {
|
|
|
79
79
|
|
|
80
80
|
const SCHEMA_PATH = '/fake/schemas';
|
|
81
81
|
const mockFiles = {
|
|
82
|
-
[SCHEMA_PATH]: ['complex-event.json', 'components'],
|
|
82
|
+
[SCHEMA_PATH]: ['complex-event.json', 'mobile-event.json', 'components'],
|
|
83
83
|
[path.join(SCHEMA_PATH, 'components')]: ['address.json'],
|
|
84
84
|
};
|
|
85
85
|
const addressSchema = {
|
|
@@ -92,6 +92,7 @@ describe('getVariablesFromSchemas', () => {
|
|
|
92
92
|
};
|
|
93
93
|
const complexEventSchema = {
|
|
94
94
|
title: 'Complex Event',
|
|
95
|
+
'x-tracking-targets': ['web-datalayer-js'],
|
|
95
96
|
type: 'object',
|
|
96
97
|
properties: {
|
|
97
98
|
$schema: { type: 'string', description: 'Should now be included.' },
|
|
@@ -107,12 +108,104 @@ describe('getVariablesFromSchemas', () => {
|
|
|
107
108
|
},
|
|
108
109
|
},
|
|
109
110
|
},
|
|
111
|
+
contact_method: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
oneOf: [
|
|
114
|
+
{
|
|
115
|
+
title: 'Email Contact',
|
|
116
|
+
properties: {
|
|
117
|
+
email: {
|
|
118
|
+
type: 'string',
|
|
119
|
+
description: 'Email address.',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
title: 'Phone Contact',
|
|
125
|
+
properties: {
|
|
126
|
+
phone_number: {
|
|
127
|
+
type: 'string',
|
|
128
|
+
description: 'Phone number.',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
platform: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
description: 'Target platform.',
|
|
137
|
+
},
|
|
110
138
|
timestamp: { type: 'number', description: 'Event timestamp.' },
|
|
111
139
|
},
|
|
140
|
+
if: {
|
|
141
|
+
properties: {
|
|
142
|
+
platform: { const: 'ios' },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
then: {
|
|
146
|
+
properties: {
|
|
147
|
+
att_status: {
|
|
148
|
+
type: 'string',
|
|
149
|
+
description: 'App Tracking Transparency status.',
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
else: {
|
|
154
|
+
properties: {
|
|
155
|
+
ad_personalization_enabled: {
|
|
156
|
+
type: 'boolean',
|
|
157
|
+
description: 'Whether ad personalization is enabled.',
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
const mobileEventSchema = {
|
|
163
|
+
title: 'Mobile Event',
|
|
164
|
+
'x-tracking-targets': ['android-firebase-kotlin-sdk'],
|
|
165
|
+
type: 'object',
|
|
166
|
+
properties: {
|
|
167
|
+
event: { type: 'string', const: 'screen_view' },
|
|
168
|
+
screen_name: { type: 'string', description: 'Screen name.' },
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
const mobileConditionalSchema = {
|
|
172
|
+
title: 'Mobile Conditional Event',
|
|
173
|
+
'x-tracking-targets': ['android-firebase-kotlin-sdk'],
|
|
174
|
+
type: 'object',
|
|
175
|
+
allOf: [
|
|
176
|
+
{
|
|
177
|
+
if: {
|
|
178
|
+
properties: {
|
|
179
|
+
mobile_platform_hint: { const: 'ios' },
|
|
180
|
+
},
|
|
181
|
+
required: ['mobile_platform_hint'],
|
|
182
|
+
},
|
|
183
|
+
then: {
|
|
184
|
+
required: ['att_status'],
|
|
185
|
+
},
|
|
186
|
+
else: {
|
|
187
|
+
required: ['ad_personalization_enabled'],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
properties: {
|
|
192
|
+
event: { type: 'string', const: 'screen_view' },
|
|
193
|
+
mobile_platform_hint: { type: 'string' },
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
const untaggedEventSchema = {
|
|
197
|
+
title: 'Untagged Event',
|
|
198
|
+
type: 'object',
|
|
199
|
+
properties: {
|
|
200
|
+
event: { type: 'string', const: 'legacy_event' },
|
|
201
|
+
legacy_field: { type: 'string', description: 'Legacy field.' },
|
|
202
|
+
},
|
|
112
203
|
};
|
|
113
204
|
const mockFileContents = {
|
|
114
205
|
[path.join(SCHEMA_PATH, 'complex-event.json')]:
|
|
115
206
|
JSON.stringify(complexEventSchema),
|
|
207
|
+
[path.join(SCHEMA_PATH, 'mobile-event.json')]:
|
|
208
|
+
JSON.stringify(mobileEventSchema),
|
|
116
209
|
[path.join(SCHEMA_PATH, 'components', 'address.json')]:
|
|
117
210
|
JSON.stringify(addressSchema),
|
|
118
211
|
};
|
|
@@ -148,7 +241,7 @@ describe('getVariablesFromSchemas', () => {
|
|
|
148
241
|
expect.objectContaining({ name: 'timestamp' }),
|
|
149
242
|
]);
|
|
150
243
|
|
|
151
|
-
expect(result.length).toBe(
|
|
244
|
+
expect(result.length).toBe(14);
|
|
152
245
|
expect(result).toEqual(expected);
|
|
153
246
|
});
|
|
154
247
|
|
|
@@ -172,7 +265,209 @@ describe('getVariablesFromSchemas', () => {
|
|
|
172
265
|
];
|
|
173
266
|
|
|
174
267
|
expect(result.map((r) => r.name)).toEqual(expect.arrayContaining(expected));
|
|
175
|
-
expect(result.length).toBe(
|
|
268
|
+
expect(result.length).toBe(12);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should include variables from oneOf choices and conditional branches', async () => {
|
|
272
|
+
const bundledSchema = JSON.parse(JSON.stringify(complexEventSchema));
|
|
273
|
+
bundledSchema.properties.user_data.properties.addresses.items =
|
|
274
|
+
addressSchema;
|
|
275
|
+
RefParser.bundle.mockResolvedValue(bundledSchema);
|
|
276
|
+
|
|
277
|
+
const result = await gtmScript.getVariablesFromSchemas(SCHEMA_PATH, {});
|
|
278
|
+
const variableNames = result.map((variable) => variable.name);
|
|
279
|
+
|
|
280
|
+
expect(variableNames).toEqual(
|
|
281
|
+
expect.arrayContaining([
|
|
282
|
+
'contact_method.email',
|
|
283
|
+
'contact_method.phone_number',
|
|
284
|
+
'att_status',
|
|
285
|
+
'ad_personalization_enabled',
|
|
286
|
+
]),
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should only include schemas explicitly targeted to web-datalayer-js', async () => {
|
|
291
|
+
const untaggedEventPath = path.join(SCHEMA_PATH, 'legacy-event.json');
|
|
292
|
+
mockFiles[SCHEMA_PATH].push('legacy-event.json');
|
|
293
|
+
mockFileContents[untaggedEventPath] = JSON.stringify(untaggedEventSchema);
|
|
294
|
+
|
|
295
|
+
const bundledWebSchema = JSON.parse(JSON.stringify(complexEventSchema));
|
|
296
|
+
bundledWebSchema.properties.user_data.properties.addresses.items =
|
|
297
|
+
addressSchema;
|
|
298
|
+
|
|
299
|
+
RefParser.bundle.mockImplementation(async (filePath) => {
|
|
300
|
+
if (filePath.endsWith('complex-event.json')) {
|
|
301
|
+
return bundledWebSchema;
|
|
302
|
+
}
|
|
303
|
+
if (filePath.endsWith('mobile-event.json')) {
|
|
304
|
+
return mobileEventSchema;
|
|
305
|
+
}
|
|
306
|
+
if (filePath.endsWith('legacy-event.json')) {
|
|
307
|
+
return untaggedEventSchema;
|
|
308
|
+
}
|
|
309
|
+
if (filePath.endsWith('address.json')) {
|
|
310
|
+
return addressSchema;
|
|
311
|
+
}
|
|
312
|
+
throw new Error(`Unexpected schema file: ${filePath}`);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const result = await gtmScript.getVariablesFromSchemas(SCHEMA_PATH, {});
|
|
316
|
+
|
|
317
|
+
expect(result.map((variable) => variable.name)).toEqual(
|
|
318
|
+
expect.arrayContaining([
|
|
319
|
+
'$schema',
|
|
320
|
+
'event',
|
|
321
|
+
'user_data',
|
|
322
|
+
'user_data.user_id',
|
|
323
|
+
'user_data.addresses',
|
|
324
|
+
'user_data.addresses.0.street',
|
|
325
|
+
'user_data.addresses.0.city',
|
|
326
|
+
'timestamp',
|
|
327
|
+
]),
|
|
328
|
+
);
|
|
329
|
+
expect(result.map((variable) => variable.name)).not.toContain(
|
|
330
|
+
'screen_name',
|
|
331
|
+
);
|
|
332
|
+
expect(result.map((variable) => variable.name)).not.toContain(
|
|
333
|
+
'legacy_field',
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should skip non-web schemas before mergeAllOf can fail on unsupported keywords', async () => {
|
|
338
|
+
const mobileConditionalPath = path.join(
|
|
339
|
+
SCHEMA_PATH,
|
|
340
|
+
'mobile-conditional-event.json',
|
|
341
|
+
);
|
|
342
|
+
mockFiles[SCHEMA_PATH].push('mobile-conditional-event.json');
|
|
343
|
+
mockFileContents[mobileConditionalPath] = JSON.stringify(
|
|
344
|
+
mobileConditionalSchema,
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
const bundledWebSchema = JSON.parse(JSON.stringify(complexEventSchema));
|
|
348
|
+
bundledWebSchema.properties.user_data.properties.addresses.items =
|
|
349
|
+
addressSchema;
|
|
350
|
+
const loggerErrorSpy = jest
|
|
351
|
+
.spyOn(console, 'error')
|
|
352
|
+
.mockImplementation(() => {});
|
|
353
|
+
|
|
354
|
+
RefParser.bundle.mockImplementation(async (filePath) => {
|
|
355
|
+
if (filePath.endsWith('complex-event.json')) {
|
|
356
|
+
return bundledWebSchema;
|
|
357
|
+
}
|
|
358
|
+
if (filePath.endsWith('mobile-event.json')) {
|
|
359
|
+
return mobileEventSchema;
|
|
360
|
+
}
|
|
361
|
+
if (filePath.endsWith('mobile-conditional-event.json')) {
|
|
362
|
+
return mobileConditionalSchema;
|
|
363
|
+
}
|
|
364
|
+
if (filePath.endsWith('address.json')) {
|
|
365
|
+
return addressSchema;
|
|
366
|
+
}
|
|
367
|
+
throw new Error(`Unexpected schema file: ${filePath}`);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const result = await gtmScript.getVariablesFromSchemas(SCHEMA_PATH, {});
|
|
371
|
+
|
|
372
|
+
expect(result.map((variable) => variable.name)).not.toContain(
|
|
373
|
+
'mobile_platform_hint',
|
|
374
|
+
);
|
|
375
|
+
expect(loggerErrorSpy).not.toHaveBeenCalledWith(
|
|
376
|
+
expect.stringContaining(
|
|
377
|
+
`Error processing schema ${mobileConditionalPath}`,
|
|
378
|
+
),
|
|
379
|
+
expect.any(Error),
|
|
380
|
+
);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should include root tracking schemas based on content instead of path names', async () => {
|
|
384
|
+
const nestedSchemaDir = path.join(SCHEMA_PATH, 'event-components-demo');
|
|
385
|
+
const nestedSchemaPath = path.join(nestedSchemaDir, 'checkout-event.json');
|
|
386
|
+
const nestedSchema = {
|
|
387
|
+
title: 'Checkout Event',
|
|
388
|
+
'x-tracking-targets': ['web-datalayer-js'],
|
|
389
|
+
type: 'object',
|
|
390
|
+
properties: {
|
|
391
|
+
event: { type: 'string', const: 'checkout' },
|
|
392
|
+
order_id: { type: 'string', description: 'Order identifier.' },
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
mockFiles[SCHEMA_PATH].push('event-components-demo');
|
|
397
|
+
mockFiles[nestedSchemaDir] = ['checkout-event.json'];
|
|
398
|
+
mockFileContents[nestedSchemaPath] = JSON.stringify(nestedSchema);
|
|
399
|
+
|
|
400
|
+
const bundledWebSchema = JSON.parse(JSON.stringify(complexEventSchema));
|
|
401
|
+
bundledWebSchema.properties.user_data.properties.addresses.items =
|
|
402
|
+
addressSchema;
|
|
403
|
+
|
|
404
|
+
RefParser.bundle.mockImplementation(async (filePath) => {
|
|
405
|
+
if (filePath.endsWith('complex-event.json')) {
|
|
406
|
+
return bundledWebSchema;
|
|
407
|
+
}
|
|
408
|
+
if (filePath.endsWith('mobile-event.json')) {
|
|
409
|
+
return mobileEventSchema;
|
|
410
|
+
}
|
|
411
|
+
if (filePath.endsWith('address.json')) {
|
|
412
|
+
return addressSchema;
|
|
413
|
+
}
|
|
414
|
+
if (filePath.endsWith('legacy-event.json')) {
|
|
415
|
+
return untaggedEventSchema;
|
|
416
|
+
}
|
|
417
|
+
if (filePath.endsWith('checkout-event.json')) {
|
|
418
|
+
return nestedSchema;
|
|
419
|
+
}
|
|
420
|
+
throw new Error(`Unexpected schema file: ${filePath}`);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
const result = await gtmScript.getVariablesFromSchemas(SCHEMA_PATH, {});
|
|
424
|
+
const variableNames = result.map((variable) => variable.name);
|
|
425
|
+
|
|
426
|
+
expect(variableNames).toContain('order_id');
|
|
427
|
+
expect(RefParser.bundle).toHaveBeenCalledWith(nestedSchemaPath);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should ignore component schemas even when scanning all json files', async () => {
|
|
431
|
+
const bundledWebSchema = JSON.parse(JSON.stringify(complexEventSchema));
|
|
432
|
+
bundledWebSchema.properties.user_data.properties.addresses.items =
|
|
433
|
+
addressSchema;
|
|
434
|
+
const nestedSchema = {
|
|
435
|
+
title: 'Checkout Event',
|
|
436
|
+
'x-tracking-targets': ['web-datalayer-js'],
|
|
437
|
+
type: 'object',
|
|
438
|
+
properties: {
|
|
439
|
+
event: { type: 'string', const: 'checkout' },
|
|
440
|
+
order_id: { type: 'string', description: 'Order identifier.' },
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
RefParser.bundle.mockImplementation(async (filePath) => {
|
|
445
|
+
if (filePath.endsWith('complex-event.json')) {
|
|
446
|
+
return bundledWebSchema;
|
|
447
|
+
}
|
|
448
|
+
if (filePath.endsWith('mobile-event.json')) {
|
|
449
|
+
return mobileEventSchema;
|
|
450
|
+
}
|
|
451
|
+
if (filePath.endsWith('address.json')) {
|
|
452
|
+
return addressSchema;
|
|
453
|
+
}
|
|
454
|
+
if (filePath.endsWith('legacy-event.json')) {
|
|
455
|
+
return untaggedEventSchema;
|
|
456
|
+
}
|
|
457
|
+
if (filePath.endsWith('checkout-event.json')) {
|
|
458
|
+
return nestedSchema;
|
|
459
|
+
}
|
|
460
|
+
throw new Error(`Unexpected schema file: ${filePath}`);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
const result = await gtmScript.getVariablesFromSchemas(SCHEMA_PATH, {});
|
|
464
|
+
const variableNames = result.map((variable) => variable.name);
|
|
465
|
+
|
|
466
|
+
expect(RefParser.bundle).toHaveBeenCalledWith(
|
|
467
|
+
path.join(SCHEMA_PATH, 'components', 'address.json'),
|
|
468
|
+
);
|
|
469
|
+
expect(variableNames).not.toContain('street');
|
|
470
|
+
expect(variableNames).not.toContain('city');
|
|
176
471
|
});
|
|
177
472
|
});
|
|
178
473
|
|
|
@@ -270,18 +565,61 @@ describe('GTM Synchronization Logic', () => {
|
|
|
270
565
|
});
|
|
271
566
|
|
|
272
567
|
describe('deleteGtmVariables', () => {
|
|
273
|
-
it('should
|
|
568
|
+
it('should delete variables and return their names', () => {
|
|
274
569
|
const toDelete = gtmScript.getVariablesToDelete(
|
|
275
570
|
schemaVariables,
|
|
276
571
|
gtmVariables,
|
|
277
572
|
);
|
|
278
|
-
const deleted = gtmScript.deleteGtmVariables(toDelete);
|
|
573
|
+
const { deleted, failedDeletes } = gtmScript.deleteGtmVariables(toDelete);
|
|
279
574
|
expect(execSync).toHaveBeenCalledTimes(1);
|
|
280
575
|
expect(execSync).toHaveBeenCalledWith(
|
|
281
576
|
'gtm variables delete --variable-id 123 --force --quiet',
|
|
282
577
|
{ stdio: 'inherit' },
|
|
283
578
|
);
|
|
284
579
|
expect(deleted).toEqual(['old_variable']);
|
|
580
|
+
expect(failedDeletes).toEqual([]);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it('should capture failed deletes when GTM rejects the deletion', () => {
|
|
584
|
+
execSync.mockImplementation(() => {
|
|
585
|
+
throw new Error('Returned an error response for your request.');
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
const toDelete = gtmScript.getVariablesToDelete(
|
|
589
|
+
schemaVariables,
|
|
590
|
+
gtmVariables,
|
|
591
|
+
);
|
|
592
|
+
const { deleted, failedDeletes } = gtmScript.deleteGtmVariables(toDelete);
|
|
593
|
+
|
|
594
|
+
expect(deleted).toEqual([]);
|
|
595
|
+
expect(failedDeletes).toEqual([
|
|
596
|
+
{ name: 'old_variable', variableId: '123' },
|
|
597
|
+
]);
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
describe('syncGtmVariables', () => {
|
|
602
|
+
it('should report failed deletes when GTM rejects a deletion', async () => {
|
|
603
|
+
const syncedSchemaVariables = [
|
|
604
|
+
{ name: 'event', description: 'The event name.' },
|
|
605
|
+
];
|
|
606
|
+
|
|
607
|
+
execSync.mockImplementation((command) => {
|
|
608
|
+
if (command === 'gtm variables list -o json --quiet') {
|
|
609
|
+
return Buffer.from(JSON.stringify(gtmVariables));
|
|
610
|
+
}
|
|
611
|
+
if (command.startsWith('gtm variables delete')) {
|
|
612
|
+
throw new Error('Returned an error response for your request.');
|
|
613
|
+
}
|
|
614
|
+
throw new Error(`Unexpected command: ${command}`);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
const summary = await gtmScript.syncGtmVariables(syncedSchemaVariables);
|
|
618
|
+
|
|
619
|
+
expect(summary.deleted).toEqual([]);
|
|
620
|
+
expect(summary.failedDeletes).toEqual([
|
|
621
|
+
{ name: 'old_variable', variableId: '123' },
|
|
622
|
+
]);
|
|
285
623
|
});
|
|
286
624
|
});
|
|
287
625
|
});
|
|
@@ -301,6 +639,12 @@ describe('main function', () => {
|
|
|
301
639
|
syncGtmVariables: jest.fn().mockResolvedValue({
|
|
302
640
|
created: ['var1'],
|
|
303
641
|
deleted: ['var2'],
|
|
642
|
+
failedDeletes: [
|
|
643
|
+
{
|
|
644
|
+
name: 'legacy_field',
|
|
645
|
+
variableId: '88',
|
|
646
|
+
},
|
|
647
|
+
],
|
|
304
648
|
inSync: ['var3'],
|
|
305
649
|
}),
|
|
306
650
|
getVariablesFromSchemas: jest
|
|
@@ -334,6 +678,12 @@ describe('main function', () => {
|
|
|
334
678
|
workspace: { workspaceName: 'test-workspace', workspaceId: '123' },
|
|
335
679
|
created: ['var1'],
|
|
336
680
|
deleted: ['var2'],
|
|
681
|
+
failedDeletes: [
|
|
682
|
+
{
|
|
683
|
+
name: 'legacy_field',
|
|
684
|
+
variableId: '88',
|
|
685
|
+
},
|
|
686
|
+
],
|
|
337
687
|
inSync: ['var3'],
|
|
338
688
|
});
|
|
339
689
|
});
|
|
@@ -343,4 +693,14 @@ describe('main function', () => {
|
|
|
343
693
|
await gtmScript.main(argv, mockDeps);
|
|
344
694
|
expect(mockDeps.getLatestSchemaPath).toHaveBeenCalledWith('./my-demo');
|
|
345
695
|
});
|
|
696
|
+
|
|
697
|
+
it('should report failed deletions in human-readable output', async () => {
|
|
698
|
+
const argv = ['node', 'script.js'];
|
|
699
|
+
await gtmScript.main(argv, mockDeps);
|
|
700
|
+
|
|
701
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
702
|
+
'Skipped deleting 1 GTM variables (GTM rejected the delete, they may still be referenced):',
|
|
703
|
+
);
|
|
704
|
+
expect(logSpy).toHaveBeenCalledWith('- legacy_field (ID: 88)');
|
|
705
|
+
});
|
|
346
706
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @jest-environment node
|
|
2
|
+
* @jest-environment @stryker-mutator/jest-runner/jest-env/node
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import fs from 'fs';
|
|
@@ -21,6 +21,75 @@ describe('updateSchemaIds', () => {
|
|
|
21
21
|
fs.rmSync(tempSiteDir, { recursive: true });
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
+
it('skips versions whose schema directory does not exist (L33)', () => {
|
|
25
|
+
// Add a version that has no corresponding directory
|
|
26
|
+
const versionsPath = path.join(tempSiteDir, 'versions.json');
|
|
27
|
+
fs.writeFileSync(versionsPath, JSON.stringify(['1.1.1', '9.9.9']));
|
|
28
|
+
|
|
29
|
+
// Should not throw; 9.9.9 directory doesn't exist and is skipped
|
|
30
|
+
expect(() => updateSchemaIds(tempSiteDir, url)).not.toThrow();
|
|
31
|
+
|
|
32
|
+
// The existing 1.1.1 schemas should still be updated
|
|
33
|
+
const schemaPath = path.join(
|
|
34
|
+
tempSiteDir,
|
|
35
|
+
'static/schemas',
|
|
36
|
+
'1.1.1',
|
|
37
|
+
'add-to-cart-event.json',
|
|
38
|
+
);
|
|
39
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
|
|
40
|
+
expect(schema.$id).toBe(
|
|
41
|
+
'https://tracking-docs-demo.buchert.digital/schemas/1.1.1/add-to-cart-event.json',
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('skips non-.json files in schema directories (L19)', () => {
|
|
46
|
+
// Create a non-json file in the schema directory
|
|
47
|
+
const nonJsonPath = path.join(
|
|
48
|
+
tempSiteDir,
|
|
49
|
+
'static/schemas',
|
|
50
|
+
'1.1.1',
|
|
51
|
+
'readme.txt',
|
|
52
|
+
);
|
|
53
|
+
fs.writeFileSync(nonJsonPath, 'This is not a JSON file');
|
|
54
|
+
|
|
55
|
+
expect(() => updateSchemaIds(tempSiteDir, url)).not.toThrow();
|
|
56
|
+
|
|
57
|
+
// The non-json file should remain unchanged
|
|
58
|
+
const content = fs.readFileSync(nonJsonPath, 'utf8');
|
|
59
|
+
expect(content).toBe('This is not a JSON file');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('accepts a specific version parameter (L27-28)', () => {
|
|
63
|
+
updateSchemaIds(tempSiteDir, url, '1.1.1');
|
|
64
|
+
|
|
65
|
+
const schemaPath = path.join(
|
|
66
|
+
tempSiteDir,
|
|
67
|
+
'static/schemas',
|
|
68
|
+
'1.1.1',
|
|
69
|
+
'add-to-cart-event.json',
|
|
70
|
+
);
|
|
71
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
|
|
72
|
+
expect(schema.$id).toBe(
|
|
73
|
+
'https://tracking-docs-demo.buchert.digital/schemas/1.1.1/add-to-cart-event.json',
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('returns early when versions.json does not exist (L6-8)', () => {
|
|
78
|
+
const noVersionsDir = path.resolve(__dirname, '__temp_no_versions__');
|
|
79
|
+
fs.mkdirSync(noVersionsDir, { recursive: true });
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
83
|
+
updateSchemaIds(noVersionsDir, url);
|
|
84
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
85
|
+
'No versions.json file found, skipping schema ID update.',
|
|
86
|
+
);
|
|
87
|
+
consoleSpy.mockRestore();
|
|
88
|
+
} finally {
|
|
89
|
+
fs.rmSync(noVersionsDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
24
93
|
it('should update the $id of the schemas in the versioned directories', () => {
|
|
25
94
|
updateSchemaIds(tempSiteDir, url);
|
|
26
95
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @jest-environment node
|
|
2
|
+
* @jest-environment @stryker-mutator/jest-runner/jest-env/node
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import path from 'path';
|
|
@@ -25,5 +25,5 @@ describe('validateSchemas - Integration', () => {
|
|
|
25
25
|
const { schemaDir } = getPathsForVersion('next', siteDir);
|
|
26
26
|
const result = await validateSchemas(schemaDir);
|
|
27
27
|
expect(result).toBe(true);
|
|
28
|
-
});
|
|
28
|
+
}, 60000);
|
|
29
29
|
});
|