@auto-engineer/narrative 0.20.0 → 0.21.0

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 (31) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-format.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/.turbo/turbo-test.log +6 -6
  5. package/.turbo/turbo-type-check.log +5 -4
  6. package/CHANGELOG.md +13 -0
  7. package/dist/src/commands/export-schema-runner.js +6 -1
  8. package/dist/src/commands/export-schema-runner.js.map +1 -1
  9. package/dist/src/fluent-builder.specs.js +2 -2
  10. package/dist/src/fluent-builder.specs.js.map +1 -1
  11. package/dist/src/getNarratives.cache.specs.js +5 -5
  12. package/dist/src/getNarratives.cache.specs.js.map +1 -1
  13. package/dist/src/getNarratives.specs.js +194 -207
  14. package/dist/src/getNarratives.specs.js.map +1 -1
  15. package/dist/src/id/addAutoIds.specs.js +72 -409
  16. package/dist/src/id/addAutoIds.specs.js.map +1 -1
  17. package/dist/src/id/hasAllIds.specs.js +215 -408
  18. package/dist/src/id/hasAllIds.specs.js.map +1 -1
  19. package/dist/src/model-to-narrative.specs.js +505 -564
  20. package/dist/src/model-to-narrative.specs.js.map +1 -1
  21. package/dist/src/narrative-context.specs.js +58 -133
  22. package/dist/src/narrative-context.specs.js.map +1 -1
  23. package/dist/src/schema.d.ts +302 -302
  24. package/dist/src/transformers/narrative-to-model/type-inference.specs.js +94 -104
  25. package/dist/src/transformers/narrative-to-model/type-inference.specs.js.map +1 -1
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +4 -4
  28. package/dist/src/transformers/model-to-narrative/generators/gwt.specs.d.ts +0 -2
  29. package/dist/src/transformers/model-to-narrative/generators/gwt.specs.d.ts.map +0 -1
  30. package/dist/src/transformers/model-to-narrative/generators/gwt.specs.js +0 -142
  31. package/dist/src/transformers/model-to-narrative/generators/gwt.specs.js.map +0 -1
@@ -1,11 +1,10 @@
1
- import path from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
- import { InMemoryFileStore } from '@auto-engineer/file-store';
4
- import { NodeFileStore } from '@auto-engineer/file-store/node';
5
1
  import { beforeEach, describe, expect, it } from 'vitest';
6
- import { getNarratives } from './getNarratives.js';
7
- import { modelToNarrative } from './index.js';
8
2
  import { modelSchema } from './schema.js';
3
+ import { modelToNarrative } from './index.js';
4
+ import { fileURLToPath } from 'url';
5
+ import path from 'path';
6
+ import { InMemoryFileStore, NodeFileStore } from '@auto-engineer/file-store';
7
+ import { getNarratives } from './getNarratives.js';
9
8
  const __filename = fileURLToPath(import.meta.url);
10
9
  const __dirname = path.dirname(__filename);
11
10
  const pattern = /\.(narrative)\.(ts)$/;
@@ -47,56 +46,42 @@ describe('getNarratives', (_mode) => {
47
46
  expect(createItemSlice.stream).toBe('item-${id}');
48
47
  if (createItemSlice.type === 'command') {
49
48
  expect(createItemSlice.client.specs).toBeDefined();
50
- expect(Array.isArray(createItemSlice.client.specs)).toBe(true);
51
- expect(createItemSlice.client.specs).toHaveLength(1);
52
- expect(createItemSlice.client.specs[0].type).toBe('describe');
53
- expect(createItemSlice.client.specs[0].title).toBe('A form that allows users to add items');
54
- if (createItemSlice.client.specs[0].type === 'describe') {
55
- expect(createItemSlice.client.specs[0].children).toHaveLength(1);
56
- }
49
+ expect(createItemSlice.client.specs?.name).toBe('A form that allows users to add items');
50
+ expect(createItemSlice.client.specs?.rules).toHaveLength(1);
57
51
  expect(createItemSlice.server.specs).toBeDefined();
58
- expect(Array.isArray(createItemSlice.server.specs)).toBe(true);
59
- expect(createItemSlice.server.specs).toHaveLength(1);
60
- const spec = createItemSlice.server.specs[0];
61
- expect(spec.feature).toBeDefined();
52
+ const spec = createItemSlice.server.specs;
53
+ expect(spec.name).toBeDefined();
62
54
  expect(spec.rules).toHaveLength(1);
63
55
  const rule = spec.rules[0];
64
- expect(rule.name).toBeDefined();
56
+ expect(rule.description).toBeDefined();
65
57
  expect(rule.examples).toHaveLength(1);
66
58
  const example = rule.examples[0];
67
- expect(example.steps).toBeDefined();
68
- expect(example.steps.length).toBeGreaterThanOrEqual(2);
69
- const whenStep = example.steps.find((s) => s.keyword === 'When');
70
- const thenStep = example.steps.find((s) => s.keyword === 'Then');
71
- expect(whenStep).toBeDefined();
72
- expect(thenStep).toBeDefined();
73
- if (whenStep && 'text' in whenStep) {
74
- expect(whenStep.text).toBe('CreateItem');
75
- expect(whenStep.docString).toMatchObject({
59
+ expect(typeof example.when === 'object' && !Array.isArray(example.when)).toBe(true);
60
+ if (typeof example.when === 'object' && !Array.isArray(example.when)) {
61
+ if ('commandRef' in example.when) {
62
+ expect(example.when.commandRef).toBe('CreateItem');
63
+ }
64
+ expect(example.when.exampleData).toMatchObject({
76
65
  itemId: 'item_123',
77
66
  description: 'A new item',
78
67
  });
79
68
  }
80
- if (thenStep && 'text' in thenStep) {
81
- expect(thenStep.text).toBe('ItemCreated');
82
- expect(thenStep.docString).toMatchObject({
69
+ expect(example.then).toHaveLength(1);
70
+ expect(example.then[0]).toMatchObject({
71
+ eventRef: 'ItemCreated',
72
+ exampleData: {
83
73
  id: 'item_123',
84
74
  description: 'A new item',
85
75
  addedAt: new Date('2024-01-15T10:00:00.000Z'),
86
- });
87
- }
76
+ },
77
+ });
88
78
  }
89
79
  const viewItemSlice = items.slices[1];
90
80
  expect(viewItemSlice.type).toBe('query');
91
81
  expect(viewItemSlice.name).toBe('view items');
92
82
  expect(viewItemSlice.client.specs).toBeDefined();
93
- expect(Array.isArray(viewItemSlice.client.specs)).toBe(true);
94
- expect(viewItemSlice.client.specs).toHaveLength(1);
95
- expect(viewItemSlice.client.specs[0].type).toBe('describe');
96
- expect(viewItemSlice.client.specs[0].title).toBe('view Items Screen');
97
- if (viewItemSlice.client.specs[0].type === 'describe') {
98
- expect(viewItemSlice.client.specs[0].children).toHaveLength(3);
99
- }
83
+ expect(viewItemSlice.client.specs?.name).toBe('view Items Screen');
84
+ expect(viewItemSlice.client.specs?.rules).toHaveLength(3);
100
85
  expect(viewItemSlice.request).toBeDefined();
101
86
  expect(viewItemSlice.request).toMatch(/query items\(\$itemId: String!\) {\s+items\(itemId: \$itemId\) {\s+id\s+description\s+}/);
102
87
  const data = viewItemSlice?.server?.data;
@@ -106,7 +91,7 @@ describe('getNarratives', (_mode) => {
106
91
  expect(data[0].target).toMatchObject({ type: 'State', name: 'items' });
107
92
  expect(data[0].origin).toMatchObject({ name: 'ItemsProjection', type: 'projection' });
108
93
  const specs = viewItemSlice?.server?.specs;
109
- if (specs == null || specs.length === 0 || specs[0].feature === '')
94
+ if (specs == null || specs.name === '')
110
95
  throw new Error('No specs found in view items slice');
111
96
  expect(specs).toBeDefined();
112
97
  }
@@ -118,40 +103,31 @@ describe('getNarratives', (_mode) => {
118
103
  expect(submitOrderSlice.stream).toBe('order-${orderId}');
119
104
  if (submitOrderSlice.type === 'command') {
120
105
  expect(submitOrderSlice.client.specs).toBeDefined();
121
- expect(Array.isArray(submitOrderSlice.client.specs)).toBe(true);
122
- expect(submitOrderSlice.client.specs).toHaveLength(1);
123
- expect(submitOrderSlice.client.specs[0].type).toBe('describe');
124
- expect(submitOrderSlice.client.specs[0].title).toBe('Order submission form');
125
- if (submitOrderSlice.client.specs[0].type === 'describe') {
126
- expect(submitOrderSlice.client.specs[0].children).toHaveLength(2);
127
- }
106
+ expect(submitOrderSlice.client.specs?.name).toBe('Order submission form');
107
+ expect(submitOrderSlice.client.specs?.rules).toHaveLength(2);
128
108
  expect(submitOrderSlice.server.specs).toBeDefined();
129
- expect(Array.isArray(submitOrderSlice.server.specs)).toBe(true);
130
- expect(submitOrderSlice.server.specs).toHaveLength(1);
131
- const spec = submitOrderSlice.server.specs[0];
109
+ const spec = submitOrderSlice.server.specs;
132
110
  expect(spec.rules).toHaveLength(1);
133
111
  const rule = spec.rules[0];
134
112
  expect(rule.examples).toHaveLength(1);
135
113
  const example = rule.examples[0];
136
- expect(example.steps).toBeDefined();
137
- expect(example.steps.length).toBeGreaterThanOrEqual(2);
138
- const whenStep = example.steps.find((s) => s.keyword === 'When');
139
- const thenStep = example.steps.find((s) => s.keyword === 'Then');
140
- expect(whenStep).toBeDefined();
141
- expect(thenStep).toBeDefined();
142
- if (whenStep && 'text' in whenStep) {
143
- expect(whenStep.text).toBe('PlaceOrder');
144
- expect(whenStep.docString).toMatchObject({ productId: 'product_789', quantity: 3 });
114
+ expect(typeof example.when === 'object' && !Array.isArray(example.when)).toBe(true);
115
+ if (typeof example.when === 'object' && !Array.isArray(example.when)) {
116
+ if ('commandRef' in example.when) {
117
+ expect(example.when.commandRef).toBe('PlaceOrder');
118
+ }
119
+ expect(example.when.exampleData).toMatchObject({ productId: 'product_789', quantity: 3 });
145
120
  }
146
- if (thenStep && 'text' in thenStep) {
147
- expect(thenStep.text).toBe('OrderPlaced');
148
- expect(thenStep.docString).toMatchObject({
121
+ expect(example.then).toHaveLength(1);
122
+ expect(example.then[0]).toMatchObject({
123
+ eventRef: 'OrderPlaced',
124
+ exampleData: {
149
125
  orderId: 'order_001',
150
126
  productId: 'product_789',
151
127
  quantity: 3,
152
128
  placedAt: new Date('2024-01-20T10:00:00.000Z'),
153
- });
154
- }
129
+ },
130
+ });
155
131
  }
156
132
  }
157
133
  const messages = schemas.messages;
@@ -205,15 +181,16 @@ describe('getNarratives', (_mode) => {
205
181
  if (slice.type === 'react') {
206
182
  expect(slice.server).toBeDefined();
207
183
  expect(slice.server.specs).toBeDefined();
208
- expect(Array.isArray(slice.server.specs)).toBe(true);
209
- expect(slice.server.specs.length).toBeGreaterThanOrEqual(1);
210
- const spec = slice.server.specs[0];
184
+ expect(typeof slice.server.specs === 'object' && !Array.isArray(slice.server.specs)).toBe(true);
185
+ const spec = slice.server.specs;
211
186
  expect(spec.rules).toBeDefined();
212
187
  expect(Array.isArray(spec.rules)).toBe(true);
213
188
  spec.rules.forEach((rule) => {
214
189
  rule.examples.forEach((example) => {
215
- expect(example.steps).toBeDefined();
216
- expect(Array.isArray(example.steps)).toBe(true);
190
+ expect(example.when).toBeDefined();
191
+ expect(Array.isArray(example.when)).toBe(true);
192
+ expect(example.then).toBeDefined();
193
+ expect(Array.isArray(example.then)).toBe(true);
217
194
  });
218
195
  });
219
196
  }
@@ -266,10 +243,10 @@ describe('getNarratives', (_mode) => {
266
243
  const commandSlice = testFlowWithIds.slices.find((s) => s.name === 'Create test item');
267
244
  if (commandSlice?.type !== 'command')
268
245
  return;
269
- expect(commandSlice.server.specs[0].rules).toHaveLength(2);
270
- const rule1 = commandSlice.server.specs[0].rules.find((r) => r.name === 'Valid test items should be created successfully');
246
+ expect(commandSlice.server.specs.rules).toHaveLength(2);
247
+ const rule1 = commandSlice.server.specs.rules.find((r) => r.description === 'Valid test items should be created successfully');
271
248
  expect(rule1?.id).toBe('RULE-001');
272
- const rule2 = commandSlice.server.specs[0].rules.find((r) => r.name === 'Invalid test items should be rejected');
249
+ const rule2 = commandSlice.server.specs.rules.find((r) => r.description === 'Invalid test items should be rejected');
273
250
  expect(rule2?.id).toBe('RULE-002');
274
251
  });
275
252
  it('should have ids for query slice rules', async () => {
@@ -281,8 +258,8 @@ describe('getNarratives', (_mode) => {
281
258
  const querySlice = testFlowWithIds.slices.find((s) => s.name === 'Get test items');
282
259
  if (querySlice?.type !== 'query')
283
260
  return;
284
- expect(querySlice.server.specs[0].rules).toHaveLength(1);
285
- const rule3 = querySlice.server.specs[0].rules.find((r) => r.name === 'Items should be retrievable after creation');
261
+ expect(querySlice.server.specs.rules).toHaveLength(1);
262
+ const rule3 = querySlice.server.specs.rules.find((r) => r.description === 'Items should be retrievable after creation');
286
263
  expect(rule3?.id).toBe('RULE-003');
287
264
  });
288
265
  it('should have ids for react slice rules', async () => {
@@ -294,8 +271,8 @@ describe('getNarratives', (_mode) => {
294
271
  const reactSlice = testFlowWithIds.slices.find((s) => s.name === 'React to test event');
295
272
  if (reactSlice?.type !== 'react')
296
273
  return;
297
- expect(reactSlice.server.specs[0].rules).toHaveLength(1);
298
- const rule4 = reactSlice.server.specs[0].rules.find((r) => r.name === 'System should react to test item creation');
274
+ expect(reactSlice.server.specs.rules).toHaveLength(1);
275
+ const rule4 = reactSlice.server.specs.rules.find((r) => r.description === 'System should react to test item creation');
299
276
  expect(rule4?.id).toBe('RULE-004');
300
277
  });
301
278
  it('should handle when examples correctly', async () => {
@@ -311,10 +288,14 @@ describe('getNarratives', (_mode) => {
311
288
  const submitSlice = questionnaireFlow.slices.find((s) => s.name === 'submits the questionnaire');
312
289
  expect(submitSlice?.type).toBe('command');
313
290
  if (submitSlice?.type === 'command') {
314
- const example = submitSlice.server?.specs?.[0]?.rules[0]?.examples[0];
315
- const whenStep = example?.steps?.find((s) => s.keyword === 'When');
316
- if (whenStep && 'text' in whenStep) {
317
- expect(whenStep.text).toBe('SubmitQuestionnaire');
291
+ const example = submitSlice.server?.specs?.rules[0]?.examples[0];
292
+ if (example !== null &&
293
+ example !== undefined &&
294
+ typeof example.when === 'object' &&
295
+ example.when !== null &&
296
+ !Array.isArray(example.when) &&
297
+ 'commandRef' in example.when) {
298
+ expect(example.when.commandRef).toBe('SubmitQuestionnaire');
318
299
  }
319
300
  }
320
301
  }
@@ -332,13 +313,13 @@ describe('getNarratives', (_mode) => {
332
313
  it('should handle experience slice with client specs', async () => {
333
314
  const memoryVfs = new InMemoryFileStore();
334
315
  const flowWithExperienceContent = `
335
- import { flow, experience, it, specs } from '@auto-engineer/narrative';
316
+ import { flow, experience, should, specs } from '@auto-engineer/narrative';
336
317
 
337
318
  flow('Test Experience Flow', () => {
338
- experience('Homepage', 'H1a4Bn6Cy').client(() => {
319
+ experience('Homepage', 'AUTO-H1a4Bn6Cy').client(() => {
339
320
  specs(() => {
340
- it('show a hero section with a welcome message');
341
- it('allow user to start the questionnaire');
321
+ should('show a hero section with a welcome message');
322
+ should('allow user to start the questionnaire');
342
323
  });
343
324
  });
344
325
  });
@@ -355,24 +336,26 @@ flow('Test Experience Flow', () => {
355
336
  if (homepageSlice?.type === 'experience') {
356
337
  expect(homepageSlice.client).toBeDefined();
357
338
  expect(homepageSlice.client.specs).toBeDefined();
358
- expect(Array.isArray(homepageSlice.client.specs)).toBe(true);
359
- expect(homepageSlice.client.specs).toHaveLength(2);
360
- expect(homepageSlice.client.specs[0].type).toBe('it');
361
- expect(homepageSlice.client.specs[0].title).toBe('show a hero section with a welcome message');
362
- expect(homepageSlice.client.specs[1].type).toBe('it');
363
- expect(homepageSlice.client.specs[1].title).toBe('allow user to start the questionnaire');
339
+ expect(homepageSlice.client.specs?.rules).toBeDefined();
340
+ expect(homepageSlice.client.specs?.rules).toHaveLength(2);
341
+ const rules = homepageSlice.client.specs?.rules;
342
+ if (rules && Array.isArray(rules)) {
343
+ expect(rules).toHaveLength(2);
344
+ expect(rules[0]).toBe('show a hero section with a welcome message');
345
+ expect(rules[1]).toBe('allow user to start the questionnaire');
346
+ }
364
347
  }
365
348
  }
366
349
  });
367
350
  it('simulates browser execution with transpiled CommonJS code', async () => {
368
351
  const memoryVfs = new InMemoryFileStore();
369
352
  const flowContent = `
370
- import { flow, experience, it, specs } from '@auto-engineer/narrative';
353
+ import { flow, experience, should, specs } from '@auto-engineer/narrative';
371
354
 
372
355
  flow('Browser Test Flow', () => {
373
356
  experience('HomePage').client(() => {
374
357
  specs(() => {
375
- it('render correctly');
358
+ should('render correctly');
376
359
  });
377
360
  });
378
361
  });
@@ -392,10 +375,8 @@ flow('Browser Test Flow', () => {
392
375
  if (slice.type === 'experience') {
393
376
  expect(slice.client).toBeDefined();
394
377
  expect(slice.client.specs).toBeDefined();
395
- expect(Array.isArray(slice.client.specs)).toBe(true);
396
- expect(slice.client.specs).toHaveLength(1);
397
- expect(slice.client.specs[0].type).toBe('it');
398
- expect(slice.client.specs[0].title).toBe('render correctly');
378
+ expect(slice.client.specs?.rules).toHaveLength(1);
379
+ expect(slice.client.specs?.rules?.[0]).toBe('render correctly');
399
380
  }
400
381
  });
401
382
  it('handles experience slice with ES module interop correctly', async () => {
@@ -405,8 +386,8 @@ flow('Browser Test Flow', () => {
405
386
  const flowContent = `
406
387
  import { flow, experience } from '@auto-engineer/narrative';
407
388
 
408
- flow('Questionnaires', 'Q9m2Kp4Lx', () => {
409
- experience('Homepage', 'H1a4Bn6Cy').client(() => {});
389
+ flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
390
+ experience('Homepage', 'AUTO-H1a4Bn6Cy').client(() => {});
410
391
  });
411
392
  `;
412
393
  await memoryVfs.write('/browser/questionnaires.narrative.ts', new TextEncoder().encode(flowContent));
@@ -541,7 +522,7 @@ flow('questionnaires-test', () => {
541
522
  expect(querySlice?.type).toBe('query');
542
523
  if (querySlice?.type !== 'query')
543
524
  return;
544
- const example = querySlice.server.specs[0].rules[0]?.examples[0];
525
+ const example = querySlice.server.specs.rules[0]?.examples[0];
545
526
  expect(example).toBeDefined();
546
527
  if (example !== null && example !== undefined) {
547
528
  validateMixedGivenTypes(example);
@@ -580,7 +561,7 @@ flow('questionnaires-test', () => {
580
561
  const model = await createQuestionnaireBugTestModel();
581
562
  validateQuestionnaireBugFix(model);
582
563
  });
583
- it('should convert all given events to eventRef', async () => {
564
+ it('should convert all given events to eventRef', async function () {
584
565
  const memoryVfs = new InMemoryFileStore();
585
566
  const todoSummaryFlowContent = `
586
567
  import { flow, query, specs, rule, example, type Event, type State } from '@auto-engineer/narrative';
@@ -680,37 +661,39 @@ flow('Todo List', () => {
680
661
  expect(summarySlice?.type).toBe('query');
681
662
  if (summarySlice?.type !== 'query')
682
663
  return;
683
- const example = summarySlice.server.specs[0].rules[0]?.examples[0];
664
+ const example = summarySlice.server.specs.rules[0]?.examples[0];
684
665
  expect(example).toBeDefined();
685
- expect(example.steps).toBeDefined();
686
- expect(Array.isArray(example.steps)).toBe(true);
687
- const givenAndSteps = example.steps.filter((s) => s.keyword === 'Given' || s.keyword === 'And');
688
- expect(givenAndSteps).toHaveLength(5);
689
- validateGivenItemsHaveEventRef(givenAndSteps);
690
- validateTodoEventRefs(givenAndSteps);
666
+ expect(example.given).toBeDefined();
667
+ expect(Array.isArray(example.given)).toBe(true);
668
+ expect(example.given).toHaveLength(5);
669
+ if (!example.given) {
670
+ throw new Error('expected example.given to be defined');
671
+ }
672
+ validateGivenItemsHaveEventRef(example.given);
673
+ validateTodoEventRefs(example.given);
691
674
  validateTodoMessages(model);
692
675
  });
693
676
  });
694
- function validateGivenItemsHaveEventRef(givenSteps) {
695
- for (let i = 0; i < givenSteps.length; i++) {
696
- const step = givenSteps[i];
697
- if (typeof step === 'object' && step !== null) {
698
- expect(step.keyword === 'Given' || step.keyword === 'And').toBe(true);
699
- expect('text' in step).toBe(true);
677
+ function validateGivenItemsHaveEventRef(given) {
678
+ for (let i = 0; i < given.length; i++) {
679
+ const givenItem = given[i];
680
+ if (typeof givenItem === 'object' && givenItem !== null) {
681
+ expect('eventRef' in givenItem).toBe(true);
682
+ expect('stateRef' in givenItem).toBe(false);
700
683
  }
701
684
  }
702
685
  }
703
- function expectStepText(step, expectedType) {
704
- if (step !== null && step !== undefined && typeof step === 'object' && 'text' in step) {
705
- expect(step.text).toBe(expectedType);
686
+ function expectEventRef(item, expectedType) {
687
+ if (item !== null && item !== undefined && typeof item === 'object' && 'eventRef' in item) {
688
+ expect(item.eventRef).toBe(expectedType);
706
689
  }
707
690
  }
708
- function validateTodoEventRefs(givenSteps) {
709
- expectStepText(givenSteps[0], 'TodoAdded');
710
- expectStepText(givenSteps[1], 'TodoAdded');
711
- expectStepText(givenSteps[2], 'TodoAdded');
712
- expectStepText(givenSteps[3], 'TodoMarkedInProgress');
713
- expectStepText(givenSteps[4], 'TodoMarkedComplete');
691
+ function validateTodoEventRefs(given) {
692
+ expectEventRef(given[0], 'TodoAdded');
693
+ expectEventRef(given[1], 'TodoAdded');
694
+ expectEventRef(given[2], 'TodoAdded');
695
+ expectEventRef(given[3], 'TodoMarkedInProgress');
696
+ expectEventRef(given[4], 'TodoMarkedComplete');
714
697
  }
715
698
  function validateTodoMessages(model) {
716
699
  const todoAddedEvent = model.messages.find((m) => m.name === 'TodoAdded');
@@ -730,10 +713,14 @@ function validateSubmitQuestionnaireCommand(questionnaireFlow) {
730
713
  const submitSlice = questionnaireFlow.slices.find((s) => s.name === 'submits questionnaire');
731
714
  expect(submitSlice?.type).toBe('command');
732
715
  if (submitSlice?.type === 'command') {
733
- const example = submitSlice.server?.specs?.[0]?.rules[0]?.examples[0];
734
- const whenStep = example?.steps?.find((s) => s.keyword === 'When');
735
- if (whenStep && 'text' in whenStep) {
736
- expect(whenStep.text).toBe('SubmitQuestionnaire');
716
+ const example = submitSlice.server?.specs?.rules[0]?.examples[0];
717
+ if (example !== null &&
718
+ example !== undefined &&
719
+ typeof example.when === 'object' &&
720
+ example.when !== null &&
721
+ !Array.isArray(example.when) &&
722
+ 'commandRef' in example.when) {
723
+ expect(example.when.commandRef).toBe('SubmitQuestionnaire');
737
724
  }
738
725
  }
739
726
  }
@@ -744,11 +731,15 @@ function validateQuestionAnsweredEvent(model) {
744
731
  function validateGivenSectionEventRefs(questionnaireFlow) {
745
732
  const viewsSlice = questionnaireFlow.slices.find((s) => s.name === 'views progress');
746
733
  if (viewsSlice?.type === 'query') {
747
- const example = viewsSlice.server?.specs?.[0]?.rules[0]?.examples[0];
748
- if (example?.steps !== undefined && Array.isArray(example.steps)) {
749
- const givenStep = example.steps.find((s) => s.keyword === 'Given');
750
- if (givenStep && 'text' in givenStep) {
751
- expect(givenStep.text).toBe('QuestionAnswered');
734
+ const example = viewsSlice.server?.specs?.rules[0]?.examples[0];
735
+ if (example?.given && Array.isArray(example.given) && example.given.length > 0) {
736
+ const givenItem = example.given[0];
737
+ if (typeof givenItem === 'object' && givenItem !== null) {
738
+ expect('eventRef' in givenItem).toBe(true);
739
+ expect('stateRef' in givenItem).toBe(false);
740
+ if ('eventRef' in givenItem) {
741
+ expect(givenItem.eventRef).toBe('QuestionAnswered');
742
+ }
752
743
  }
753
744
  }
754
745
  }
@@ -760,43 +751,45 @@ function validateCurrentQuestionIdType(model) {
760
751
  expect(currentQuestionIdField?.type).toBe('string | null');
761
752
  }
762
753
  function validateMixedGivenTypes(example) {
763
- expect(example.name).toBe('system with 2 items reaches max of 2');
764
- expect(example.steps).toBeDefined();
765
- expect(Array.isArray(example.steps)).toBe(true);
766
- const givenSteps = example.steps.filter((s) => s.keyword === 'Given' || s.keyword === 'And');
767
- expect(givenSteps).toHaveLength(4);
768
- const firstGiven = givenSteps[0];
769
- expect('text' in firstGiven).toBe(true);
770
- if ('text' in firstGiven) {
771
- expect(firstGiven.text).toBe('ConfigState');
754
+ expect(example.description).toBe('system with 2 items reaches max of 2');
755
+ expect(example.given).toBeDefined();
756
+ expect(Array.isArray(example.given)).toBe(true);
757
+ if (!example.given)
758
+ return;
759
+ expect(example.given).toHaveLength(4);
760
+ const firstGiven = example.given[0];
761
+ expect('stateRef' in firstGiven).toBe(true);
762
+ expect('eventRef' in firstGiven).toBe(false);
763
+ if ('stateRef' in firstGiven) {
764
+ expect(firstGiven.stateRef).toBe('ConfigState');
772
765
  }
773
- const secondGiven = givenSteps[1];
774
- expect('text' in secondGiven).toBe(true);
775
- if ('text' in secondGiven) {
776
- expect(secondGiven.text).toBe('SystemInitialized');
766
+ const secondGiven = example.given[1];
767
+ expect('eventRef' in secondGiven).toBe(true);
768
+ if ('eventRef' in secondGiven) {
769
+ expect(secondGiven.eventRef).toBe('SystemInitialized');
777
770
  }
778
- const thirdGiven = givenSteps[2];
779
- expect('text' in thirdGiven).toBe(true);
780
- if ('text' in thirdGiven) {
781
- expect(thirdGiven.text).toBe('ItemAdded');
771
+ const thirdGiven = example.given[2];
772
+ expect('eventRef' in thirdGiven).toBe(true);
773
+ if ('eventRef' in thirdGiven) {
774
+ expect(thirdGiven.eventRef).toBe('ItemAdded');
782
775
  }
783
- const fourthGiven = givenSteps[3];
784
- expect('text' in fourthGiven).toBe(true);
785
- if ('text' in fourthGiven) {
786
- expect(fourthGiven.text).toBe('ItemAdded');
776
+ const fourthGiven = example.given[3];
777
+ expect('eventRef' in fourthGiven).toBe(true);
778
+ if ('eventRef' in fourthGiven) {
779
+ expect(fourthGiven.eventRef).toBe('ItemAdded');
787
780
  }
788
781
  }
789
782
  function validateEmptyWhenClause(example) {
790
- const whenStep = example.steps.find((s) => s.keyword === 'When');
791
- expect(whenStep).toBeUndefined();
783
+ expect(example.when).toBeUndefined();
792
784
  }
793
785
  function validateThenClause(example) {
794
- const thenSteps = example.steps.filter((s) => s.keyword === 'Then');
795
- expect(thenSteps).toHaveLength(1);
796
- const thenOutcome = thenSteps[0];
797
- expect('text' in thenOutcome).toBe(true);
798
- if ('text' in thenOutcome) {
799
- expect(thenOutcome.text).toBe('SystemStatus');
786
+ expect(example.then).toBeDefined();
787
+ expect(Array.isArray(example.then)).toBe(true);
788
+ expect(example.then).toHaveLength(1);
789
+ const thenOutcome = example.then[0];
790
+ expect('stateRef' in thenOutcome).toBe(true);
791
+ if ('stateRef' in thenOutcome) {
792
+ expect(thenOutcome.stateRef).toBe('SystemStatus');
800
793
  }
801
794
  }
802
795
  function validateMixedGivenTypeMessages(model) {
@@ -827,8 +820,7 @@ import {
827
820
  query,
828
821
  experience,
829
822
  flow,
830
- describe,
831
- it,
823
+ should,
832
824
  specs,
833
825
  rule,
834
826
  example,
@@ -876,11 +868,11 @@ type SubmitQuestionnaire = Command<
876
868
  }
877
869
  >;
878
870
 
879
- flow('Questionnaires', 'Q9m2Kp4Lx', () => {
880
- command('sends the questionnaire link', 'S2b5Cp7Dz')
871
+ flow('Questionnaires', 'AUTO-Q9m2Kp4Lx', () => {
872
+ command('sends the questionnaire link', 'AUTO-S2b5Cp7Dz')
881
873
  .server(() => {
882
874
  specs(() => {
883
- rule('questionnaire link is sent to participant', 'r0A1Bo8X', () => {
875
+ rule('questionnaire link is sent to participant', 'AUTO-r0A1Bo8X', () => {
884
876
  example('sends the questionnaire link successfully')
885
877
  .when<SendQuestionnaireLink>({
886
878
  questionnaireId: 'q-001',
@@ -904,16 +896,16 @@ flow('Questionnaires', 'Q9m2Kp4Lx', () => {
904
896
  }
905
897
  \`)
906
898
  .client(() => {
907
- describe('Questionnaire Link', () => {
908
- it('display a confirmation message when the link is sent');
909
- it('handle errors when the link cannot be sent');
899
+ specs('Questionnaire Link', () => {
900
+ should('display a confirmation message when the link is sent');
901
+ should('handle errors when the link cannot be sent');
910
902
  });
911
903
  });
912
904
 
913
- command('submits the questionnaire', 'T5k9Jw3V')
905
+ command('submits the questionnaire', 'AUTO-T5k9Jw3V')
914
906
  .server(() => {
915
907
  specs(() => {
916
- rule('questionnaire allowed to be submitted when all questions are answered', 'r4H0Lx4U', () => {
908
+ rule('questionnaire allowed to be submitted when all questions are answered', 'AUTO-r4H0Lx4U', () => {
917
909
  example('submits the questionnaire successfully')
918
910
  .when<SubmitQuestionnaire>({
919
911
  questionnaireId: 'q-001',
@@ -936,8 +928,8 @@ flow('Questionnaires', 'Q9m2Kp4Lx', () => {
936
928
  }
937
929
  \`)
938
930
  .client(() => {
939
- describe('Submission Confirmation', () => {
940
- it('display a confirmation message upon successful submission');
931
+ specs('Submission Confirmation', () => {
932
+ should('display a confirmation message upon successful submission');
941
933
  });
942
934
  });
943
935
  });`;
@@ -967,34 +959,32 @@ function getSubmitSlice(questionnaireFlow) {
967
959
  return submitSlice;
968
960
  }
969
961
  function getSubmitExample(submitSlice) {
970
- const rule = submitSlice.server?.specs?.[0]?.rules[0];
962
+ const rule = submitSlice.server?.specs?.rules[0];
971
963
  expect(rule).toBeDefined();
972
964
  expect(rule?.examples).toHaveLength(1);
973
965
  const example = rule?.examples[0];
974
- expect(example?.name).toBe('submits the questionnaire successfully');
966
+ expect(example?.description).toBe('submits the questionnaire successfully');
975
967
  return example;
976
968
  }
977
969
  function validateSubmitCommandRef(example) {
978
970
  const ex = example;
979
- expect(ex?.steps).toBeDefined();
980
- const whenStep = ex?.steps?.find((s) => s.keyword === 'When');
981
- if (whenStep && 'text' in whenStep) {
982
- expect(whenStep.text).toBe('SubmitQuestionnaire');
983
- expect(whenStep.text).not.toBe('SendQuestionnaireLink');
971
+ expect(ex?.when).toBeDefined();
972
+ if (typeof ex?.when === 'object' && ex.when !== null && !Array.isArray(ex.when) && 'commandRef' in ex.when) {
973
+ expect(ex.when.commandRef).toBe('SubmitQuestionnaire');
974
+ expect(ex.when.commandRef).not.toBe('SendQuestionnaireLink');
984
975
  }
985
976
  else {
986
- throw new Error('Expected steps to have a When step with text property');
977
+ throw new Error('Expected when to have commandRef property');
987
978
  }
988
979
  }
989
980
  function validateLinkSliceCommandRef(questionnaireFlow) {
990
981
  const linkSlice = questionnaireFlow.slices.find((s) => s.name === 'sends the questionnaire link');
991
982
  expect(linkSlice?.type).toBe('command');
992
983
  if (linkSlice?.type === 'command') {
993
- const linkExample = linkSlice.server?.specs?.[0]?.rules[0]?.examples[0];
984
+ const linkExample = linkSlice.server?.specs?.rules[0]?.examples[0];
994
985
  const ex = linkExample;
995
- const whenStep = ex?.steps?.find((s) => s.keyword === 'When');
996
- if (whenStep && 'text' in whenStep) {
997
- expect(whenStep.text).toBe('SendQuestionnaireLink');
986
+ if (typeof ex?.when === 'object' && ex.when !== null && !Array.isArray(ex.when) && 'commandRef' in ex.when) {
987
+ expect(ex.when.commandRef).toBe('SendQuestionnaireLink');
998
988
  }
999
989
  }
1000
990
  }
@@ -1020,19 +1010,17 @@ function getServerSpecsFromSlice(submitSlice) {
1020
1010
  const slice = submitSlice;
1021
1011
  const serverSpecs = slice.server?.specs;
1022
1012
  expect(serverSpecs).toBeDefined();
1023
- expect(Array.isArray(serverSpecs)).toBe(true);
1024
- expect(serverSpecs).toHaveLength(1);
1025
- const firstSpec = serverSpecs[0];
1026
- expect(firstSpec?.rules).toBeDefined();
1027
- expect(firstSpec?.rules).toHaveLength(1);
1028
- return firstSpec;
1013
+ const specs = serverSpecs;
1014
+ expect(specs?.rules).toBeDefined();
1015
+ expect(specs?.rules).toHaveLength(1);
1016
+ return serverSpecs;
1029
1017
  }
1030
1018
  function getFirstRuleFromSpecs(serverSpecs) {
1031
1019
  const specs = serverSpecs;
1032
1020
  const rule = specs?.rules?.[0];
1033
1021
  expect(rule).toBeDefined();
1034
1022
  const r = rule;
1035
- expect(r?.name).toBe('questionnaire allowed to be submitted when all questions are answered');
1023
+ expect(r?.description).toBe('questionnaire allowed to be submitted when all questions are answered');
1036
1024
  expect(r?.examples).toBeDefined();
1037
1025
  expect(r?.examples).toHaveLength(1);
1038
1026
  return rule;
@@ -1042,35 +1030,34 @@ function getFirstExampleFromRule(rule) {
1042
1030
  const example = r?.examples?.[0];
1043
1031
  expect(example).toBeDefined();
1044
1032
  const ex = example;
1045
- expect(ex?.name).toBe('submits the questionnaire successfully');
1033
+ expect(ex?.description).toBe('submits the questionnaire successfully');
1046
1034
  return example;
1047
1035
  }
1048
1036
  function validateExampleCommandRef(example) {
1049
1037
  const ex = example;
1050
- expect(ex?.steps).toBeDefined();
1051
- const whenStep = ex?.steps?.find((s) => s.keyword === 'When');
1052
- expect(whenStep).toBeDefined();
1053
- if (whenStep && 'text' in whenStep) {
1054
- expect(whenStep.text).toBe('SubmitQuestionnaire');
1055
- expect(whenStep.text).not.toBe('SendQuestionnaireLink');
1056
- expect(whenStep.docString).toEqual({
1038
+ expect(ex?.when).toBeDefined();
1039
+ if (typeof ex?.when === 'object' && ex.when !== null && !Array.isArray(ex.when) && 'commandRef' in ex.when) {
1040
+ expect(ex.when.commandRef).toBe('SubmitQuestionnaire');
1041
+ expect(ex.when.commandRef).not.toBe('SendQuestionnaireLink');
1042
+ expect(ex.when.exampleData).toEqual({
1057
1043
  questionnaireId: 'q-001',
1058
1044
  participantId: 'participant-abc',
1059
1045
  });
1060
1046
  }
1061
1047
  else {
1062
- throw new Error('Expected when step to have text property');
1048
+ throw new Error('Expected when to have commandRef property');
1063
1049
  }
1064
1050
  }
1065
1051
  function validateThenEvents(example) {
1066
1052
  const ex = example;
1067
- expect(ex?.steps).toBeDefined();
1068
- const thenSteps = ex?.steps?.filter((s) => s.keyword === 'Then');
1069
- expect(thenSteps).toHaveLength(1);
1070
- const thenStep = thenSteps?.[0];
1071
- if (thenStep && 'text' in thenStep) {
1072
- expect(thenStep.text).toBe('QuestionnaireSubmitted');
1073
- expect(thenStep.docString).toEqual({
1053
+ expect(ex?.then).toBeDefined();
1054
+ expect(Array.isArray(ex?.then)).toBe(true);
1055
+ expect(ex?.then).toHaveLength(1);
1056
+ const thenEvent = ex?.then?.[0];
1057
+ if (thenEvent !== null && thenEvent !== undefined && 'eventRef' in thenEvent) {
1058
+ const event = thenEvent;
1059
+ expect(event.eventRef).toBe('QuestionnaireSubmitted');
1060
+ expect(event.exampleData).toEqual({
1074
1061
  questionnaireId: 'q-001',
1075
1062
  participantId: 'participant-abc',
1076
1063
  submittedAt: new Date('2030-01-01T09:00:00.000Z'),