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.
Files changed (62) hide show
  1. package/README.md +12 -0
  2. package/__tests__/__fixtures__/validateSchemas/main-schema-with-not-allof.json +11 -0
  3. package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof-multi.json +12 -0
  4. package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof.json +30 -0
  5. package/__tests__/__fixtures__/validateSchemas/schema-with-not-edge-cases.json +24 -0
  6. package/__tests__/__fixtures__/validateSchemas/schema-with-not-non-object.json +15 -0
  7. package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +6 -0
  8. package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +6 -0
  9. package/__tests__/__snapshots__/generateEventDocs.test.js.snap +15 -0
  10. package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +6 -0
  11. package/__tests__/components/PropertiesTable.test.js +66 -0
  12. package/__tests__/components/PropertyRow.test.js +85 -4
  13. package/__tests__/components/SchemaJsonViewer.test.js +118 -0
  14. package/__tests__/generateEventDocs.anchor.test.js +1 -1
  15. package/__tests__/generateEventDocs.nested.test.js +1 -1
  16. package/__tests__/generateEventDocs.partials.test.js +1 -1
  17. package/__tests__/generateEventDocs.test.js +506 -1
  18. package/__tests__/generateEventDocs.versioned.test.js +1 -1
  19. package/__tests__/helpers/buildExampleFromSchema.test.js +240 -0
  20. package/__tests__/helpers/constraintSchemaPaths.test.js +208 -0
  21. package/__tests__/helpers/continuingLinesStyle.test.js +492 -0
  22. package/__tests__/helpers/example-helper.test.js +12 -0
  23. package/__tests__/helpers/exampleModel.test.js +209 -0
  24. package/__tests__/helpers/file-system.test.js +73 -1
  25. package/__tests__/helpers/getConstraints.test.js +43 -0
  26. package/__tests__/helpers/mergeSchema.test.js +94 -0
  27. package/__tests__/helpers/processSchema.test.js +309 -1
  28. package/__tests__/helpers/schema-doc-template.test.js +54 -0
  29. package/__tests__/helpers/schema-processing.test.js +122 -2
  30. package/__tests__/helpers/schemaToExamples.test.js +1007 -0
  31. package/__tests__/helpers/schemaToTableData.mutations.test.js +970 -0
  32. package/__tests__/helpers/schemaToTableData.test.js +157 -0
  33. package/__tests__/helpers/schemaTraversal.test.js +110 -0
  34. package/__tests__/helpers/snippetTargets.test.js +432 -0
  35. package/__tests__/helpers/trackingTargets.test.js +319 -0
  36. package/__tests__/helpers/validator.test.js +385 -1
  37. package/__tests__/index.test.js +436 -0
  38. package/__tests__/syncGtm.test.js +366 -6
  39. package/__tests__/update-schema-ids.test.js +70 -1
  40. package/__tests__/validateSchemas-integration.test.js +2 -2
  41. package/__tests__/validateSchemas.test.js +192 -1
  42. package/components/PropertiesTable.js +32 -2
  43. package/components/PropertyRow.js +29 -2
  44. package/components/SchemaJsonViewer.js +234 -131
  45. package/components/SchemaRows.css +40 -0
  46. package/components/SchemaViewer.js +11 -2
  47. package/generateEventDocs.js +21 -1
  48. package/helpers/constraintSchemaPaths.js +10 -14
  49. package/helpers/example-helper.js +2 -2
  50. package/helpers/getConstraints.js +20 -0
  51. package/helpers/processSchema.js +32 -1
  52. package/helpers/schema-doc-template.js +4 -0
  53. package/helpers/schemaToExamples.js +29 -35
  54. package/helpers/schemaToTableData.js +538 -492
  55. package/helpers/schemaTraversal.cjs +148 -0
  56. package/helpers/trackingTargets.js +26 -3
  57. package/helpers/validator.js +18 -4
  58. package/index.js +1 -2
  59. package/package.json +1 -1
  60. package/scripts/sync-gtm.js +65 -34
  61. package/test-data/payloadContracts.js +35 -0
  62. 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(8);
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(expected.length);
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 call execSync to delete variables and return their names', () => {
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
  });