@auto-engineer/narrative 0.13.1 → 0.13.2
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/src/id/addAutoIds.d.ts.map +1 -1
- package/dist/src/id/addAutoIds.js +31 -6
- package/dist/src/id/addAutoIds.js.map +1 -1
- package/dist/src/id/addAutoIds.specs.js +342 -0
- package/dist/src/id/addAutoIds.specs.js.map +1 -1
- package/dist/src/id/hasAllIds.d.ts.map +1 -1
- package/dist/src/id/hasAllIds.js +22 -4
- package/dist/src/id/hasAllIds.js.map +1 -1
- package/dist/src/id/hasAllIds.specs.js +269 -3
- package/dist/src/id/hasAllIds.specs.js.map +1 -1
- package/dist/src/schema.d.ts +507 -0
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +3 -0
- package/dist/src/schema.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/id/addAutoIds.specs.ts +370 -0
- package/src/id/addAutoIds.ts +34 -7
- package/src/id/hasAllIds.specs.ts +278 -3
- package/src/id/hasAllIds.ts +24 -5
- package/src/schema.ts +3 -0
|
@@ -52,6 +52,7 @@ describe('hasAllIds', () => {
|
|
|
52
52
|
description: 'Test server',
|
|
53
53
|
specs: [
|
|
54
54
|
{
|
|
55
|
+
id: 'SPEC-001',
|
|
55
56
|
type: 'gherkin',
|
|
56
57
|
feature: 'Test specs',
|
|
57
58
|
rules: [
|
|
@@ -72,6 +73,66 @@ describe('hasAllIds', () => {
|
|
|
72
73
|
integrations: [],
|
|
73
74
|
});
|
|
74
75
|
|
|
76
|
+
const createModelWithFullIds = (): Model => ({
|
|
77
|
+
variant: 'specs',
|
|
78
|
+
narratives: [
|
|
79
|
+
{
|
|
80
|
+
name: 'Test Flow with Full IDs',
|
|
81
|
+
id: 'FLOW-001',
|
|
82
|
+
slices: [
|
|
83
|
+
{
|
|
84
|
+
type: 'command',
|
|
85
|
+
name: 'Test slice with ID',
|
|
86
|
+
id: 'SLICE-001',
|
|
87
|
+
client: { specs: [] },
|
|
88
|
+
server: {
|
|
89
|
+
description: 'Test server',
|
|
90
|
+
specs: [
|
|
91
|
+
{
|
|
92
|
+
id: 'SPEC-001',
|
|
93
|
+
type: 'gherkin',
|
|
94
|
+
feature: 'Test specs',
|
|
95
|
+
rules: [
|
|
96
|
+
{
|
|
97
|
+
id: 'RULE-001',
|
|
98
|
+
name: 'Test rule with ID',
|
|
99
|
+
examples: [
|
|
100
|
+
{
|
|
101
|
+
id: 'EXAMPLE-001',
|
|
102
|
+
name: 'Test example',
|
|
103
|
+
steps: [
|
|
104
|
+
{
|
|
105
|
+
id: 'STEP-001',
|
|
106
|
+
keyword: 'Given',
|
|
107
|
+
text: 'TestState',
|
|
108
|
+
docString: { value: 'test' },
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: 'STEP-002',
|
|
112
|
+
keyword: 'When',
|
|
113
|
+
text: 'TestCommand',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'STEP-003',
|
|
117
|
+
keyword: 'Then',
|
|
118
|
+
text: 'TestEvent',
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
messages: [],
|
|
133
|
+
integrations: [],
|
|
134
|
+
});
|
|
135
|
+
|
|
75
136
|
const createMultipleFlowsModel = (includeAllIds: boolean, includeAllSliceIds: boolean): Model => ({
|
|
76
137
|
variant: 'specs',
|
|
77
138
|
narratives: [
|
|
@@ -84,7 +145,7 @@ describe('hasAllIds', () => {
|
|
|
84
145
|
name: 'Active Surveys Summary',
|
|
85
146
|
id: 'slice1',
|
|
86
147
|
type: 'experience',
|
|
87
|
-
client: { specs: [{ type: 'it', title: 'show active surveys summary' }] },
|
|
148
|
+
client: { specs: [{ type: 'it', id: 'it1', title: 'show active surveys summary' }] },
|
|
88
149
|
},
|
|
89
150
|
],
|
|
90
151
|
},
|
|
@@ -97,7 +158,7 @@ describe('hasAllIds', () => {
|
|
|
97
158
|
name: 'Create Survey Form',
|
|
98
159
|
id: includeAllSliceIds ? 'slice2' : undefined,
|
|
99
160
|
type: 'experience',
|
|
100
|
-
client: { specs: [{ type: 'it', title: 'allow entering survey title' }] },
|
|
161
|
+
client: { specs: [{ type: 'it', id: 'it2', title: 'allow entering survey title' }] },
|
|
101
162
|
},
|
|
102
163
|
],
|
|
103
164
|
},
|
|
@@ -110,7 +171,7 @@ describe('hasAllIds', () => {
|
|
|
110
171
|
name: 'Response Rate Charts',
|
|
111
172
|
id: 'slice3',
|
|
112
173
|
type: 'experience',
|
|
113
|
-
client: { specs: [{ type: 'it', title: 'show daily response rate charts' }] },
|
|
174
|
+
client: { specs: [{ type: 'it', id: 'it3', title: 'show daily response rate charts' }] },
|
|
114
175
|
},
|
|
115
176
|
],
|
|
116
177
|
},
|
|
@@ -166,4 +227,218 @@ describe('hasAllIds', () => {
|
|
|
166
227
|
const model = createMultipleFlowsModel(true, false);
|
|
167
228
|
expect(hasAllIds(model)).toBe(false);
|
|
168
229
|
});
|
|
230
|
+
|
|
231
|
+
it('should return false if any spec is missing an ID', () => {
|
|
232
|
+
const model = createModelWithFullIds();
|
|
233
|
+
const modifiedModel = structuredClone(model);
|
|
234
|
+
const slice = modifiedModel.narratives[0].slices[0];
|
|
235
|
+
if ('server' in slice && slice.server?.specs !== undefined && Array.isArray(slice.server.specs)) {
|
|
236
|
+
slice.server.specs[0].id = '';
|
|
237
|
+
}
|
|
238
|
+
expect(hasAllIds(modifiedModel)).toBe(false);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should return false if any example is missing an ID', () => {
|
|
242
|
+
const model = createModelWithFullIds();
|
|
243
|
+
const modifiedModel = structuredClone(model);
|
|
244
|
+
const slice = modifiedModel.narratives[0].slices[0];
|
|
245
|
+
if ('server' in slice && slice.server?.specs !== undefined && Array.isArray(slice.server.specs)) {
|
|
246
|
+
slice.server.specs[0].rules[0].examples[0].id = '';
|
|
247
|
+
}
|
|
248
|
+
expect(hasAllIds(modifiedModel)).toBe(false);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should return false if any step is missing an ID', () => {
|
|
252
|
+
const model = createModelWithFullIds();
|
|
253
|
+
const modifiedModel = structuredClone(model);
|
|
254
|
+
const slice = modifiedModel.narratives[0].slices[0];
|
|
255
|
+
if ('server' in slice && slice.server?.specs !== undefined && Array.isArray(slice.server.specs)) {
|
|
256
|
+
slice.server.specs[0].rules[0].examples[0].steps[0].id = '';
|
|
257
|
+
}
|
|
258
|
+
expect(hasAllIds(modifiedModel)).toBe(false);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should return false if step with error is missing an ID', () => {
|
|
262
|
+
const model: Model = {
|
|
263
|
+
variant: 'specs',
|
|
264
|
+
narratives: [
|
|
265
|
+
{
|
|
266
|
+
name: 'Test Flow',
|
|
267
|
+
id: 'FLOW-001',
|
|
268
|
+
slices: [
|
|
269
|
+
{
|
|
270
|
+
type: 'command',
|
|
271
|
+
name: 'Test slice',
|
|
272
|
+
id: 'SLICE-001',
|
|
273
|
+
client: { specs: [] },
|
|
274
|
+
server: {
|
|
275
|
+
description: 'Test server',
|
|
276
|
+
specs: [
|
|
277
|
+
{
|
|
278
|
+
id: 'SPEC-001',
|
|
279
|
+
type: 'gherkin',
|
|
280
|
+
feature: 'Test specs',
|
|
281
|
+
rules: [
|
|
282
|
+
{
|
|
283
|
+
id: 'RULE-001',
|
|
284
|
+
name: 'Test rule',
|
|
285
|
+
examples: [
|
|
286
|
+
{
|
|
287
|
+
id: 'EXAMPLE-001',
|
|
288
|
+
name: 'Error example',
|
|
289
|
+
steps: [
|
|
290
|
+
{
|
|
291
|
+
id: 'STEP-001',
|
|
292
|
+
keyword: 'Given',
|
|
293
|
+
text: 'TestState',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
keyword: 'Then',
|
|
297
|
+
error: { type: 'ValidationError', message: 'Invalid input' },
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
messages: [],
|
|
312
|
+
integrations: [],
|
|
313
|
+
};
|
|
314
|
+
expect(hasAllIds(model)).toBe(false);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should return false if client it spec is missing an ID', () => {
|
|
318
|
+
const model: Model = {
|
|
319
|
+
variant: 'specs',
|
|
320
|
+
narratives: [
|
|
321
|
+
{
|
|
322
|
+
name: 'Test Flow',
|
|
323
|
+
id: 'FLOW-001',
|
|
324
|
+
slices: [
|
|
325
|
+
{
|
|
326
|
+
name: 'Test slice',
|
|
327
|
+
id: 'SLICE-001',
|
|
328
|
+
type: 'experience',
|
|
329
|
+
client: {
|
|
330
|
+
specs: [{ type: 'it', title: 'test without id' }],
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
messages: [],
|
|
337
|
+
integrations: [],
|
|
338
|
+
};
|
|
339
|
+
expect(hasAllIds(model)).toBe(false);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should return false if client describe spec is missing an ID', () => {
|
|
343
|
+
const model: Model = {
|
|
344
|
+
variant: 'specs',
|
|
345
|
+
narratives: [
|
|
346
|
+
{
|
|
347
|
+
name: 'Test Flow',
|
|
348
|
+
id: 'FLOW-001',
|
|
349
|
+
slices: [
|
|
350
|
+
{
|
|
351
|
+
name: 'Test slice',
|
|
352
|
+
id: 'SLICE-001',
|
|
353
|
+
type: 'experience',
|
|
354
|
+
client: {
|
|
355
|
+
specs: [
|
|
356
|
+
{
|
|
357
|
+
type: 'describe',
|
|
358
|
+
title: 'test describe without id',
|
|
359
|
+
children: [{ type: 'it', id: 'IT-001', title: 'nested it with id' }],
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
messages: [],
|
|
368
|
+
integrations: [],
|
|
369
|
+
};
|
|
370
|
+
expect(hasAllIds(model)).toBe(false);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('should return false if nested client it spec is missing an ID', () => {
|
|
374
|
+
const model: Model = {
|
|
375
|
+
variant: 'specs',
|
|
376
|
+
narratives: [
|
|
377
|
+
{
|
|
378
|
+
name: 'Test Flow',
|
|
379
|
+
id: 'FLOW-001',
|
|
380
|
+
slices: [
|
|
381
|
+
{
|
|
382
|
+
name: 'Test slice',
|
|
383
|
+
id: 'SLICE-001',
|
|
384
|
+
type: 'experience',
|
|
385
|
+
client: {
|
|
386
|
+
specs: [
|
|
387
|
+
{
|
|
388
|
+
type: 'describe',
|
|
389
|
+
id: 'DESC-001',
|
|
390
|
+
title: 'test describe with id',
|
|
391
|
+
children: [{ type: 'it', title: 'nested it without id' }],
|
|
392
|
+
},
|
|
393
|
+
],
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
],
|
|
399
|
+
messages: [],
|
|
400
|
+
integrations: [],
|
|
401
|
+
};
|
|
402
|
+
expect(hasAllIds(model)).toBe(false);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should return true for client specs with all IDs', () => {
|
|
406
|
+
const model: Model = {
|
|
407
|
+
variant: 'specs',
|
|
408
|
+
narratives: [
|
|
409
|
+
{
|
|
410
|
+
name: 'Test Flow',
|
|
411
|
+
id: 'FLOW-001',
|
|
412
|
+
slices: [
|
|
413
|
+
{
|
|
414
|
+
name: 'Test slice',
|
|
415
|
+
id: 'SLICE-001',
|
|
416
|
+
type: 'experience',
|
|
417
|
+
client: {
|
|
418
|
+
specs: [
|
|
419
|
+
{
|
|
420
|
+
type: 'describe',
|
|
421
|
+
id: 'DESC-001',
|
|
422
|
+
title: 'test describe',
|
|
423
|
+
children: [
|
|
424
|
+
{ type: 'it', id: 'IT-001', title: 'first it' },
|
|
425
|
+
{
|
|
426
|
+
type: 'describe',
|
|
427
|
+
id: 'DESC-002',
|
|
428
|
+
title: 'nested describe',
|
|
429
|
+
children: [{ type: 'it', id: 'IT-002', title: 'nested it' }],
|
|
430
|
+
},
|
|
431
|
+
],
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
messages: [],
|
|
440
|
+
integrations: [],
|
|
441
|
+
};
|
|
442
|
+
expect(hasAllIds(model)).toBe(true);
|
|
443
|
+
});
|
|
169
444
|
});
|
package/src/id/hasAllIds.ts
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
import { Model, Slice, Spec, Rule } from '../index';
|
|
1
|
+
import { Model, Slice, Spec, Rule, Example, Step, ClientSpecNode } from '../index';
|
|
2
2
|
|
|
3
3
|
function hasValidId(item: { id?: string }): boolean {
|
|
4
4
|
return item.id !== undefined && item.id !== '';
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
function hasStepIds(steps: Step[]): boolean {
|
|
8
|
+
return steps.every((step) => hasValidId(step));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function hasExampleIds(examples: Example[]): boolean {
|
|
12
|
+
return examples.every((example) => hasValidId(example) && hasStepIds(example.steps));
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
function hasRuleIds(rules: Rule[]): boolean {
|
|
8
|
-
return rules.every((rule) => hasValidId(rule));
|
|
16
|
+
return rules.every((rule) => hasValidId(rule) && hasExampleIds(rule.examples));
|
|
9
17
|
}
|
|
10
18
|
|
|
11
19
|
function hasSpecIds(specs: Spec[]): boolean {
|
|
12
|
-
return specs.every((spec) => hasRuleIds(spec.rules));
|
|
20
|
+
return specs.every((spec) => hasValidId(spec) && hasRuleIds(spec.rules));
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
function hasServerSpecIds(slice: Slice): boolean {
|
|
@@ -17,8 +25,19 @@ function hasServerSpecIds(slice: Slice): boolean {
|
|
|
17
25
|
return hasSpecIds(slice.server.specs);
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
function
|
|
21
|
-
return
|
|
28
|
+
function hasClientSpecNodeIds(nodes: ClientSpecNode[]): boolean {
|
|
29
|
+
return nodes.every((node) => {
|
|
30
|
+
if (!hasValidId(node)) return false;
|
|
31
|
+
if (node.type === 'describe' && node.children) {
|
|
32
|
+
return hasClientSpecNodeIds(node.children);
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasClientSpecIds(slice: Slice): boolean {
|
|
39
|
+
if (!('client' in slice) || slice.client?.specs === undefined || !Array.isArray(slice.client.specs)) return true;
|
|
40
|
+
return hasClientSpecNodeIds(slice.client.specs);
|
|
22
41
|
}
|
|
23
42
|
|
|
24
43
|
function hasSliceIds(slice: Slice): boolean {
|
package/src/schema.ts
CHANGED
|
@@ -158,12 +158,14 @@ const StepErrorSchema = z.object({
|
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
const StepWithDocStringSchema = z.object({
|
|
161
|
+
id: z.string().optional().describe('Optional unique identifier for the step'),
|
|
161
162
|
keyword: z.enum(['Given', 'When', 'Then', 'And']).describe('Gherkin keyword'),
|
|
162
163
|
text: z.string().describe('The type name (e.g., AddTodo, TodoAdded)'),
|
|
163
164
|
docString: z.record(z.unknown()).optional().describe('The example data'),
|
|
164
165
|
});
|
|
165
166
|
|
|
166
167
|
const StepWithErrorSchema = z.object({
|
|
168
|
+
id: z.string().optional().describe('Optional unique identifier for the step'),
|
|
167
169
|
keyword: z.literal('Then').describe('Error steps use Then keyword'),
|
|
168
170
|
error: StepErrorSchema.describe('Error details'),
|
|
169
171
|
});
|
|
@@ -188,6 +190,7 @@ const RuleSchema = z
|
|
|
188
190
|
|
|
189
191
|
const SpecSchema = z
|
|
190
192
|
.object({
|
|
193
|
+
id: z.string().optional().describe('Optional unique identifier for the spec'),
|
|
191
194
|
type: z.literal('gherkin').describe('Specification type'),
|
|
192
195
|
feature: z.string().describe('Feature name'),
|
|
193
196
|
rules: z.array(RuleSchema).describe('Business rules for this spec'),
|